Repository navigation
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
Description
Activity
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p2·domain:spec·area:records·pm:queue;findingremoved. Type each member by reference, and retireresizableColumnsnowTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T17:54Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. It is class (c), measured at the public doors: six members a renderer reads with a fixed shape accept any value, and the grid silently drops or substitutes authored configuration. That includes row height, row colour, navigation and operations.
Routing.
packages/spec/src/ui/component.zod.ts(ObjectGridPropsSchema) isdomain:spec.Direction: the card's, accepted.
- Item 1: each member is typed by reference where the spec already exports the shape (
RowHeightSchema,RowColorConfigSchema,NavigationConfigSchema).- For
aggregations,conditionalFormattingandoperations, the claim measures the renderer's read and declares that shape. Where a list-view schema already declares the same member, it is used by reference. - ⛔ No
z.unknown()for a member a renderer reads with a fixed shape. bulkActionDefsis measured againstBulkActionDefSchema. If the renderer reads members that schema lacks, the claim reports the gap and does not widen it silently.
- For
- Item 2:
resizableColumnsretires now, as a tombstone namingresizable. There are zero writers in either repository, so there is no window. That follows objectui#6152's ruling A, and the startup rule of immediate retirement. - Both items narrow the row's accept set. The claim states the changeset level and its
Clause-②line.
objectui follows, once the release carrying item 2 is installable, as the card lists. objectui#6152 carries that step.
Pins:
- each measured member refuses an off-shape value at the spec parse;
- a value of the declared shape passes;
resizableColumnsis refused with the tombstone's words.
Generated by Claude Code
- Item 1: each member is typed by reference where the spec already exports the shape (
- addedarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 2, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01UtnxvdiN376GF3sgXwAw4d
Account:os-sales(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-21445-object-grid-typed-members
Worktree:objectstack-issue-21445
Domain:domain:spec
Seat:domain:spec#1(seat post #6017)
File surface: onorigin/main53fd35e3e3.packages/spec/src/ui/component.zod.ts,ObjectGridPropsSchema(about:3808-4110), per triage5958164933.- Item 1:
rowHeight,rowColorandnavigationtyped by reference (RowHeightSchema,RowColorConfigSchema,NavigationConfigSchemainui/view.zod.ts).aggregations,conditionalFormattingandoperationsdeclare the shape the renderer reads, by reference where a list-view schema already has the member.bulkActionDefsis measured againstBulkActionDefSchema; a gap is reported, not widened. - Item 2:
resizableColumnsretires as a tombstone namingresizable, with its ADR-0087 entry and generatedmigrations/registry.tsregion.
Plus the tests, the regenerated reference and API-surface artifacts, and one
@objectstack/specchangeset. ⛔ Read-only inview.zod.ts(reference its exports; no edit there). ⛔ No objectui change; objectui#6152 carries that step. Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(default build tier;packages/spec/src/**non-test, so the contract review runs atCONTRACT_REVIEW_TIERthrough an isolated subagent)
Clause-②: no (narrowing) per triage's card; the dev confirms the arm withcheck-widening-tellsand switches to yes (narrowing) only if an export-listing row is added
Thread-read: 5958164933
Serial constraints cleared: at 2026-10-02T18:21Z, no open PR touchespackages/spec/src/ui/component.zod.tsorview.zod.ts. #20234 (os-justin, in flight) sweeps comment-line tracker citations acrosspackages/spec/src; whichever lands second mergesmain.migrations/registry.tsis also written by in-flight #21000 (this seat) and #21320 / #21326 (other seats); each generated region merges throughscripts/pm/os-regen-merge.shby whichever lands later.- Item 1:
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21445,
"status": "needs_decision",
"branch": "claude/issue-21445-object-grid-typed-members",
"pr": "#21463",
"session": "session_01UtnxvdiN376GF3sgXwAw4d",
"premise_still_valid": true,
"summary": "Draft PR #21463 implements both items. Item 1: ObjectGridPropsSchema types its seven fixed-shape members. rowHeight, rowColor and navigation take RowHeightSchema, RowColorConfigSchema and NavigationConfigSchema by reference. conditionalFormatting takes ListViewSchema.shape.conditionalFormatting, the list view's own member. bulkActionDefs is z.array(BulkActionDefSchema). aggregations is [{ field, type }], with type = AggregationFunction, the query AST vocabulary, by reference. operations is a strict { create?, update?, delete?, export? }: the four booleans any ObjectGrid read point names at the .objectui-sha pin 89cad75d55. read and import are refused with guidance because nothing reads them. Item 2: resizableColumns is a retiredKey tombstone naming resizable. It has the D2 conversion object-grid-resizable-columns-removed (protocol 18, retiredAfter 17.6.0, order 57), which follows resizable ?? resizableColumns exactly, the RETIRED_KEYS_BY_MAJOR[18] row ui/ObjectGridProps:resizableColumns, and the D3 entries object-grid-resizable-columns-retired and ui-object-grid-row-members-typed, with two STEP18_RATIONALE fragments at orders 62 and 63. The status is needs_decision for ONE member: the census (Zone 2 #3) found a producer the new operations shape refuses. objectui's root README.md 'Data Grid' example, at the pin, writes operations: { create, read: true, update, delete, export }. The PR implements the recommended arm A, so applying arm B is a two-line change. Zone 2 assumption 1 held (the six z.unknown members, bulkActionDefs and resizableColumns at the cited lines on 53fd35e). Assumption 2 held (the reads at the pin; the card's ObjectGrid.tsx:5361 is exact). Assumption 4 held: the renderer's rowHeight set equals RowHeightSchema's five values, because resolveRowHeightMode tests membership against a table keyed by the spec's RowHeight and answers compact otherwise. bulkActionDefs gap vs BulkActionDefSchema, reported and not widened: the renderer also reads actionDef (renderer-attached, refused by the schema's guidance by design); param keys outside the declared set, spread onto the widget bag (refused by BULK_PARAM_WIDGET_CONFIG_KEYS under its recorded ruling); a visible envelope with dialect optional (objectui ExpressionWire; the spec envelope requires dialect); and params[].type typed string (the spec types it FieldType). Each gap already stands on the list view's door. Clause-② is measured as yes (narrowing), not the dispatch's no (narrowing): check-widening-tells --declaration no exits 4 with T1 tells on keys inside the former z.unknown bags, which is the shape the gate's own text rules a true refusal ('declare Clause-②: yes'). No export row is added (check:api-surface unchanged). The PR body's first two lines are 'Fixes #21445' and 'Clause-②: yes (narrowing)'. The worktree's node_modules and the worktree itself are removed after this report comment is written.",
"tests": "All at HEAD 52c4c42. pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: 'Test Files 602 passed (602) · Tests 17769 passed | 1 todo'. pnpm --filter @objectstack/spec typecheck: exit 0, 'check:test-typecheck: OK — 52 file(s) / 246 error(s) / 135 pinned signature(s)' (unchanged). The @ts-expect-error on ObjectGridProps.resizableColumns is consumed, so the tsc channel is proven. repo project: 45 of 50 files run green in four targeted runs ('9 passed · 221 tests', '18 passed · 274 tests', '18 passed · 224 tests', and the six-file rerun at the final head '6 passed · 77 tests'). NOT MEASURED, family repo-project script tests: build-schemas-check-mode, dist-freshness, dist-freshness-adoption, publish-smoke-boot-failure, publish-smoke-port-collision. Reason: the whole project overran the ~10-minute foreground cap (build-schemas-check-mode alone runs 7-18 s per case), and these files test build and publish machinery this diff does not touch; declared to CI. New pins: component-object-grid-typed-members.pin.test.ts (52 tests: shapes parse, off-shape refused with code AND path, by-reference identity, exact AggregationFunction set, exact operations key set, tombstone prescription, conversion arms, registration) and object-grid-resizable-columns-retirement.test.ts (tree-scoped absence pin in the already-declared @objectstack/spec radius, registered in vitest.repo-tests.json). Public door measured: validateComponentProps (the os validate / build / lint component-props gate) on a page with rowHeight: 42, resizableColumns: true and operations.read answers component-props-invalid at properties.rowHeight, component-props-invalid at properties.resizableColumns with the tombstone text, and component-props-unknown-key at properties.operations.read. Ablations run through scripts/ablation-replace.mjs; the pin reads src/ by relative import, so no build sits between mutation and run, and each was restored to blob == HEAD with git diff HEAD empty. (1) rowHeight back to z.unknown(): 3 failed / 49 passed. (2) tombstone key renamed away: 3 failed (all prescription pins). (3) operations back to z.unknown(): 6 failed. (4) a resizableColumns: false planted in examples/app-showcase/src/ui/pages/my-work.page.ts: the absence pin fails, naming that file. Its first attempt was refused by the tool because the replacement contained its own anchor; it wrote nothing (blob unchanged) and was re-run with a non-overlapping anchor. Direction: each red as predicted, no inversion. Lint, a declared narrowing: node --stack-size=4000 node_modules/eslint/bin/eslint.js --no-inline-config --format json on the changed .ts files gives 10 files, 0 errors, 0 warnings. Population: eslint.config.mjs's /*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED. Invariance: the config enables no type-aware linting (no parserOptions.project, no typed rules), so this diff cannot move a verdict on an untouched file. Full pnpm lint is CI's.",
"gates": "dispatch-gates --repo objectstack-ai/objectstack --commands derives 114 families at 52c4c42, identical to the seat's gates-21445.txt (empty diff). All 114 were run on 52c4c42, each exit captured before any pipe: 114 exit 0. dispatch-gates --ran with 'cmd :: exit N' records: '114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED (a DERIVED zero — all 114 recorded an exit code and none of them is 3)'. In the first pass, on an earlier head, check:docs-transcript-drift, check:dual-build-cjs-loads and check:lean-entry-closure answered exit 3 PREREQUISITE NOT MET; in the final pass they were measured and exit 0. In the first pass check:objectui-pin-citations went red on a pin citation wrapped across a string concatenation in the step-18 fragment; fixed and green. check:generated: green on the final head; --fix regenerated exactly the two artifacts it proved stale (content/docs/references/ui/component.mdx and docs/audits/2026-07-unknown-key-strictness-ledger.counts/ui.md). gen:schema printed the dropped-refinements corrected entry, applied as ui/ObjectGridProps +bulkActionDefs.element, with droppedRefinementSites 652 to 653. Changeset gates on HEAD: check-adr-0087-registration '[BREAKING+bang+clause-②-narrowing] registered object-grid-resizable-columns-removed, object-grid-resizable-columns-retired, ui-object-grid-row-members-typed (new here: all three)'; check-changeset-no-major exit 0; check-empty-changeset exit 0. check-widening-tells on the final diff: --declaration no exit 4 (8 tells), --declaration yes exit 0. PR CI: in_progress (14 of 32 check runs completed, 0 failed at one read; not awaited).",
"line_budget": "not applicable — no skills/ or governed ledger file touched",
"deviations": [
"Clause-② arm: the dispatch said 'no (narrowing)' unless an export-listing row is added, and none is. check-widening-tells refuses 'no' for this diff: its T1 tells fire on keys bounded inside former z.unknown() bags, and the gate's own text rules that shape a true refusal with the remedy 'declare Clause-②: yes'. Declared the measured 'yes (narrowing)' in the changeset and as the PR body's second line. Flagged here, not silently chosen.",
"operations narrowed to the four read members, so it refuses read and import. The census found one producer it refuses (objectui README.md at the pin), which per Zone 2 #3 makes this report needs_decision. The PR carries the recommended arm A, so it is ready if A is ruled and a two-line change if B is.",
"PR body first line is 'Fixes #21445' as dispatched. If the PM routes #21445 itself into the decision inbox rather than filing a separate decision record, a seat must rewrite that line to 'Part of #21445' (the dev writes the body once).",
"repo vitest project: 5 of 50 files NOT MEASURED locally (foreground cap); declared to CI.",
"merged origin/main once (086ad0a, service-analytics only, no generated artifact touched) with a plain git merge; no os-regen-merge was needed."
],
"files_changed": [
".changeset/21445-object-grid-typed-members.md",
"content/docs/references/ui/component.mdx",
"docs/audits/2026-07-unknown-key-strictness-ledger.counts/ui.md",
"packages/spec/authorable-surface/ui.json",
"packages/spec/dropped-refinements.baseline.json",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/migrations/entries/retired-keys/18.ui__ObjectGridProps__resizableColumns.ts",
"packages/spec/src/migrations/entries/semantic/18.object-grid-resizable-columns-retired.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-object-grid-row-members-typed.ts",
"packages/spec/src/migrations/registry.ts",
"packages/spec/src/ui/component-object-grid-typed-members.pin.test.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/object-grid-resizable-columns-retirement.test.ts",
"packages/spec/vitest.repo-tests.json"
],
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 — all through the fleet-write relay as objectstack-fleet[bot], each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, which became POST /repos/objectstack-ai/objectstack/pulls (PR #21463, draft; read back 14761 bytes sent, 14761 stored, identical); (2) label-write --assign os-sales, which became the assignees write on #21463 (read back: assignee os-sales; labels present were set by the labeler, not by this write); (3) this os-dev-report comment on #21445, through post-stamped.mjs. Not REST: 11 git pushes to the branch (an empty probe push, then WIP and merge commits).",
"open_questions": [
{
"question": "objectui's root README.md 'Data Grid' example (at the .objectui-sha pin 89cad75d55) authors an object-grid properties bag with operations: { create: true, read: true, update: true, delete: true, export: true }. The new operations shape refuses read (and import, which no measured bag writes). Should the spec row refuse read and import, or declare them?",
"options": [
"A (implemented in PR #21463): declare the four booleans a grid read point names (create, update, delete, export), with read and import refused by strictObject guidance that says no grid read point reads them. The README example gets the one-key correction in objectui#6152's follow-up. objectui's own content/docs/api/schema-reference.md already dropped operations.read from its example as a key 'which nothing reads' (objectui#11068). Cost: one objectui doc line; objectui validate on a copy of the README example warns at properties.operations.read until then.",
"B: declare read and import as optional booleans too, mirroring objectui's TypeScript twin and flat mirror. Cost: two published toggles nobody reads, and a future retirement kit to remove them.",
"C: leave operations as z.unknown(). This contradicts the ruled direction ('No z.unknown() for a member a renderer reads with a fixed shape') and is listed only for completeness."
],
"recommendation": "A. Business need, measured: ObjectGrid at the pin reads operations.create (:5468), .update and .delete (:1898-1899) and .export (:4088 / :5340 / :6141), and nothing reads .read or .import. The only writer of read is one stale README example; the census found zero writers in objectstack and one in the 76 objectui bags (control: columns in 46). Long-term soundness: declaring only what is enforced is the contract-first direction, and a member that toggles nothing is the ADR-0049 declared-but-unenforced state, which would later owe a retirement. Preventing AI authoring mistakes: A refuses at the door with a prescription naming why, so an AI that copies the README is told what to delete; under B an AI writing read: false believes it hid something and nothing happens. Startup focus: A adds no surface and needs no window. B publishes two inert members with no pull. The only cost of A is a one-line doc correction in objectui that objectui#6152 already carries a follow-up for. Four-axis framework verbatim from the dispatch (pm-dispatch SKILL.md at 53fd35e)."
}
],
"out_of_scope_findings": [
"class: a · reach: named producer: this PR's own diff, packages/spec/src/ui/component.zod.ts:34-36 at 52c4c42 (three names added to a multi-line import list from './view.zod') · evidence: node scripts/pm/check-widening-tells.mjs --declaration no --diff on the final diff reports 'T2 … a new member of a closed set (z.enum / union / an as const array)' on the lines '+ RowHeightSchema,', '+ RowColorConfigSchema,' and '+ ListViewSchema,'. Those are import specifiers, not enum or union members. A pure by-reference narrowing that adds only an import would be forced to declare Clause-② yes. It did not change this PR's verdict, because T1 forced yes anyway. · dedupe words: check-widening-tells T2 import specifier false positive · widening tells multi-line import list closed set · Clause-② tell import list",
"carrier: family closure card for ComponentPropsMap fixed-shape members typed z.unknown (same defect class as #21445; not refiled per the family rule) · noted, not filed. The same family remains on sibling rows at 52c4c42: object-kanban conditionalFormatting: z.unknown() (component.zod.ts:4839), and navigation: z.unknown() on object-map (:5671), object-gantt (:5884) and object-tree (:6209), while object-kanban, object-calendar, object-timeline and now object-grid take NavigationConfigSchema. No public-door measurement was taken for those rows in this run. · dedupe words: ComponentPropsMap navigation z.unknown object-map object-gantt object-tree · object-kanban conditionalFormatting z.unknown",
"carrier: objectui#6152 follow-up (objectui seat) · noted, not filed. Once a release carrying this is installable: the TypeScript resizableColumns member becomes never, the flat mirror gets a tombstone, the ?? schema.resizableColumns read at ObjectGrid.tsx:5361 is dropped, and the key leaves UnmirroredDeclared (the card's list). Plus what this run measured: the root README.md operations.read example; the TypeScript twin's and the flat mirror's operations.read / .import; the grid TypeScript conditionalFormatting union, whose two objectui-native rule spellings the spec row now refuses; and apps/console registry-inputs-spec-parity.test.ts:1227, which names object-grid.resizableColumns.",
"carrier: none (承接者:无) · noted in PR #21463 Acceptance notes only. The regenerated content/docs/references/ui/component.mdx renders the shared grouping.fields[] nested shape with order? / collapsed? (the input face) where main rendered them required. This is generator output from check:generated --fix, not a hand edit."
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsPM answer to the dev's
needs_decision(os-dev-report5960961904) ·domain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5958653004· 2026-10-02T20:32ZAnswer: A, as PR #21463 implements it.
ObjectGridPropsSchema.operationsdeclares the four booleans a grid read point names (create,update,delete,export).readandimportare refused with guidance.Why this is answered here and not escalated. It is not a product or contract-shape fork that existing direction leaves open.
- Triage's direction (
5958164933) already rules it: "foraggregations,conditionalFormattingandoperations, the claim measures the renderer's read and declares that shape". - The dev measured that read at
.objectui-shapin89cad75d55:ObjectGridreadsoperations.create,.update,.deleteand.export, and nothing reads.reador.import. - objectui's own
content/docs/api/schema-reference.mdalready droppedoperations.readfrom its example as a key "which nothing reads" (objectui#11068). - The one writer the census found is a stale prose example in objectui's root
README.md, not authored metadata. There are zero writers in this repository and none among the 76 objectui bags the dev scanned.
Under the four axes, the reading is long-term first: declaring only what is enforced is the contract-first shape, and an inert toggle is the ADR-0049 declared-but-unenforced state. The rest agree:
- Business need: there is no reader.
- Preventing AI authoring mistakes: a refusal with a prescription tells an agent that copies the README what to delete.
read: falsethat hides nothing would be the trap. - Startup focus: no inert surface is published, and no later retirement is owed.
So it is the triage ruling's own measurement, and needs no new decision.
Carried to objectui: the README's
operations.readline goes with objectui#6152's follow-up, recorded there in the same pass.Also from the report:
Clause-②: yes (narrowing)as measured is accepted for review. The T1 tells fire on keys inside the formerz.unknown()bags, which the gate rules a true refusal.- The PR keeps
Fixes #21445: this card is not routed to the decision inbox. - The at-tier review is dispatched on
52c4c42d72.
- Triage's direction (
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21445,
"status": "done",
"branch": "claude/issue-21445-object-grid-typed-members",
"pr": "#21463",
"session": "session_01UtnxvdiN376GF3sgXwAw4d",
"premise_still_valid": true,
"round": "regen-merge round (coordinator), after the at-tier PASS 5961282483 on 52c4c42 and the seat's ruling A (5960993628), which PR #21463 already implements",
"summary": "Merged origin/main 713b0fa into the branch through scripts/pm/os-regen-merge.sh (steps 1-3). It made one merge commit, 38159d1, and pushed it once. New head: 38159d1. Commits since 52c4c42 (first-parent): 38159d1 'Merge remote-tracking branch origin/main into claude/issue-21445-object-grid-typed-members'; there is no regeneration commit. The step-4 regeneration reproduced the merged registry.ts byte-identically. The merge is pure. The merged tree differs from origin/main in exactly the branch's own 14 paths (git diff --name-only HEAD^2 HEAD equals git diff --name-only 086ad0a 52c4c42). The only path both sides touched is packages/spec/src/migrations/registry.ts, which auto-merged as text; os-regen-pending was absent. Its merged-vs-main delta equals the branch-vs-base delta line for line. Step 2 kept the branch's bytes of its three generated paths main did not move (component.mdx, the strictness-ledger counts ui.md, authorable-surface/ui.json). check:generated then measured all 15 artifacts up to date. One correction to the round's premise: main's registry.ts change was not only the generated region. Of the three hunks main brought (086ad0a to 713b0fa), two are in the generated semantic:18 region. The third is a hand-maintained STEP18_RATIONALE fragment, cube-metric-expression-types-retired at order 62, from 99589f9 (#21452). It sits in a different gap of the id-sorted list, so it merged with no conflict, and the merged list stays sorted by id. Sibling entries survive: cube-metric-expression-types-retired appears 2 times in the merged registry.ts and 2 times on main; ui-html-page-div-refused 1 and 1. Both of those entry files are byte-equal to main. No entry was renumbered.",
"registry_byte_identity": "Before regeneration, the merged packages/spec/src/migrations/registry.ts was blob e0f36abbb5dcef1ba5792090e892f9d63dea5590 (equal to HEAD:registry.ts). pnpm --filter @objectstack/spec gen:migration-registry exited 0 and printed '✓ wrote src/migrations/registry.ts (351 semantic, 245 retired-key, 212 retired-def)'. After, the blob is e0f36abbb5dcef1ba5792090e892f9d63dea5590: BYTE-IDENTICAL, and git status is empty. So the merge commit is the whole push.",
"entry_counts": "D3 semantic entries: branch 52c4c42 350 / main 713b0fa 349 (merge base 086ad0a: 348) / merged 351, the generator's own count. Retired-key entries: 245 / 244 / 244 / 245. Retired-def entries: 212 merged, unchanged. MAJOR_18_CONVERSIONS rows: 58 / 57 / 57 / 58 (main added no conversion). STEP18_RATIONALE fragments: 65 / 64 / 63 / 66.",
"order_collisions": "Rationale (STEP18_RATIONALE), on the merged head: order 62 is cube-metric-expression-types-retired (main, #21452) plus object-grid-resizable-columns-retired (this PR); this one is NEW with the merge, and the ties break by id, cube- first. Order 56 is form-field-public-picker-retired plus ui-object-master-detail-form-details-closed (pre-existing on main). Order 60 is agent-structured-output-refused-members-retired plus translation-widget-sub-caption-retired (pre-existing). This PR's ui-object-grid-row-members-typed at 63 collides with nothing. Conversions (MAJOR_18_CONVERSIONS): order 55 is agentStructuredOutputRefusedMembersRemoved plus translationWidgetSubCaptionRemoved (pre-existing on main). This PR's objectGridResizableColumnsRemoved at 57 collides with nothing. No gate refused a duplicate: check:migration-registry, check:generated, scripts/step18-rationale-merge.test.ts and scripts/conversions-major18-merge.test.ts are all green. Not renumbered.",
"gates": "All on the merged head 38159d1, exit captured before any pipe. pnpm --filter @objectstack/spec build: exit 0. pnpm --filter @objectstack/spec check:generated: exit 0, '✓ All 15 generated artifacts are up to date'. pnpm --filter @objectstack/spec check:migration-registry: exit 0, 'build-migration-registry --self-test: ok · ✓ src/migrations/registry.ts is current (351 semantic, 245 retired-key, 212 retired-def)'. node scripts/check-adr-0087-registration.mjs --base origin/main: exit 0, '.changeset/21445-object-grid-typed-members.md [BREAKING+bang+clause-②-narrowing] registered object-grid-resizable-columns-removed, object-grid-resizable-columns-retired, ui-object-grid-row-members-typed (new here: all three)'. node scripts/check-changeset-no-major.mjs --base origin/main: exit 0, '✓ This diff introduces no major bump'. pnpm check:nul-bytes: exit 0, 'OK (scanned 9809 text file(s) … no raw ASCII control bytes)'. Spec vitest: --project local over component-object-grid-typed-members.pin.test.ts plus src/migrations/migrations.test.ts plus src/conversions/conversions.test.ts gave 'Test Files 3 passed (3) · Tests 458 passed'. --project repo over object-grid-resizable-columns-retirement.test.ts plus scripts/step18-rationale-merge.test.ts plus scripts/conversions-major18-merge.test.ts gave 'Test Files 3 passed (3) · Tests 23 passed'.",
"deviations": [
"The round's brief says #21451 and #21452 touched only the generated region of registry.ts. Measured: #21452 also added a hand-maintained STEP18_RATIONALE fragment, outside the generated markers. It merged cleanly into a different gap and is reported, not changed."
],
"files_changed": [],
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "1 this round — this os-dev-report comment on #21445, through post-stamped.mjs and the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, which became POST /repos//issues/21445/comments). Not REST: 1 git push (the merge commit 38159d1).",
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsACCEPT — PR #21463 @
38159d1362domain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5958653004· 2026-10-02T21:22Z-
Shape (read on GitHub): a draft against
main. The first line isFixes #21445, thenClause-②: yes (narrowing). PR assigneeos-sales. The branch's delta againstmainis 14 files, +1167 / -28:ObjectGridPropsSchemainpackages/spec/src/ui/component.zod.ts, with seven members typed;- the
resizableColumnsretirement kit: the tombstone, D2 conversionobject-grid-resizable-columns-removed, theRETIRED_KEYS_BY_MAJOR[18]row, D3 entriesobject-grid-resizable-columns-retired/ui-object-grid-row-members-typed, twoSTEP18_RATIONALEfragments, and the generatedmigrations/registry.tsregion; - the two pin tests;
- the regenerated reference page, authorable surface, strictness counts and dropped-refinements baseline;
- one
@objectstack/specchangeset.
No governed path.
-
The dev's
needs_decision(operations.read/.import): answered by this seat as A (5960993628), under triage's own direction, and not escalated. The PR implements A. -
Review: at-tier PASS
5961282483on52c4c42d72, carried to this head over one pure-regeneration hop (Regen-provenance:5961435829). The hop is a merge ofmain713b0fa76d, andregistry.tsis reproduced byte for byte. The record finds:- each of the seven members matches
ObjectGrid's read at.objectui-shapin89cad75d55, and no honoured value is refused except the objectui-nativeconditionalFormattingdialect, which the list view already refuses; - the
bulkActionDefsgaps stand identically on the list view's door; - the retirement kit is complete and the D2 conversion is right on every arm, matching the renderer's
resizable ?? resizableColumns; Clause-②: yes (narrowing)is the right arm: members bounded inside a formerz.unknown()bag, which the gate's own text rules a true refusal;- every changeset sentence is right.
- each of the seven members matches
-
Gates on this head: 35 check-runs: 33
success, 2 skipped, none failed and none pending.check-expected-skips: OK.check-governed-merges --pr 21463: NOT governed, 1195 changed lines.mergeable_state: clean. A localgit merge-treeagainstorigin/main4c8363f421merges without conflict, and nomaincommit since the merge base touches the 14 files.- Serial: open PR feat(spec)!: retire agent.lifecycle — a conversation phase is a skill, orchestration is Flow, record transitions are the state_machine rule; the XState StateMachineSchema family leaves with it (#21320) #21461 (spec(ai): retire
agent.lifecycle— phase-scoped instructions and tools are skills withtriggerConditions, process orchestration is Flow (ruled on cloud#2569) #21320, another seat) also writesmigrations/registry.ts,conversions/registry.tsandvitest.repo-tests.json. Whichever lands later mergesmainthroughscripts/pm/os-regen-merge.sh.
- Serial: open PR feat(spec)!: retire agent.lifecycle — a conversation phase is a skill, orchestration is Flow, record transitions are the state_machine rule; the XState StateMachineSchema family leaves with it (#21320) #21461 (spec(ai): retire
-
Out-of-scope findings:
- filed finding(pm):
check-widening-tellsT2 reads identifiers in a multi-line import list as "a new member of a closed set" — a by-reference narrowing that only adds imports is forced to declareClause-②: yes#21465:check-widening-tellsT2 fires on import specifiers. - filed spec(ui): the
ComponentPropsMaprows still type renderer-read members asz.unknown()—navigationon object-map / object-gantt / object-tree andconditionalFormattingon object-kanban accept42— the family close-out after #21445 #21464: the family close-out for the sibling rows' renderer-readz.unknown()members, with the reach measured on published 17.6.0. - filed spec(docs-gen): the reference generator renders one shared def with two faces on one page — ObjectGridProps shows
order?/method?where sibling rows show them required, by evaluation order #21466: the reference generator renders one shared def with two faces on one page, depending on evaluation order. - carried on 121 declared-but-unmirrored keys across 16 schema pairs — the lane #6058's new UnmirroredDeclared ledger made visible objectui#6152 (
5961014236), for the follow-up that wakes on the release carrying this PR:- the README
operations.readline; - the twin's
read/import,visible.dialectandparams[].type; ObjectView'soperationsrelay;- the native
conditionalFormattingdialect in grid tests; - the console parity test;
- the registry description that still names
resizableColumns.
- the README
- filed finding(pm):
Landing: ready, then auto-merge through the merge queue.
-
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsLanded: PR #21463 →
aa4632235bdomain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5958653004· 2026-10-02T21:51Z- Landed: PR feat(spec)!: an object-grid page block types the seven members the grid reads, and resizableColumns retires for resizable (#21445) #21463 went through the merge queue as
aa4632235b, with one parent (49524f6906). All 14 files are blob-equal to the accepted head38159d1362on the merge commit, the generatedmigrations/registry.tsandconversions/registry.tsincluded. Nomaincommit between the head's merge base713b0fa76dand that parent touches any of the 14 files. - Card: closed
completedwhen the PR merged, by itsFixesline. This act removespm:dispatchedand the assignee. - Review: at-tier PASS
5961282483, carried to the head byRegen-provenance:5961435829; ACCEPT5961693629. - What changes: an
object-gridpage block types the seven members the grid reads:rowHeight,rowColor,navigation,conditionalFormatting,bulkActionDefs,aggregationsandoperations(strict{ create, update, delete, export }). They werez.unknown()before.resizableColumnsis retired as a tombstone namingresizable; D2 conversionobject-grid-resizable-columns-removedrewrites it, and the ADR-0087 entries areobject-grid-resizable-columns-retiredandui-object-grid-row-members-typed. - Not yet installable downstream:
@objectstack/spec@17.6.0(npmlatest) predates this merge. The objectui follow-up carried on 121 declared-but-unmirrored keys across 16 schema pairs — the lane #6058's new UnmirroredDeclared ledger made visible objectui#6152 (5961014236) wakes on the release that carriesaa4632235b. - Serial lines this landing clears:
- spec(ui): the
ComponentPropsMaprows still type renderer-read members asz.unknown()—navigationon object-map / object-gantt / object-tree andconditionalFormattingon object-kanban accept42— the family close-out after #21445 #21464 (theComponentPropsMapz.unknown()close-out) was graded serial after this PR, in the same file. It stays inpm:queue, unclaimed: this seat stands down after its current work, on the maintainer's instruction, and the nextdomain:specseat takes it. - Open PR feat(spec)!: retire agent.lifecycle — a conversation phase is a skill, orchestration is Flow, record transitions are the state_machine rule; the XState StateMachineSchema family leaves with it (#21320) #21461 (spec(ai): retire
agent.lifecycle— phase-scoped instructions and tools are skills withtriggerConditions, process orchestration is Flow (ruled on cloud#2569) #21320, another seat) also writesmigrations/registry.ts,conversions/registry.tsandvitest.repo-tests.json; it mergesmainthroughscripts/pm/os-regen-merge.shbefore it lands.
- spec(ui): the
- Unlock scan: no open
pm:blockedcard namesBlocked-by: #21445(or PR feat(spec)!: an object-grid page block types the seven members the grid reads, and resizableColumns retires for resizable (#21445) #21463) in its body or comments. All 25pm:blockedcards here and all 9 in objectstack-ai/objectui were read.
- Landed: PR feat(spec)!: an object-grid page block types the seven members the grid reads, and resizableColumns retires for resizable (#21445) #21463 went through the merge queue as
- added 5 commits that reference this issue
on Oct 7, 2026
Filing-gate category: ① a contract gap with named sites, class (c).
reach:a public door, measured. On@objectstack/spec17.5.0, objectui'ssafeValidateSchemaandStrictAnyComponentSchemaboth answer success for{ type: 'object-grid', properties: { objectName: 'acct', KEY: 42 } }, for each of the seven members the row then typedz.unknown().os validate's row does the same.ObjectGridthen drops the value or falls back silently:resolveRowHeightModeanswers'compact'for an off-presetrowHeight, andschema.grouping?.fieldsreads nothing from42.5957803718,out_of_scope_findings[0], the H2 face probe), PR objectui#11492.domain:spec @ objectuiseat (objectui#10217,session_01XvhGmGAP79ZB8swnkapxPC) as the spec half objectui#6152 owes this repository. ⛔ Not graded here.The sites (objectstack
main535d1d25, seat re-read)packages/spec/src/ui/component.zod.ts,ObjectGridPropsSchema(astrictObject,:3808), registered asComponentPropsMap['object-grid'](:6574).1. Six members a renderer reads with a fixed shape, typed
z.unknown():rowHeight:4038RowHeightSchema(ui/view.zod.ts:1158)aggregations:4050conditionalFormatting:4051rowColor:4052RowColorConfigSchema(ui/view.zod.ts:1474)navigation:4059NavigationConfigSchema(ui/view.zod.ts:2287)operations:4102groupingwas the seventh at 17.5.0. It is typed byGroupingConfigSchemaonmainnow (objectstack#20856).bulkActionDefs(:4058) isz.array(z.unknown()). objectui measuredBulkActionDefSchemaby reference as narrower than objectui's own TypeScript twin (params,visible), so it is listed here for the claim to measure, not ruled.2.
resizableColumns(:4077), described as "Alternate spelling ofresizable(the renderer readsresizable ?? resizableColumns)".resizableis canonical, and the spec half forks here. That is the seat answer A on objectui#6152 (5894479982item 3, 2026-09-29), named in the director's review5904218639③.resizableColumnsin either repository, at objectstackmain3a6d92f7and objectuimain, and no in-code producer writes it.schema.resizable ?? schema.resizableColumns(packages/plugin-grid/src/ObjectGrid.tsx:5361), and keeps reading it until this row retires it, so that no public door accepts a key nothing reads. That is the seat's answer on objectui#6152 today.Direction (for triage and the spec seat, ⛔ not a ruling)
Item 1: type each member by reference where the spec already has the shape. For the rest, declare the shape the renderer reads. ⛔ No
z.unknown()for a member a renderer reads with a fixed shape.Item 2: retire
resizableColumnson the row as a tombstone namingresizable, immediately. There are no writers, so there is no window.objectui follows once the release carrying item 2 is installable:
?: never;?? schema.resizableColumnsread is dropped;UnmirroredDeclaredledger.objectui#6152 carries that step.
Both items narrow the row's accept set. The claim states the changeset level.
Dedupe
The 1,000 most recently updated objectstack issues and PRs, open and closed, back to 2026-09-29T02:45Z (REST, grepped locally 2026-10-02):
resizableColumns: 0 hits.ObjectGridPropsSchema: 3, all closed and on other members (spec:ComponentPropsMap['object-grid'].exportOptionsisz.unknown(), so a bareexportOptions: ['csv']passes every door and objectui's grid silently exports csv/json instead #21229exportOptions, finding(spec):ComponentPropsMap['object-grid'](ObjectGridPropsSchema) declares neitherdescriptionnoremptyState, which objectui'sObjectGridreads once objectui#11130 lands #20694description/emptyState, feat(spec): object-grid declares description, emptyState (the list view's own shape) and keyboardNavigation #20882).rowHeight|rowColor|conditionalFormatting: 2, closed and unrelated (fix(metadata-protocol,spec)!: a saved view stores the parsed value of every key its body carried, and a ViewItem record's top-level options is refused by name (stage iv of #20051) #20868, docs(spec): re-anchor the dead tracker citations left in data/ to the commits that decided them (stage 4) #20548).object-grid: 16.Dedupe words:
object-grid row z.unknown rowHeight rowColor navigation·ObjectGridPropsSchema resizableColumns alternate spelling retire·ComponentPropsMap object-grid typed membersGenerated by Claude Code