Skip to content

fix(spec): state the row-cap guard ElementDataSourceGate implements, and record where the per-kind view limit actually lands (#19228) - #19533

Merged
os-warren merged 9 commits into
mainfrom
claude/issue-19228-view-limit-unreachable-arm
Sep 21, 2026
Merged

os-warren merged 9 commits into
mainfrom
claude/issue-19228-view-limit-unreachable-arm

Conversation

@os-warren

@os-warren os-warren commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Part of #19228

Clause-②: no

⚠️ Read this first — the card's causal claim did not reproduce

#19228 says an APPLIED .default(100) makes the react tier's 「fills it only when unset」 arm
structurally unreachable. Measured end to end at the objectui pin this repo builds against
(.objectui-sha = 87af769e9, 2026-09-21T06:30–06:40Z), it does not:

  • The arm's guard reads the element-face key — readLimit(base, 'limit') where base is the
    object-kanban / object-timeline node. That key is .optional() with no applied default,
    so an author's silence is still silence at parse time.
  • The applied default #19226 added sits on the view-face per-kind blocks
    (GalleryConfigSchema / KanbanConfigSchema / TimelineConfigSchema). Different schemas.
  • Nothing carries the view-face default across: composed.limit = config.limit ?? savedViewLimit(view),
    and savedViewLimit reads view.pagination.pageSize, else a flat limit on the runtime saved-view
    RECORD — ElementSavedView is an untyped string-keyed record from the adapter's own listViews()
    (core/src/data-scope/element-data-source.ts:96, fallback at :237-241), ⛔ NOT a document
    ListViewSchema accepts: a flat limit on a view document is refused unrecognized_keys. A third
    face, named here so it is not read as the author face — and never a per-kind block. ListView's
    baseProps (plugin-list/src/ListView.tsx:2840-2865) carries no limit on any branch.

⛔ So no .default() moves in this PR, and no precedence is picked — both fences the dispatch set
hold. What DID measure as a real 说明书脱节 is a different, smaller thing, and that is what is repaired.

What is repaired

1. The describe's 「only when unset」 is right for its face — and now names the face.
The branch is if (!fromView || !isUsableRowLimit(authored))
(react/src/element-data-source/ElementDataSourceGate.tsx:316-331).
⚠️ RETRACTED — rounds 1 and 3 both got this backwards, and round 4 measured it. On THIS face the
displacement arm is unreachable: the key is z.number().int().positive().optional() and the gate's
isUsableRowLimit admits integers above zero, so every value the contract accepts is usable and the
「set but not usable」 cell is EMPTY (measured per value: 1 / 25 / 100 / 5000 accepted and usable;
0 / -1 / 2.5 / '100' / null refused by both). ⇒ 「fills this key only when it is unset」 — which round 1
retired as too narrow — was never too narrow; it was right for its face, and is restored. The view half
is pagination.pageSize ALONE on this face, per the bullet above. The describe states the rule that is
reachable from the accept set; the unreachable arm is recorded in the docblock, ⛔ not in author prose.

2. The per-kind view limit is recorded as WHERE IT LANDS. ⚠️ An earlier revision of this section said 「read by nobody」; that was measured with an instrument blind to spreads and is retracted — see the CORRECTION sections below. Kanban and timeline DO read it; gallery alone does not.
Two instruments, because the first one's answer was wrong. PROPERTY-ACCESS over all 8,228 files tracked at the pin — probe \.(kanban|gallery|timeline)(\?)?\.limit\b → 0 lines; control \.(kanban|gallery|timeline)(\?)?\.(groupByField|scale|coverField)\b → 13 lines across 6 files. ⚠️ That zero is WRONG as a claim about readers: a spread carries the key without spelling it. SPREADS, by the predicate 「a spread whose target is the object literal an adapter returns as the node」, return four, and they overturn it. Recorded in two places, on two different faces: on rowLimitKey in view.zod.ts (the VIEW face — flattened onto the node and read, on both routes for kanban, on the plugin-view route for timeline, and read by nobody for gallery), and on the object-timeline door in component.zod.ts (the ELEMENT face — a node's own nested timeline.limit, which is the one read on no route at all). ⛔ Recorded, not repaired: what should read it is the card's open half.

3. ObjectTimelinePropsSchema carries two authorable row caps on one strictObject.
Taking TimelineConfigSchema by reference imported #19226's limit beside the flat limit. The
published member list for that door omitted the new key; it now names it and says it is inert here.
The stale limit read anchor (:234, :254, at the old pin 53ded82b) is corrected to :407 at
the current pin; ⛔ the other anchors in that list are NOT swept — they remain the 53ded82b
readings the docblock header names, and the note says so.

The three relayed consumer readings, reproduced first-hand

Comment 5747444798 marked these 「复核的读数,⛔ 本席未复现」. All three reproduce by shape; two
of the three file:line pairs are wrong at this pin, which is why shape was the instrument.

relayed reproduced at pin 87af769e9
ListView.tsx:2493 forwards the timeline block nested ✓ shape — the nested forward is :3084 inside case 'timeline': (:3062-3117). :2493 is a useMemo dependency array here (:2496). Only startDateField / endDateField / titleField / groupByField / colorField / scale are hoisted flat; limit is not
ObjectTimeline.tsx:234 reads only the flat schema.limit ✓ shape — the one $top is :407, resolveRowLimit(schema.limit, DEFAULT_TIMELINE_LIMIT); also :279 / :281 / :442. No nested read anywhere
nothing reads timeline.limit ⚠️ RETRACTED — ✓ 0 on that instrument, but see the CORRECTION sections: four flat spreads land the key where kanban/timeline read it

The zero's instrument and its reach radius. git grep over the 8,228 files tracked at commit
87af769e9 — a literal text search of property-access spellings. Lit control on the identical
receiver alternation (schema.timeline / timelineConfig / resolvedTimeline / mergedTimeline /
tCfg): .startDateField7 hits, .limit0. ⚠️ A first attempt at this probe returned
0 for the control too — a broken bracket expression — and is recorded here because a dead instrument
and a clean result are indistinguishable without one.

One known target deliberately outside the radius: a computed read (cfg[key] with key from a
variable) or a wholesale spread of the config into a query builder is invisible to a text search.
⚠️ CORRECTION (seat, after the at-tier review at head 85ad2898). That hole was named and
then closed by ASSERTION rather than by an instrument, and the assertion was false. A spread carries
a key without ever spelling it, so this radius owed a SECOND instrument with its own lit control; it
was not built. Built afterwards, it returns four flat spreads, not one: ListView.tsx:2979 and
ObjectView.tsx:1638 (...restKanban), ObjectView.tsx:1697 (...(viewOptions.gallery || {}))
and ObjectView.tsx:1725 (...(viewOptions.timeline || {})). Neither restKanban destructure
strips limit. ⭐ Naming a limitation is not discharging it. Also outside the radius: untracked
build output, and objectui HEAD rather than the pin.

Verification

  • pnpm --filter @objectstack/spec build && typecheck && test509 files / 14,890 tests pass
    (post-merge run on this head).
  • Consumer package: @objectstack/lint — the only package outside packages/spec naming any of
    the touched symbols (in a comment, not a runtime read; lit control: ListViewSchema /
    PageComponentSchema fire across 8+ files, so the instrument discriminates). Its first run was a
    Failed to resolve entry cascade from unbuilt workspace deps; after
    pnpm --filter '@objectstack/lint^...' build it reads 106 files / 4,034 tests pass, 5 skipped.
  • pnpm --filter @objectstack/spec check:generatedall 15 generated artifacts up to date
    (content/docs/references/ui/component.mdx regenerated via --fix; three table rows changed in component.mdx and nine in view.mdx,
    nothing else).
  • pnpm lint (repo-wide eslint . --no-inline-config) — exit 0, the whole union, no narrowing.
  • check:react-declaration-parity run exactly as lint.yml runs it
    (MANIFEST="$PWD/sdui.manifest.json" … --baseline react-declaration-parity.baseline.json --strict)
    exit 0, no new declaration divergence. ⚠️ Run WITHOUT the baseline it exits 1 on 126
    pre-existing divergences; that invocation is not the gate.
  • Derived gate families (scripts/pm/dispatch-gates.mjs --commands, reconciled with --ran):
    108 derived, 100 measured green, 8 NOT MEASURED — every one of the 8 a PREREQUISITE NOT MET
    exit 3 (unbuilt sibling packages; check-plugin-teardown-shape --self-test needs an unshallow
    clone). ⛔ Reported as what they are, never as a pass. None of the 8 reads packages/spec/src/ui/.
  • origin/main merged through scripts/pm/os-regen-merge.sh; no regeneration debt, and main's
    newest entry (filter-between-field-reference-endpoint-refused) survives at the same 2-file count
    on both sides.

⛔ Refused / not done

  • No precedence picked. Which of the per-kind view limit, a view's pagination.pageSize and
    a component's flat limit wins is untouched, in prose and in code.
  • .default(100) not moved, demoted or removed on any of the three view configs.
  • Nothing written in objectui — read-only at the pin, as dispatched.
  • ⛔ The 14 other read anchors in the object-timeline docblock were NOT re-swept at the current pin.

Acceptance notes

  • ⚠️ File-surface deviation, declared. The claim's declared surface named
    packages/spec/src/ui/component.zod.ts. Two files fall outside it: packages/spec/src/ui/view.zod.ts
    (the applied default the card is about lives in rowLimitKey there, not in component.zod.ts
    the claim and the dispatch were both written against a stale location) and
    content/docs/references/ui/component.mdx (the mandatory regeneration from a .describe() change;
    check:docs reds without it — the claim anticipated regeneration but named
    packages/spec/json-schema/, which this diff leaves untouched). Both are inside the seat's measured
    serial-constraint clearance: zero open PRs hold any path under packages/spec/src/ui/.
  • ⚠️ Part of, not Fixes. This PR delivers the 说明书 half triage queued; the card's own two contract questions stay open, so line 1 is deliberately Part of #19228 and merging this does not close the card.
  • noted, not filed: the object-timeline docblock header claims every read anchor was taken at pin
    53ded82b while .objectui-sha is 87af769e9; only the limit anchor is re-read here. Carrier: a
    future object-timeline card, or the seat that next re-pins that block.
  • noted, not filed: spec: declare an author-settable row ceiling for gallery (and kanban) view configs — the protocol lacks the knob objectui#7390 was ruled to read (principle: 协议不正确的先改协议) #17393's own pin text argues an authorable ceiling on the non-grid four 「would be
    surface no renderer reads」 — the measurement above says the ceiling it DID add is exactly that.
    The pin is still correct about the four; the irony is a reading, not a defect.

⚠️ CORRECTION — the per-kind view limit is NOT inert

Seat, after the at-tier review at head 85ad2898, re-measured first-hand at the pin. This PR's
earlier claim of zero read points was taken with an instrument blind to spreads. Four flat spreads
carry the key onto the generated node (ListView.tsx:2979, ObjectView.tsx:1638 / :1697 /
:1725), and ObjectKanban.tsx / ObjectTimeline.tsx read the resulting flat limit.

ADR-0049 enforce-or-remove is therefore NOT a live option for kanban or timeline — retiring
that key would break two adapters that actively forward it. It IS the honest reading for gallery
alone
, whose renderer contains no limit at all (0 occurrences, against a lit control that fires
on the same file).

⚠️ One distinction this correction itself owes: the $top those keys would govern is not issued on
either route today, because both hosts hand the child its rows as a data prop and the child
short-circuits its own fetch. So 「governs no query on these two routes」 is true; 「read by nobody」
is not.

⭐ For the decision box: through those same spreads an APPLIED default puts an authored-LOOKING flat
limit: 100 on every node built from a spec-parsed view — a value no author wrote.

Cross-repo, ⛔ not acted on

objectui#7390's acceptance face is where this gets consumed. ⚠️ Corrected: kanban and timeline have
a read point today; gallery is the one that owes one. Stated here for the seat to file in
objectui's queue with a Blocked-by: line; ⛔ nothing was written there.


Generated by Claude Code

…tate the gate guard the gate implements

#19228, prose + pins only. ⛔ No `.default()` moves and no precedence is
picked — both are contract directions this card is explicitly not allowed
to take.

Measured first-hand at the objectui pin `87af769e9` (2026-09-21T06:30-06:40Z),
over all 8,228 files tracked at that commit:

- `.kanban.limit` / `.gallery.limit` / `.timeline.limit` -> 0 read points,
  against 8 for the identically-shaped `.kanban.groupByField` /
  `.gallery.coverField` / `.timeline.scale` control on the same instrument.
- The row caps objectui does read are `savedViewLimit` (a view's
  `pagination.pageSize`, else its flat `limit`) and the element block's own
  flat `limit`. `ListView`'s `baseProps` carries no `limit` on any branch.
- `ElementDataSourceGate`'s arm is `!fromView || !isUsableRowLimit(authored)`,
  reading the ELEMENT-face key, which is `.optional()` with no applied
  default. The arm is reachable; the view-face default never lands on it.

So the published «fills it only when unset» was narrower than the guard, and
the per-kind key #19226 declared reaches no consumer at all. Both are now
recorded where an author and an auditor read them.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
…ate the reference page

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 21, 2026
@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 37ed9ae04b7b656114009378fe62cd186ab3b168packageMentionDocs.

Which tree this was computed on

This run read content/docs from 304229705818df682866ab8d570fca5a1b152cda — the merge of head b922105e1629344f85c6f03519b9ff8bb64cbe86 into base 37ed9ae04b7b656114009378fe62cd186ab3b168, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 304229705818df682866ab8d570fca5a1b152cda && git checkout 304229705818df682866ab8d570fca5a1b152cda
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 37ed9ae04b7b656114009378fe62cd186ab3b168 b922105e1629344f85c6f03519b9ff8bb64cbe86 && git checkout -B drift-repro 37ed9ae04b7b656114009378fe62cd186ab3b168 && git merge --no-ff b922105e1629344f85c6f03519b9ff8bb64cbe86

node scripts/docs-audit/affected-docs.mjs --json 37ed9ae04b7b656114009378fe62cd186ab3b168

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

…s the node through a SPREAD

The at-tier review of #19533 returned FAIL on five prose grounds, all descending
from one hole: the zero was taken with a property-access instrument, and a spread
carries a key without ever spelling it.

Re-measured first-hand at the pin 87af769e9 with a SECOND instrument (spreads,
lit control ...mergedTimeline = 1 line), which returns four:

  plugin-list/src/ListView.tsx:2979    ...restKanban
  plugin-view/src/ObjectView.tsx:1638  ...restKanban
  plugin-view/src/ObjectView.tsx:1697  ...(viewOptions.gallery || {})
  plugin-view/src/ObjectView.tsx:1725  ...(viewOptions.timeline || {})

Neither restKanban destructure strips limit, so a view's per-kind limit -- the
materialized 100 included -- lands on the flat key ObjectKanban:553 and
ObjectTimeline:279 read. The $top it would govern is not issued on either route
(both hosts pass rows as a React data prop; children short-circuit at :559 / :420),
which is a statement about the query, not about the key being unread.

Gallery alone is genuinely read by nobody: ObjectGallery.tsx contains no limit.

Fixed: the rowLimitKey docblock and its contradicting describe, the kanban
docblock, the object-timeline read anchor, the VALUE posture paragraph, the
published timeline describe, one test comment, and the changeset -- which drops
from minor to patch (zero accept-set movement, zero export movement).

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
…ore the changeset to minor

Two CI reds from c6b3663.

1. `check:generated` — the rowLimitKey `.describe()` change is published on
   nine `content/docs/references/ui/view.mdx` rows and that page was left
   stale. Regenerated with `check:generated --fix`, never by hand; the diff is
   exactly 9 insertions / 9 deletions.

2. `Check Changeset` LEVEL axis (#16055 / #16776) refuses a PR that declares
   `Clause-②: yes` while grading no moved package `minor` or above. The
   at-tier review ruled `patch` on the lane's auxiliary-surface criterion, and
   a required gate insists on `minor`. Acting on the stricter of the two and
   leaving the conflict to its own card, per the dispatch protocol; the
   declaration is NOT flipped to `no` to make `patch` legal. The changeset
   prose now says the level is the gate's floor and not a behaviour change.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
…et to patch

At-tier FAIL, grounds 1-3. Grounds 1 and 2 were one error: the previous round
fixed a face-swap by swapping the other way. A view-face fact had sat on the
element-face key; the rewrite put an element-face key under a view-face fact.

Re-measured first-hand at pin 87af769e9:

 - ELEMENT FACE, a node's own nested timeline.limit: read on NO route.
   ObjectTimeline binds timelineConfig = schema.timeline (:262) and reads
   exactly eight members off it -- startDateField, dateField, titleField,
   endDateField, groupByField, colorField, metaFields (:520, behind an
   `as any` cast, which is why a timelineConfig?.x sweep under-counts) and
   scale. No spread of a node's own timeline block exists anywhere (0 hits;
   lit control = the view-block spreads, which fire). The gate mapping is
   limit: 'limit' (plugin-timeline/src/index.tsx:333) -- flat.
 - VIEW FACE, a ListViewSchema document's timeline.limit: that is the
   route-dependent one. ObjectView.tsx:1725 flattens it onto the node's flat
   limit (and that node then carries no timeline block at all);
   ListView.tsx:3084 keeps it nested and unread.

So every sentence now names its face first, in both docblocks, the published
describe, the test comment and the changeset -- the distinction is stated
rather than navigated, because three rounds have failed on it.

Also flagged, not fixed: a third route neither the card nor the first two
reviews described -- a hand-authored object-timeline node reaching
ObjectTimeline through SchemaRenderer with no adapter. Its nested
timeline.limit is unread there too, and that is the route an author face is
written for.

The second instrument is now recorded by its PREDICATE (a spread whose target
is the object literal an adapter returns as the node) rather than by its
count, so the reading can be re-taken without re-deriving the rule.

Changeset level to patch per the coordinator's correction: the gate's two
remedies are not interchangeable and this is the second one -- the level was
right and the declaration was wrong. The "Why minor" paragraph is dropped.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
…e arm — both removed

Round 4 leads, each verified first-hand before a line moved. Both halves of
lead 1 reproduce; the repair shipped in round 1 was wrong in its own way.

1. `ListViewSchema` REFUSES a flat `limit`. Measured on this tree with four
   lit controls: the minimal view document parses; + `pagination.pageSize: 50`
   parses; + per-kind `kanban.limit: 50` parses; + flat `limit: 50` is
   REFUSED with unrecognized_keys: ["limit"], the same verdict a bogus key
   gets. Only three `limit` members exist on any view document and all three
   are rowLimitKey(...) per-kind blocks; no retiredKey() tombstone for a flat
   one. So `savedViewLimit`'s fallback to `view.limit` names a THIRD face --
   a saved-view RECORD from the adapter's listViews() -- and stating it on the
   author face with no qualifier is the same face-merge this card has failed
   on three times. Dropped from the describe, recorded in the docblock.

2. The displaced-and-reported arm is unreachable from inside the accept set.
   isUsableRowLimit is `typeof v === 'number' && Number.isInteger(v) && v > 0`
   -- the same set this key declares as z.number().int().positive(). Measured
   per value: 1/25/100/5000 accepted and usable; 0/-1/2.5/'100'/null refused
   by both; unset accepted and not usable. So across the whole accept set
   "set but not usable" is EMPTY and the guard is exactly "only when unset"
   -- the wording round 1 retired as too narrow. Restored, with the reason.

The prose pin that guarded the retired sentence is replaced by the structural
fact underneath it: the schema's accept set and isUsableRowLimit coincide, so
the sentence is derivable rather than pinned, and either side widening reds it.

Lead 3: the property-access control is now published as its EXPRESSION plus
every hit -- 13 lines across 6 files -- with the filter that yields 8 stated
beside it (3 comments, 2 inside a quoted source-text pin; one hit set, three
readings). The spread predicate now also names what it EXCLUDES:
app-shell timelineViewOptions:206 and galleryViewOptions:342, options-bag
builders feeding ListView's nested forward.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
…three wordings overstated

Round 4 touched four files and not the changeset, so the tree carried both the
retracted claim and the corrected one at once. The changeset is the published
CHANGELOG entry for @objectstack/spec, an author-facing surface, which makes
that round 2's lesson repeated inside the tree instead of in the PR body.

1. Changeset 'What the published text said' rewritten to the round-4 claim:
   'only when unset' is exactly right for this face; the accept set is a
   SUBSET of isUsableRowLimit so 'set but not usable' is empty; the view half
   is pagination.pageSize ALONE, with savedViewLimit's flat view.limit named
   as the saved-view RECORD face it belongs to. Grade unchanged at patch --
   this is text, not level.

2. 'the SAME set this key declares' overstated and is now the derivable
   claim, accept-set SUBSET-OF usable-set, with the separating witness:
   2**53+2 is refused here (zod 4 .int() is safe-integer, too_big) and
   Number.isInteger calls it usable. Verified by parse, not asserted.
   The test title moves the same way and the witness is pinned as a case.

3. The test comment claimed 'either side widening reds this'. Half wrong, and
   now stated asymmetrically: the SPEC side reds (nullable, a 0 sentinel,
   dropping .int(), adding a .default() each fail a specific expect), but the
   GATE side CANNOT -- usableToTheGate is a transcription of isUsableRowLimit
   dated to pin 87af769e9, not an import, so a rewrite at objectui HEAD leaves
   this green. It refreshes only on a pin bump, by hand.

4. The instrument partition was off by one label: ListView.kanbanOptionsBag
   Canonical-8193.test.tsx:99 is an it() title string, not a comment. Now 2
   comments, 1 test-title string, 2 quoted-pin lines -> 8 executable of 13.
   The spread predicate's exclusion list also gains ListView.tsx:3044-3046
   (mergedGallery, nested) and app-shell ObjectView.tsx:1284 (a write back
   into a view document's own block, not a node build).

No describe changed, so no .mdx regenerates; check:generated is green without
--fix, which is the prediction this round was able to state in advance.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
@os-warren
os-warren marked this pull request as ready for review September 21, 2026 11:28
@os-warren
os-warren enabled auto-merge September 21, 2026 11:28
@os-warren
os-warren added this pull request to the merge queue Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants