Skip to content

feat(spec)!: eight list members of object-grid, object-kanban and object-calendar take the shape each block reads; object-grid columns held (#21464, stage 2) - #21559

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-21464-s-list
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-21464-s-list

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Part of #21464
Clause-②: yes (narrowing)

Fix round (contract review 5966757164, item 1)

The review found that the grid reads options off an AUTHORED object-grid column. The group-header formatter (plugin-grid/src/ObjectGrid.tsx:2997-3001 at the pin) takes the column whose field is the grouping field, reads colOverride?.options || objectDefField?.options with the column winning, and draws the group-header labels from it. objectui pins that as behaviour in gridGroupingMembers-8071.test.tsx:260-301. ListColumn declares no options, so the by-reference columns narrowing refused a value the grid draws, a working writer by the seat's test (5966636964). This round:

The other eight members stand as reviewed.

What this does

Stage 2 (S-list) of the ComponentPropsMap z.unknown() close-out, per triage 5961300594, the seat answer 5963787404 (staging A) and the claim 5965611825. Eight members the list blocks read with a fixed shape were z.unknown() (an array of it for the lists). Any value passed the component-props gate, and the renderer dropped or substituted an off-shape one with no report. Each member now takes the shape its block reads, measured at the .objectui-sha pin 89cad75d55. The family's ninth, object-grid columns, is held (above).

row · member was now read point at the pin
object-grid · columns z.array(z.unknown()) held, unchanged: z.array(z.unknown()) plugin-grid/src/ObjectGrid.tsx:2158 normalizeColumns; the group-header formatter reads an authored column's options (:2997-3001), which ListColumn does not declare (objectstack-ai/objectui#11544)
object-grid · fields z.array(z.unknown()) z.array(z.string()), the measured shape (no list-view counterpart) plugin-grid/src/ObjectGrid.tsx:1946; the draw path looks each entry up as objectSchema.fields[fieldName] (:3969, :4012)
object-grid · selection z.unknown() ListViewSchema.shape.selection (SelectionConfigSchema), by reference .type, :4799-4812
object-grid · selectable z.unknown() boolean, 'single' or 'multiple', the measured shape :4813-4815, handed to the table at :5333; components/src/renderers/complex/data-table.tsx:644 resolveSelectionMode
object-grid · rowActions z.array(z.unknown()) ListViewSchema.shape.rowActions, by reference :1834-1835, string[]
object-grid · bulkActions z.array(z.unknown()) ListViewSchema.shape.bulkActions, by reference :4763 batchActions ?? bulkActions, then resolveBulkActions by name
object-grid · batchActions z.array(z.unknown()) the same def as bulkActions the same read, which takes batchActions first
object-kanban · columns z.array(z.unknown()) all bare value strings, or all lanes { id, title, cards?, limit?, className?, collapsed? } (module-private ObjectKanbanLaneSchema) plugin-kanban/src/ObjectKanban.tsx:1177-1188 dispatches on the first entry; index.tsx:129-158 buckets by id and keeps static cards; KanbanImpl.tsx:540, :568, :742 read limit, className, collapsed
object-calendar · calendar z.unknown() ListViewSchema.shape.calendar (CalendarConfigSchema), by reference plugin-calendar/src/ObjectCalendar.tsx:294-297 returns the block as the config; :857, :1056, :1141 read its five bindings

A static kanban card is a record row. Its id and title are typed, and the rest of the card is the row's own values. That one new z.unknown() member, object-kanban columns[].cards[].*, carries a records line in the enumeration pin.

bulkActions / batchActions (A4). The grid reads schema.batchActions ?? schema.bulkActions (ObjectGrid.tsx:4763), so batchActions is the second spelling of one capability, read first. objectui's own type calls it the legacy alias. ListViewSchema declares only bulkActions. Both members now hold bulkActions's def, and neither is retired here.

selection default. SelectionConfigSchema defaults type to none. The grid reads an object with no type as ON (objectui#9837, ruling A-prime: presence enables). That default reaches only a parsed document, never the bag the grid reads. It is the list view's declaration either way, and objectui#9837 holds the question. The member's docblock records it.

The census (A1), whole

A writer is a page-component node: an object literal naming the type, a literal annotated with the block's type, a schema={{…}} on the block's React component, a call into a local helper that builds the node, or a direct parse through the row. Each member's value is resolved through same-file constants. The control is objectName on the same nodes. The instrument is a TypeScript-AST walk over every .ts, .tsx, .js, .json, .md, .mdx and .yaml file, with fenced code in the documents parsed too.

corpus object-grid nodes object-kanban nodes object-calendar nodes control objectName
objectstack 49161683fb (examples/, packages/ incl. packages/apps/, content/, skills/, apps/) 57 30 5 47 / 27 / 4
objectui 89cad75d55 (whole tree) 689 240 160 293 / 108 / 98
row · member objectstack values (parse) objectui static values (distinct) · parse · refused · not static
grid columns (held, not narrowed) 5 310 (143) · not parsed by this PR · 20 not static
grid fields 0 16 (10) · 13 · 3 · 2
grid selection 0 17 (4) · 17 · 0 · 1
grid selectable 0 2 (1) · 2 · 0 · 1
grid rowActions 0 10 (5) · 10 · 0 · 1
grid bulkActions 0 23 (9) · 21 · 2 · 1
grid batchActions 0 5 (3) · 5 · 0 · 0
kanban columns 1 (1) 108 (39) · 107 · 1 · 12
calendar calendar 0 60 (26) · 58 · 2 · 6

The objectui values were parsed through the built rows on this branch. Across the eight typed members, objectstack holds one value (the protocol docs' lane example, which parses), and objectui holds 241 static values: 233 parse and 8 are refused. The grid columns row is listed for completeness. Its 5 objectstack values (the showcase's two grids among them) and 310 objectui values meet no new shape here.

Writer parse results (A3)

No refused value is one the renderer draws. Each of the 8 refused objectui values is a test fixture whose value the renderer drops, skips or refuses:

  • 2 { name } entries in bulkActions (bulkActionMembers-8071, bulkActionDefsUnusableMember-8730). The fold skips them, and both tests assert the skip.
  • 3 object entries in fields (serverGroupedSelectIdentity-11105). They copy the node the list view hands the grid at run time, as that test's own header says, so they are not an authored page.
  • A lane color (objectKanbanColumnMembers-8071). The console retired it, and the test marks it an undeclared member. The lane now refuses it with a prescription naming className.
  • The calendar's retired dateField / endField aliases (calendar-date-alias-refusal-8355). The test asserts their refusal.

The 24 values that are not static are helper parameters, .map results and the run-time hand-offs (plugin-view/src/ObjectView.tsx:2462, plugin-designer). None is an authored page.

The held member. Under the first head, object-grid columns was narrowed by reference, and this list carried 40 refused grid-column values. Among them, "16 column keys the grid never reads" counted 14 editable and 2 options, and said no authored-column read names either key. That was right for editable and wrong for options: the 2 options values (gridGroupingMembers-8071) are drawn in the group headers. So columns is held, and its 40 values are no longer refused by this PR.

Under the triage caveat ("a narrowing that would refuse a measured writer is reported, not shipped silently"), this list is the report. A3 is applied with the seat's test (5966636964): a writer is a value the renderer draws. A refused fixture that probes the renderer's drop is not one. On that test the eight members stand, and columns is held.

A2, per member

Typed by reference (4): grid selection, rowActions, bulkActions; calendar calendar. Typed to the same def (1): grid batchActions. Typed to the renderer's read (3): grid fields, selectable; kanban columns. Held for a ruling (1): grid columns (objectstack-ai/objectui#11544). None is runner-forwarded.

The pin (A5)

  • Eight staged lines leave the enumeration pin's ledger, and its list-family stage goes with them. object-grid columns[] stays as held-for-decision. One records line is added for object-kanban columns[].cards[].*.
  • no-reader is removed. It had no user since PR feat(spec)!: ai:chat_window is retired — refused by name at the schema door, the floating chat overlay is the AI chat entry point (#21504) #21531, the pin's own checks never require a kind to be in use, and no other file pins its vocabulary (git grep no-reader finds only an unrelated prose use in scripts/pm/check-half-states.mjs).
  • The typed members are pinned in their own file, component-list-family-typed-members.pin.test.ts, as finding(spec): ComponentPropsMap['object-grid'] keeps resizableColumns as a live alias of resizable, and types six members ObjectGrid reads with fixed shapes as z.unknown() (rowHeight: 42 passes every door) #21445's were. §1 checks that each declared shape parses, byte-identical, or to what the list view's schema answers where a default materializes. §2 checks each refusal by code and path, and for the kanban's two-array union by the arm's own issue. §3 checks identity with the list view's defs and the measured vocabularies. §4 checks the D3 registration.
  • Ablation, fix round, at 8b276755c2: object-grid rowActions was reverted to z.array(z.unknown()).optional(), with no ledger line. It landed: anchor x1 to x0, replacement x0 to x1, blob 285edfb205 to 2b6b2f8643. Red: Tests 4 failed | 95 passed (99). The enumeration pin's §1 received exactly [ 'object-grid rowActions[]' ], its census-equals-ledger control failed, and the companion pin failed its two rowActions cases. The restore was proven: blob after restore 285edfb205 == blob at HEAD, and git diff HEAD was empty. Green rerun: Tests 99 passed (99).
  • Ablation, first head, at 870e7327ec, via node scripts/ablation-replace.mjs in wrap mode. The mutation reverted object-grid selection to z.unknown().optional(), with no ledger line. It landed: anchor x1 to x0, replacement x0 to x1, blob e60c46e776 to 2111ab1538. Both pins went red: Tests 7 failed | 101 passed (108). The enumeration pin's §1 received exactly [ 'object-grid selection' ], and its census-equals-ledger control failed. The typed-member pin failed its five selection cases. The restore was proven: blob after restore e60c46e776 == blob at HEAD, and git diff HEAD was empty. The green rerun showed Tests 108 passed (108). The pins import ./component.zod from source, so no build sits between mutation and run.

The rest of the kit (A6, A7)

Gates, at 8b276755c2 (after merging origin/main ce532184d1 through os-regen-merge.sh)

  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 114 commands (9 paths, 688 changed lines, merge base ce532184d1). Every exit code was written to disk before any pipe. --ran reconciled: 114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN.
  • On this head, after a full turbo run build (72 tasks), all 114 ran and every one exited 0. On the first head, six gates answered PREREQUISITE NOT MET (exit 3) before the build; those lines were kept apart, not counted.
  • pnpm --filter @objectstack/spec build: exit 0. check:generated: exit 0, "All 15 generated artifacts are up to date". check:liveness, check:migration-registry, check:strictness-ledger, check:authorable-surface and check:api-surface: exit 0.
  • pnpm --filter @objectstack/spec test: Test Files 606 passed (606), Tests 17952 passed, 1 todo. typecheck: exit 0, check:test-typecheck: OK.
  • pnpm --filter @objectstack/lint test (the one import side of ComponentPropsMap): 119 files, 5620 tests passed.
  • pnpm check:doc-authoring and pnpm check:nul-bytes: exit 0.
  • check-widening-tells: --declaration no gives exit 4 with 9 T1 tells, all at the new lane schema's keys inside the former z.unknown() bag. That is the shape the gate's own text rules a true refusal, so declare yes. --declaration yes gives exit 0.
  • The changeset gates were run with this body as the --event payload; their verdict lines are in the dev report.
  • NOT MEASURED: the Console Pin Gate, Dogfood and the full pnpm lint. Reason: they are CI-owned. objectui's source indexes SpecObjectCalendarProps['data'], SpecObjectFormProps['layout'] and SpecObjectKanbanProps['swimlaneField'], none of them a narrowed member.

Acceptance notes

#21464 remains open for S-form, S-metric and S-objectui-held. object-kanban conditionalFormatting stays held on objectstack-ai/objectui#11522, and object-grid columns on objectstack-ai/objectui#11544.


Generated by Claude Code

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation protocol:ui tests tooling labels Oct 3, 2026
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

6 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/ui/component-type-vocabulary.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/ui/component-type-vocabulary.ts) — pages documenting those are invisible to this run
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 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; 97 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 — 138 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 10454b3afa94d49e6e424cc16fbbff3a898f3ad8 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 283c449372a495f572a38ad2028223b7034b53e2 — the merge of head 8b276755c216f2e2a122ea25c63fad49347a94a1 into base 10454b3afa94d49e6e424cc16fbbff3a898f3ad8, 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 283c449372a495f572a38ad2028223b7034b53e2 && git checkout 283c449372a495f572a38ad2028223b7034b53e2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 10454b3afa94d49e6e424cc16fbbff3a898f3ad8 8b276755c216f2e2a122ea25c63fad49347a94a1 && git checkout -B drift-repro 10454b3afa94d49e6e424cc16fbbff3a898f3ad8 && git merge --no-ff 8b276755c216f2e2a122ea25c63fad49347a94a1

node scripts/docs-audit/affected-docs.mjs --json 10454b3afa94d49e6e424cc16fbbff3a898f3ad8

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 99e0dd7c915c117929a45a2130049c02f4450cdd
Local-runs: none

Isolated contract reviewer for PR #21559 (card #21464, stage 2 / S-list), written 2026-10-03T07:24Z. Inputs: the card body and its ten comments (triage 5961300594, the stage-1 claim 5962381903, dev reports 5963737459 / 5964076083 / 5966618272, seat answers 5963787404 / 5966636964, the ACCEPT 5964211944, the landing 5964405985, the stage-2 claim 5965611825); PR #21559 (body, the 9-file list, the net diff against main, +679 / -45, the docs-drift bot comment 5966588436); the 34 check-runs on this head; the precedents (PR #21463's record 5961282483; PR #21502's records 5963877733 and 5964200665, landed 529d9711fb); and objectui at the .objectui-sha pin 89cad75d55, read file by file through the contents API: plugin-grid/src/ObjectGrid.tsx, columnSpellingDiagnostics.ts, resolveBulkActions.ts, inline-edit-options.ts, plugin-kanban/src/ObjectKanban.tsx, index.tsx, KanbanImpl.tsx, KanbanBoardCore.tsx, types.ts, plugin-calendar/src/ObjectCalendar.tsx, components/src/renderers/complex/data-table.tsx, types/src/complex.ts, and the fixtures gridGroupingMembers-8071, serverGroupedSelectIdentity-11105, inlineEditPersistence. objectstack's view.zod.ts was read at the head through git show. Nothing was built, run or re-run.

Gate verdicts on this head, as they stood at 07:23Z: 34 check-runs — 31 success · 0 failure · 1 in progress (Test Core (1/6)) · 2 skipped by design (Console Pin Gate, Packed-tarball smoke (opt-in)). The other five Test Core shards, Lint & Repo Gates, all four Type Check jobs, Check Changeset, Governed Surface Queue Guard, Spec property liveness, both Part-of gates and the three Dogfood gates are success. An in-progress shard is read as in progress, not as a pass. The verdict below is a contract judgment and does not rest on it.

① Derived judgments

1. object-grid columns → ListViewSchema.shape.columns by reference — WRONG AS SHIPPED; the member is held under the seat's own test. The shape itself matches the read: normalizeColumns (ObjectGrid.tsx:819-830) dispatches the whole array on its first entry (all strings or all ListColumn objects), the summary memo (:3170-3177) and the draw path (:3435-3443, resolvesToDataColumn in columnSpellingDiagnostics.ts:83-88) keep only an entry with a non-empty string field, the retired accessorKey / header spellings and a { name } entry draw nothing and are named by the diagnostic (columnSpellingDiagnostics.ts:76-79, :124-142), and every ListColumnSchema member is read in the path-A column literal (:3664-3690: field, label, width, align, sortable, resizable, pinned, wrap, type, link, action, prefix, with hidden in the filter and summary in useColumnSummary). editable on a column is indeed not read: that literal carries no editable, the enrichment at :4365-4373 starts from it ({ ...col } of the EMITTED column, not the authored one) and only ever forces false for a non-editable field, and data-table.tsx:2521 reads the emitted value — so the authored { field: 'name', editable: false } in inlineEditPersistence.test.tsx:68 is dropped, as the dev says. But options IS read from an authored column at the pin. The group-header formatter (ObjectGrid.tsx:2978-3041, groupValueFormatter) does normalizeColumns(schema.columns), finds the authored entry whose field is the grouping field (:2997-2998), and reads colOverride?.options || objectDefField?.options (:3001) — the column's own options WIN over the object field's — into the per-value label map the group headers draw (:3021-3030). objectui pins that as behaviour, not as a drop: gridGroupingMembers-8071.test.tsx:33-42 ("the memo reads columns[].options, which is not among ListColumn's fourteen declared members … while this renderer honours it … pinned below AS BEHAVIOUR and reported as a finding, not repaired here"), :260-283 (an authored options: [{ value: 'won', label: 'Closed Won' }, …] makes the headers read Closed Lost / Closed Won, with a no-options control reading lost / won), and :285-301 (order sorts by those rendered labels). Those are the 2 options values the dev's census refused, filed under "16 column keys the grid never reads … No authored-column read names either key". That sentence is wrong for options: it is a value the renderer draws, authored on an object-grid column, in a fixture written to pin that it is honoured — a working writer by the test the seat set in 5966636964 ("If the review finds any renderer path at the pin that reads either key from an authored object-grid column, that member's narrowing is held (option C for columns), and a fix round keeps it in the pin's ledger"). That is also the shape stage 1 held kanban conditionalFormatting on (5963787404 §2): objectui fixtures author a value the renderer reads. That the list view's own door already refuses options (strict ListColumnSchema) and that objectui's own header calls the read a declared != enforced split to retire are the arguments for the ruling, not a licence to ship past the caveat ("reported, not shipped silently"; dispatch A3 "do not ship that member's narrowing"). Consequences for the fix round: object-grid columns[] returns to the enumeration pin's ledger as held-for-decision (the kind still exists; its reader is ObjectGrid.tsx:2158 and :2997-3001), its cases leave the companion pin (§1 two, §2 six, §3 one), and the four texts that assert the grid never reads options are corrected: the columns docblock (component.zod.ts, "a key the grid never reads (editable, options)"), the changeset's FROM → TO row ("a column key the grid never reads, such as editable or options — delete the key"), its "Who is affected" paragraph, and the PR body's A3 list. The renderer-side read is the objectui finding objectui's own test already names; the seat decides whether it is carded there (as objectstack-ai/objectui#11522 was for kanban) before the member is typed.

2. object-grid fields → z.array(z.string()) — RIGHT. The seat's second test ("if a draw path reads an object entry there") is not met. The draw path reads names only: fieldsToShow = schemaFields (:3968-3969) then objectSchema.fields?.[fieldName] with if (!field) return; (:4012-4014), and the inline-data path (:3875-3885) likewise emits accessorKey: fieldName. The one place an object entry is read is the fetch projection (:2583-2589, columnIdentity) and the $expand harvest (:2786-2789) — the serverGroupedSelectIdentity-11105 fix, whose own header (:9-31) and cast (:127-132: "ObjectGridSchema.fields is declared string[], and this is the object-entry node the grid receives anyway") say it exercises the node ListView hands the grid at run time, not an authored page. Those 3 refused values are that hand-off. No list view declares the member; the renderer's declared type is string[].

3. object-grid selection → ListViewSchema.shape.selection (SelectionConfigSchema.optional()) — RIGHT. The grid reads schema.selection?.type with presence-enables and type: 'none' as the explicit off (:4799-4812, objectui#9837 A-prime). Observation, carried: the shared def defaults type to none, so { selection: {} } PARSES to { type: 'none' } (the companion pin §1 asserts exactly that) while the raw bag means ON at the grid; the default lives in parsed output only, the list view door has carried it since the def was declared, objectui#9837 holds the question, and the member docblock records it. Not a defect of this diff.

4. object-grid selectable → boolean | 'single' | 'multiple' — RIGHT. Read only when selection is absent (:4813-4815), handed to the table as selectable: selectionMode (:5333); resolveSelectionMode (data-table.tsx:637-648) honours 'single', treats falsy as off, and any other truthy value as multiple. The declared set is the table prop's declared type. 'none', which the table tolerates "verbatim from raw SDUI JSON" on its own door, is refused here with a lossless rewrite (false), and the census authors no 'none' (2 values, both parse). No working writer refused.

5. object-grid rowActions → by reference, string[] — RIGHT. :1834-1835 reads Array.isArray(schema.rowActions) ? schema.rowActions : [] typed string[]; edit / delete select the generic entries.

6. object-grid bulkActions / batchActions → ListViewSchema.shape.bulkActions, one def for both — RIGHT. :4763 reads schema.batchActions ?? schema.bulkActions, filters by String(a).toLowerCase() !== 'delete' and folds names through resolveBulkActions (its contract, resolveBulkActions.ts:10, :45: "bulkActions: string[] — bare action names"); a { name } entry is the def vocabulary (bulkActionDefs) and the two refused values are the fixtures asserting the skip. One capability, one accept set; neither spelling retired, which the direction does not ask for.

7. object-kanban columns → all strings, or all lanes { id, title, cards?, limit?, className?, collapsed? } (module-private strict ObjectKanbanLaneSchema, cards[] a loose object with id and title) — RIGHT. effectiveColumns (ObjectKanban.tsx:1161-1188) dispatches on the first entry and uses a string list only with no groupBy (:1179-1186); bucketCardsIntoColumns (index.tsx:129-158) reads id and cards (col.cards || [], so cards optional requires no more than the read); KanbanImpl.tsx reads limit (:540, :591), className (:568), collapsed (:742), title and cards. The member census over the three files finds id, title, cards, limit, className, collapsed and nothing else. objectui's own KanbanColumn (types/src/complex.ts:126-171) declares exactly those six, with color a RETIRED tombstone ("zero column.color read sites"), which the lane's color guidance naming className mirrors; KanbanCard (:69-121) declares id: string; title: string plus [key: string]: any, which is the loose object. limit positive-int refuses 0, which the read treats as no limit (column.limit && …); no writer authors it. The one refused value (a lane color, objectKanbanColumnMembers-8071) is the tombstoned key.

8. object-calendar calendar → ListViewSchema.shape.calendar (CalendarConfigSchema.optional()) — RIGHT. getCalendarConfig (ObjectCalendar.tsx:294-298) returns the block as the config; every read destructures exactly the five bindings (:857, :1056, :1141, :1342-1343); startDateField required is the read ([startDateField] keys every event). The retired dateField / endField aliases were never read off calendar (the flat spellings at :309-317 are the other rung), and the 2 refused values are the alias-refusal fixture.

9. The enumeration pin. Nine staged lines and the list-family stage leave; no-reader leaves with no remaining user (its one line went with ai:chat_window in PR #21531); one records line is added for columns[].cards[].*, the loose object's catchall the walk reaches through the union arm — honest (KanbanCard's [key: string]: any). The census-equals-ledger construction stands from record 5963877733 ①2. With ①1 held, the fix round re-adds object-grid columns[] as held-for-decision. 10. The companion pin component-list-family-typed-members.pin.test.ts — §1 parse-to-identity with the shared defs, §2 code AND path (arm issues for the two unions), §3 def identity through ._zod.def, §4 the D3 registration; consistent with the schema as diffed; the columns cases go with the hold. 11. The kit — D3 entry ui-object-grid-kanban-calendar-list-members-typed (D3 only, no conversion, reason recorded) and its generated registry region (text-identical to the entry file); rationale fragment order 66, equal to #21547's page-requires-non-compiled-kind-refused (equal orders render in id order per the header; the duplicates 56 / 60 / 62 pre-exist on main); check:migration-registry runs in Lint & Repo Gates, success. RIGHT in form; the entry's text does not name options, so only the four texts in ①1 move. 12. Generated artifacts — component.mdx (the sync step is in Type Check · source gates, success); strictness counts ui/ 189 → 191 (+1 strict for the lane, +1 passthrough for the card; component.zod.ts 59 → 61) match the two new sites. RIGHT. 13. Comment-only edits — the KNOWN_COMPONENT_TYPES docblock adds ai:chat_window (review 5965171663 ①10); the ObjectGridPropsParsed docblock now names the three defaults (navigation's, selection.type, a column's prefix.type), which is accurate. RIGHT.

② Semver level

Level and arm RIGHT; one migration row WRONG on a fact. .changeset/21464-component-props-list-family-typed.md: '@objectstack/spec': minor, feat(spec)!:, Clause-②: yes (narrowing) (also PR body line 2), the BREAKING banner with the launch-window minor sentence, exactly one ADR-0087 marker (registered, the new id), a FROM → TO table, the measured census, "Deployed metadata was not measured". Check Changeset is success, and the dev's check-changeset-no-major / check-adr-0087-registration / check-widening-tells readings (9 T1 tells at the lane's keys inside a former universal acceptor, the shape the gate rules a true refusal — declare yes) agree with the text, as #21445's did. The row "object-grid a column key the grid never reads, such as editable or options — delete the key" ships to consumers as CHANGELOG.md and tells an upgrading author to delete a key that changes the group headers a grouped grid draws; it and the "16 column keys the grid never reads" sentence are corrected in the same round as ①1 (right for editable, wrong for options). Observation, carried from 5963877733 ②: a bullet describes the enumeration pin, a test, in release prose.

③ Boundary flags

  • open_questions[0] (A3 versus the census) — contract matter; the seat's answer 5966636964 is applied as written. Option A with one test. The test is met on columns[].options (①1) and not met on editable (①1) or on the fields object entries (①2). So eight of the nine narrowings stand on the seat's reading — a fixture probing a value the renderer drops, skips, diagnoses or refuses is not a working writer — and object-grid columns is held (option C for columns) pending the seat's ruling on the objectui read.
  • Deviation "A3 applied as stage 1 and finding(spec): ComponentPropsMap['object-grid'] keeps resizableColumns as a live alias of resizable, and types six members ObjectGrid reads with fixed shapes as z.unknown() (rowHeight: 42 passes every door) #21445 applied it, not literally" — right in principle (the precedents did exactly that), wrong on the one fact above.
  • Deviation: a companion pin file beyond the claim's named surface — seat matter; finding(spec): ComponentPropsMap['object-grid'] keeps resizableColumns as a live alias of resizable, and types six members ObjectGrid reads with fixed shapes as z.unknown() (rowHeight: 42 passes every door) #21445 did the same, and the enumeration pin's header points to it.
  • Deviation: the kanban reader correction (KanbanBoardCore.tsx:95 reads already-normalized lanes; the authored read is ObjectKanban.tsx:1177-1188) — confirmed at the pin (KanbanBoardCore.tsx:92-100 takes schema.columns ?? [] from the parent).
  • Deviation: the new z.unknown() member columns[].cards[].* recorded as records — right (①7, ①9).
  • Deviations: two os-regen-merge.sh merges of main (last 72af58c621), the AST census instrument (counts not comparable to stage 1's), commit trailers and footer per AGENTS.md — seat matters; every generated artifact is current on this head by CI's own sync steps.
  • not_measured: Console Pin Gate, Dogfood, full lint — CI-owned; on this head the pin gate is skipped by design, Dogfood 1/3 · 2/3 · 3/3 and Dogfood Verify CLI are success, Lint & Repo Gates is success. objectui's source at the pin indexes none of the nine members.
  • out_of_scope[0], the selection.type default (objectui#9837) — confirmed at :4799-4812 against SelectionConfigSchema; carried, not filed (①3).
  • out_of_scope[1], objectui fixtures to respell at the next spec bump — holds for the drop-probes; gridGroupingMembers-8071 is not one of them: its refusal is the read in ①1, which objectui's own header already files as a declared != enforced finding with "a retirement decision with its own authors measurement". That is the seat's ruling, not a respell.
  • out_of_scope[2], the stale ObjectCalendar.tsx docblock about allDayField — objectui prose; not re-derived here.
  • Docs Drift Check 5966588436 — advisory; no anchor for component-type-vocabulary.ts (comment only here), coarse fallback only.
  • The file list touches no governed surface (Governed Surface Queue Guard success); the PR is Part of #21464, both Part-of gates success; a draft, auto_merge null; no objectui edit and no renderer change, as the claim required. The landing is the owning seat's, after the fix round's own record.

Implemented-by: claude/issue-21464-s-list
Reviewed-by: session_01YDt3PzwfrkuFzUBF89WPmM

VERDICT: FAIL

Failed item (one): object-grid columns — the grid at the pin reads columns[].options from an authored column (ObjectGrid.tsx:2997-3001, the group-header formatter) and draws the group headers from it, and objectui pins that as behaviour (gridGroupingMembers-8071.test.tsx:260-301), so the by-reference narrowing refuses a working writer; by the seat's test in 5966636964 the member is held (option C for columns) and kept in the enumeration pin's ledger, and the changeset row, the member docblock, the "Who is affected" paragraph and the PR body that say the grid never reads options are corrected. The other eight narrowings, the kanban lane, the pins, the kit, the generated artifacts and the semver level pass as judged above; a new head with that fix needs its own record.


Generated by Claude Code

claude added 2 commits October 3, 2026 07:32
… authored column's options, which ListColumn does not declare (#21464 stage 2)

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot changed the title feat(spec)!: the list members of object-grid, object-kanban and object-calendar take the shape each block reads (#21464, stage 2) feat(spec)!: eight list members of object-grid, object-kanban and object-calendar take the shape each block reads; object-grid columns held (#21464, stage 2) Oct 3, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 8b276755c216f2e2a122ea25c63fad49347a94a1
Local-runs: none

Isolated contract reviewer for PR #21559 (card #21464, stage 2 / S-list), second record, after the fix round on the FAIL record 5966757164 (head 99e0dd7c), written 2026-10-03T08:45Z. Inputs: the card body and its eleven comments (triage 5961300594; the stage-1 claim 5962381903, reports 5963737459 / 5964076083, seat answer 5963787404, ACCEPT 5964211944, landing 5964405985; the stage-2 claim 5965611825, dev report 5966618272, seat answer 5966636964, fix-round report 5967212722); PR #21559 (title, body, the 9-file list, the net diff against main, +646 / -42, and its two thread comments: the docs-drift bot 5966588436 and record 5966757164); the 42 check-runs on this head; the net diff of the previous head against main (GitHub compare), diffed against this head's to isolate the fix round; the precedents (PR #21463 record 5961282483; PR #21502 records 5963877733 and 5964200665, landed 529d9711fb); objectui at the .objectui-sha pin 89cad75d55 (the pin file at this head reads 89cad75d55702cc4f267bead5bf267de575d5842), file by file through the contents API: plugin-grid/src/ObjectGrid.tsx, columnSpellingDiagnostics.ts, resolveBulkActions.ts, __tests__/gridGroupingMembers-8071.test.tsx, components/src/renderers/complex/data-table.tsx, plugin-kanban/src/ObjectKanban.tsx, index.tsx, KanbanImpl.tsx, KanbanBoardCore.tsx, plugin-calendar/src/ObjectCalendar.tsx, types/src/complex.ts; objectstack's view.zod.ts and the enumeration pin at this head through the contents API; the hold carriers objectui#11544 and objectui#11522 (state and title only). Nothing was built, run or re-run; the local checkout was not fetched into or written.

Gate verdicts on this head, at this read: 42 check-runs, all completed — 38 success · 0 failure · 0 in progress · 4 skipped (Console Pin Gate and Packed-tarball smoke (opt-in) by design; Auto Label and Check PR Size on the second workflow run 37109937089, the one the title-and-body edit triggered, after both succeeded on the first run 37107013579). Success: all six Test Core shards and the rollup, Lint & Repo Gates, the four Type Check jobs and TypeScript Type Check, Check Changeset on both runs, Governed Surface Queue Guard, Spec property liveness, both Part-of gates, both claim gates, both single-writer gates, Dogfood Regression Gate 1/3 · 2/3 · 3/3 and rollup, Dogfood Verify CLI, Build Core, Build Docs, Temporal Conformance, Check Documentation Links, Flag docs affected by code changes. The failures on the intermediate heads 91fafb26 (cancellations) and d3a5b3ab (the generated-docs sync step, one commit before the regenerated page) are not this head's.

① Derived judgments

1. The failed item of 5966757164, object-grid columns — RIGHT now: held at its pre-PR acceptance, and every text agrees.

  • The member. columns: z.array(z.unknown()).optional().describe('Columns: field names or column definition objects') is a CONTEXT line in the net diff, so it is byte-identical to main; the accept set is the pre-PR one. Its new docblock records the hold with the reads it rests on, each confirmed at the pin: normalizeColumns (ObjectGrid.tsx:819-830, dispatching on the first entry) read at :2158; the group-header formatter groupValueFormatter (:2978-3041) normalizes schema.columns, finds the authored column whose field is the grouping field (:2997-2998) and reads colOverride?.options || objectDefField?.options (:3001), the column's list winning, into the label map the headers draw (:3021-3030); objectui pins it as behaviour (gridGroupingMembers-8071.test.tsx:33-42 header, :260-283 with a no-options control, :285-301 where order sorts by those labels). The docblock's parenthetical that a column editable is read nowhere off an authored column still holds: the path-A column literal (:3664-3690) carries no editable, and the enrichment at :4365-4373 spreads the EMITTED column. The carrier objectui#11544 exists, open, filed by the seat, bug · priority:p2 · pm:queue · domain:ui, and its measured section names the same lines.
  • The enumeration pin. on(['object-grid'], ['columns[]'], staged('held-for-decision', 'plugin-grid/src/ObjectGrid.tsx:2158 (normalizeColumns), columns[].options drawn by the group-header formatter at :2997-3001')) sits beside the kanban conditionalFormatting line, in the shape that precedent uses; the reader string matches §2's \.tsx?:\d check; held-for-decision is still a declared stage ("a by-reference shape exists, but measured writers author values it refuses"), which is this member's case exactly. The walk keys an array-of-unknown member columns[], so the ledger key is the census key; list-family leaves the STAGES table with its last member either typed or held. The header now says the family's ninth is held below.
  • The companion pin. Diffing the two heads' net diffs: the columns cases are gone — §1 two BYTE_IDENTICAL rows (field names; column entries) and the prefix parsed-default case (three), §2 five UNION_REFUSED rows (the mixed list, accessorKey / header, name, editable, a numeric columns), §3 the union-def identity case (one). The dev's three / five / one is the true count; 5966757164's two / six was a miscount of the same cases. The header says eight of nine, names the ninth as held with the carrier, and §3's "four members a list view also declares" is now the count (selection, rowActions, bulkActions, calendar). ListViewSchema is still imported for those four.
  • The D3 entry, the registry, the fragment. surface lists the eight members; replacement and reason say the grid's columns is not narrowed and why; acceptanceCriteria reads "the eight members' paths"; the accessorKey / name sentences are gone. The generated region in registry.ts is text-identical to the entry file on surface, replacement, reason and acceptanceCriteria (compared field by field after unfolding the string concatenation). The rationale fragment at order 66 says "eight list members" and "the grid's columns stays open: its group headers draw an authored column's options".
  • The changeset. Title: "eight list members … the grid's fields, selection, selectable, rowActions, bulkActions and batchActions, the kanban's columns and the calendar's calendar". The four columns FROM → TO rows are gone, including the one that told an author to delete options; a new bullet says columns is not narrowed and names the group-header read; "Who is affected" now counts the eight members (241 static / 233 parse / 8 refused / 24 not static) and says the grid's columns (310 static) is held because 2 of them author an options the grid draws.
  • The reference page. The object-grid columns row is any[], a context line (unchanged from main); the ObjectGridProps.columns[number] nested-shape table of the previous head is gone; check:docs runs in Type Check · source gates, success.
  • The PR. Title says eight members with columns held. Body: a "Fix round" section heads it; the member table's columns row reads "held, unchanged"; A3 carries "The held member" ("right for editable and wrong for options"); A2 reads "Held for a ruling (1)"; A5 says columns[] stays as held-for-decision; the Acceptance notes name the read and the carrier. No text at this head claims columns is typed.

2. The delta between the heads is confined to the hold — RIGHT. In component.zod.ts the fix round changed three things only: the grid row's header docblock ("Six list members", the three by-reference members, columns held), the columns docblock and member, and the ObjectGridPropsParsed docblock, which drops the column prefix.type default and now names "navigation's four and selection.type" — accurate: NavigationConfigSchema defaults mode, preventNavigation, openNewTab and size (view.zod.ts:2291, :2302-2303, :2312), and SelectionConfigSchema defaults type (:1131). Nothing moved in the eight typed members, the kanban lane schema, the calendar member, the strictness counts or the vocabulary docblock. So ①2-①8 of 5966757164 stand; each was re-read at the pin for this record and holds:

3. fields → z.array(z.string()) — RIGHT. :1946 reads it; the draw path assigns fieldsToShow = schemaFields as string[] (:3968-3969) and looks each up as objectSchema.fields?.[fieldName] with if (!field) return; (:4012-4014); the inline-data path maps names likewise (:3875-3885). The object-tolerant projection (:2583-2589, columnIdentity) is the hand-off ListView makes, as the serverGroupedSelectIdentity-11105 fixture's own header says; not an authored page.

4. selection → ListViewSchema.shape.selection — RIGHT. It is SelectionConfigSchema.optional() (view.zod.ts:2701), a strict { type } with none / single / multiple (:1127-1132); the grid reads schema.selection?.type with presence-enables and 'none' as the explicit off (:4799-4812). Carried: the shared def's type default makes { selection: {} } PARSE to { type: 'none' } while the raw bag means ON at the grid; the companion pin §1 asserts exactly the parsed answer; objectui#9837 holds the question; the docblock records it.

5. selectable → boolean | 'single' | 'multiple' — RIGHT. Read only when selection is absent (:4813-4815), handed to the table as selectable: selectionMode (:5333); resolveSelectionMode (data-table.tsx:644-648) returns single for 'single', none for 'none' or a falsy value, multiple otherwise, and its declared input is boolean | 'single' | 'multiple' plus a 'none' the table tolerates "verbatim from raw SDUI JSON". The spec refuses 'none' with a lossless rewrite (false); the census authors none.

6. rowActions → ListViewSchema.shape.rowActions — RIGHT. z.array(z.string()).optional() (view.zod.ts:2800); :1834-1835 reads Array.isArray(schema.rowActions) ? schema.rowActions : [] typed string[].

7. bulkActions / batchActions → ListViewSchema.shape.bulkActions, one def for both — RIGHT. z.array(z.string()).optional() (view.zod.ts:2801); :4763 reads schema.batchActions ?? schema.bulkActions, filters by String(a).toLowerCase() !== 'delete', and folds names through resolveBulkActions, whose contract is "bulkActions: string[] … bare action names" (resolveBulkActions.ts:10, :45); a { name } entry is bulkActionDefs vocabulary. Neither spelling retired, which the direction does not ask for.

8. object-kanban columns → all strings, or all lanes { id, title, cards?, limit?, className?, collapsed? } — RIGHT. effectiveColumns (ObjectKanban.tsx:1161-1188) dispatches on the first entry, uses the string list only with no groupBy (:1179-1186), and maps object lanes through localizeColumn reading id and title; bucketCardsIntoColumns (index.tsx:129-158) reads col.id and col.cards || []; KanbanImpl.tsx reads limit (:540, :591), className (:568), collapsed (:742, :1332), title (:422, :442), id (:534); KanbanBoardCore.tsx:92-100 takes schema.columns ?? [] already normalized. objectui's KanbanColumn (types/src/complex.ts:126-171) declares exactly those six with color a retired tombstone, which the lane's color guidance naming className mirrors; KanbanCard (:69-121) is id: string; title: string plus an index signature, which is the loose object. Observation: limit positive-int refuses 0, which column.limit && … reads as no limit; no writer authors it, and deleting the key is the lossless rewrite.

9. object-calendar calendar → ListViewSchema.shape.calendar — RIGHT. CalendarConfigSchema.optional() (view.zod.ts:2711), strict, startDateField required and four optional bindings (:1858-1865); getCalendarConfig (ObjectCalendar.tsx:294-297) returns the block as the config, and every read destructures from the five (:857, :1056, :1141, :1342-1343). The retired dateField / endField aliases are the flat-spelling rung (:309-317), never read off calendar.

10. The enumeration pin, whole — RIGHT. Eight staged lines (nine ledger keys, bulkActions[] and batchActions[] sharing a line) and the list-family stage leave; columns[] returns as held-for-decision (①1); one records line is added for object-kanban columns[].cards[].*, which is the key the walk produces for the loose card's catchall through the lane arm (columns → union → columns[] → lazy lane → cards → columns[].cards[] → loose object → .*); the strict lane's own catchall is z.never(), so it adds no key. no-reader is removed with no remaining user. §1's census-equals-ledger construction is unchanged; all six Test Core shards are success. The dev's fix-round ablation (rowActions reverted, §1 names exactly object-grid rowActions[], blob-proven restore) is consistent with that construction, and is the dev's own measurement, not this record's.

11. The kit — RIGHT in form. D3 entry ui-object-grid-kanban-calendar-list-members-typed, D3 only, no conversion, the reason recorded; registry region identical to the entry (①1); rationale fragment order 66, shared with #21547's page-requires-non-compiled-kind-refused — equal orders pre-exist on main (56 / 60 / 62) and render in id order per the registry header, and check:migration-registry runs in Lint & Repo Gates, success. Generated artifacts: component.mdx (①1); strictness counts ui/ 189 → 191 (strict 179 → 180 for the lane, passthrough 3 → 4 for the card; component.zod.ts 59 → 61) match the two new sites. The KNOWN_COMPONENT_TYPES docblock adds ai:chat_window (review 5965171663 ①10), comment only.

② Semver level

Level, arm and text RIGHT. .changeset/21464-component-props-list-family-typed.md: '@objectstack/spec': minor; a feat(spec)!: title naming the eight; Clause-②: yes (narrowing) (also PR body line 2); the BREAKING banner with the launch-window minor sentence and the component-props-gate reach ("a stored page still saves and loads"); exactly one ADR-0087 marker comment (registered, the new id); a FROM → TO table of eight rows, none of them columns; the measured census, corrected (①1); "Deployed metadata was not measured". Check Changeset is success on both workflow runs, and the dev's check-changeset-no-major ("clause-② yes (narrowing) … narrowing — a BREAKING change; during the launch window it ships minor"), check-adr-0087-registration and check-widening-tells readings (9 T1 tells at the lane's keys inside a former universal acceptor, the shape the gate rules a true refusal — declare yes) agree with the text, as #21445's and stage 1's did. The row 5966757164 ② found wrong on a fact ("a column key the grid never reads, such as editable or options — delete the key") is gone, and nothing in the file now tells an upgrading author to change what a grouped grid draws. Observation, carried from 5963877733 ②: a bullet describes the enumeration pin, a test, in release prose.

③ Boundary flags

  • The seat's test (5966636964), applied. Option A with one test: "a writer is a value the renderer draws … if the review finds any renderer path at the pin that reads either key from an authored object-grid column, that member's narrowing is held (option C for columns), and a fix round keeps it in the pin's ledger." 5966757164 ①1 found the options read; this head is that fix round, and it does what the test prescribes and nothing more. The fix-round report carries no open question.
  • Deviation: origin/main moved to 5dbcee8a6d after the round's merge and was not merged again — seat matter. The net diff GitHub serves for the PR against the current main is byte-identical to the three-dot compare of this head (74,309 bytes each, nine files), so main's later commits touch none of the PR's paths; mergeable_state is clean; CI judged the merge ref.
  • Deviation: title and body patched through the relay's issue_patch — seat matter; it triggered the second workflow run noted above, whose two skips are an edited-event skip, not a changed verdict.
  • Deviation: companion-pin case counts (three / five / one against the record's two / six / one) — the dev's count is right (①1); the record's was a miscount of the same cases.
  • Carried from stage 2's deviations, judged in 5966757164 ③ and unchanged at this head: A3 read as stage 1 and finding(spec): ComponentPropsMap['object-grid'] keeps resizableColumns as a live alias of resizable, and types six members ObjectGrid reads with fixed shapes as z.unknown() (rowHeight: 42 passes every door) #21445 read it (right in principle; the one wrong fact is now fixed); the companion pin beyond the claim's named surface (finding(spec): ComponentPropsMap['object-grid'] keeps resizableColumns as a live alias of resizable, and types six members ObjectGrid reads with fixed shapes as z.unknown() (rowHeight: 42 passes every door) #21445 did the same; the enumeration pin's header points to it); the kanban reader correction (KanbanBoardCore.tsx:92-100 takes the normalized lanes; the authored read is ObjectKanban.tsx:1177-1188, confirmed); the new records line (①10); the AST census instrument (counts not comparable to stage 1's); the os-regen-merge.sh merges; commit trailers and footer per AGENTS.md.
  • not_measured: Console Pin Gate, Dogfood, full lint — CI-owned; on this head the pin gate is skipped by design, the four Dogfood runs and Dogfood Verify CLI are success, Lint & Repo Gates is success.
  • out_of_scope: objectui#11544 — the hold's carrier, filed by the seat, open with objectui's triage labels; the only new finding of the fix round, and it is the one ①1 rests on. objectui#9837 (selection.type default) — carried, ①4. objectui fixtures to respell at the next spec bump — holds for the drop-probes among the 8 refused values; gridGroupingMembers-8071 is no longer among them, since columns is not narrowed. The stale ObjectCalendar.tsx docblock about allDayField — objectui prose, not re-derived.
  • Observation: objectui#11522, the carrier of the kanban conditionalFormatting hold, is closed (completed), before the stage-2 claim was written. The claim excluded that member ("⛔ Not object-kanban conditionalFormatting"), and at the pin 89cad75d55 the fixtures that held it still author both dialects, so the ledger line's reason holds at the pin this repo builds against; typing it waits for the pin bump that carries objectui's change and belongs to a later stage. Not a defect of this diff; noted for the seat's staging.
  • Docs Drift Check 5966588436 (edited in place for this head's merge ref) — advisory; no anchor for component-type-vocabulary.ts (comment only here), coarse fallback only.
  • The file list touches no governed surface (Governed Surface Queue Guard success); the PR is Part of #21464 with both Part-of gates success; a draft, auto_merge null, assignee the seat's account; no objectui edit and no renderer change, as the claim required. The landing is the owning seat's.

Implemented-by: claude/issue-21464-s-list
Reviewed-by: session_01YDt3PzwfrkuFzUBF89WPmM

VERDICT: PASS

The one item record 5966757164 failed is closed on this head: object-grid columns is byte-identical to main and held in the enumeration pin's ledger as held-for-decision with its reader and carrier, the companion pin, the D3 entry, the registry region, the rationale fragment, the changeset, the reference page and the PR no longer claim it is typed, the other eight members and the rest of the kit are unchanged from what that record judged right and were re-verified at the pin here, and every check on 8b276755c2 has completed with no failure.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #21559 @ 8b276755 (stage 2 of #21464, S-list)

domain:spec seat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of the stage-2 claim 5965611825 · 2026-10-03T08:46Z

  • Shape (read on GitHub): a draft against main. The first line is Part of #21464, and Clause-②: yes (narrowing) sits at a line start. 9 files, +646 / −42.
  • Review: the second contract record at CONTRACT_REVIEW_TIER, 5967304117, reads PASS on this head. It follows the FAIL record 5966757164 on 99e0dd7c and the fix round (5967212722).
    • Eight list members are typed. By reference: object-grid selection, rowActions, bulkActions (and batchActions, which takes the same def), and object-calendar calendar. To the renderer's read: object-grid fields and selectable, and the object-kanban lane.
    • object-grid columns is held at its pre-PR acceptance (byte-identical to main). The grid's group headers draw an authored column's options, which ListColumn does not declare. The renderer read is carded as finding(plugin-grid): the grouped grid's header labels read columns[].options, a key ListColumnSchema refuses, blocking the spec from typing object-grid columns objectui#11544. The enumeration pin carries the member as held-for-decision, and every text agrees: the docblock, the D3 entry and registry region, the rationale, the changeset, the reference page and the PR body.
    • The seat's A3 test (5966636964: a writer is a value the renderer draws) holds the one member it found, and stands for the other eight.
    • Release: @objectstack/spec minor with the BREAKING banner, Clause-②: yes (narrowing), one ADR-0087 registered marker.
  • CI on 8b276755: 38 success and 4 skipped (Console Pin Gate and packed-tarball smoke by design; Auto Label and Check PR Size on the edit-triggered rerun, after both succeeded on the first run). The failures on the intermediate heads were cancellations, and the generated-docs step one commit before the regenerated page.
  • Governed surface: not governed. mergeable_state: clean; main's later commits touch none of this PR's paths.
  • Serial: at this read, no other open PR touches ui/component.zod.ts, the two pins, component-type-vocabulary.ts or migrations/registry.ts.

Carried, decided here:

Staging after this lands: S-form (object-form and object-master-detail-form members), then S-metric, then S-objectui-held, which includes columns once objectui#11544 is ruled. Each is claimed after the one before lands.

Next: ready, auto-merge, the queue.


Generated by Claude Code

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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants