You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(components): the static table renderer is a fourth site of the undeclared column-alias family, and its published docs declare name/label as TableColumn while packages/types declares header/accessorKey #5350
Found while running #5120's census (not claiming; observation class). Baseline objectui main @ 9c60144.
The alias
packages/components/src/renderers/complex/table.tsx:43 resolves a cell as:
{row[col.accessorKey || col.name]}
That is the same undeclared name alias #5120 rules on for data-table.tsx:777/:786 — a fourth site of the family (data-table, ObjectDataTable, ObjectGrid pre-#5068, and this). #5120 does not name it, so it is deliberately untouched by that card's PR.
Why it is not a mechanical follow-on
The census that gated #5120 turned up something specific to this renderer: the published docs declare the name spelling as the contract for type: 'table'.
content/docs/core/report-schema.mdx:327 authors the same spelling on a table node ({ name: 'region', label: 'Region' }, plus aggregation).
But TableColumn in packages/types/src/data-display.ts:218-226 declares header: string and accessorKey: string, both required, and neither name nor label. So the docs and the type disagree about a type they both call TableColumn — the docs describe the runtime's alias set, the type describes the declared set, and each is internally consistent.
Retiring the alias here would therefore break documented, authorized usage; declaring name on TableColumn is the opposite direction and is explicitly not ruled (#5120). Which of the two spellings TableSchema actually owns is a maintainer call, not a mechanical fix — which is why this is filed rather than folded into #5120's PR.
Recheck
sed -n '25,50p' packages/components/src/renderers/complex/table.tsx
sed -n '392,420p' content/docs/api/schema-reference.md
sed -n '218,227p' packages/types/src/data-display.ts
Grading
finding, not pm:queue. Related: #5120 (the data-table half), #5068, #3951.
Triage first-touch grading (finding centralized round, 2026-08-20, triage seat session session_016A4EBi3ky1mjTi6vD2kCvA): finding → pm:blocked — the decision box for this exact alias family already exists at #5120; a second box would split one ruling. Re-verified on origin/main: table.tsx:33/:43 still reads the undeclared aliases, and the published schema-reference.md §TableSchema still contradicts packages/types/src/data-display.ts:222-226 about which spelling TableColumn owns.
Triage unlock: the family ruling landed on #5120 (2026-08-20 — consumer-side aliases retired, producers resolve identity before delivery, declared header/accessorKey contract wins). For this card that also settles the docs-vs-types contradiction: packages/types' declared contract stands; schema-reference.md §TableSchema corrects to match. Returned to the queue; coordinate/fold with the #5120 implementation (same adapter file — the dispatching seat answers fold-or-serial).
Claim: PM loop round 21
Session: session_01RV6yuVCxymHYE16PL9vQkE
Branch: claude/issue-5350-table-alias-fourth-site
Worktree: objectui-issue-5350
Domain: repo:objectui
File surface: packages/components/src/renderers/complex/table.tsx (:33, :43) + content/docs/api/schema-reference.md §TableSchema (stop on breach; explain in the report)
Container & model: M, mode:subagent
Clause-②: yes — retires the undeclared col.name tolerance on a fourth site; covered by the family ruling recorded on #5120.
Serial constraints cleared — this card was blocked and is now unblocked:
{
"issue": 5350,
"status": "done",
"branch": "claude/issue-5350-table-alias-fourth-site",
"pr": "https://github.com/objectstack-ai/objectui/pull/5475",
"premise_still_valid": true,
"summary": "Both halves landed in one PR, per the family ruling recorded on #5120 (2026-08-20, maintainer verbatim 「其他接受你的建议。」). CODE: renderers/complex/table.tsx retires both undeclared aliases — the header now resolves as col.header (was col.header || col.label, :33) and the cell as row[col.accessorKey] (was row[col.accessorKey || col.name], :43). DOCS: schema-reference.md TableSchema now authors accessorKey/header in its copyable example and its property row states the declared contract, resolving the docs-vs-types contradiction the ruling settled in favour of packages/types. Both columns.map callbacks are typed TableColumn instead of any, so reintroducing an alias here is a type error rather than a reviewer's catch. PREMISE REFINEMENT, not a refutation: the card's two load-bearing claims both held and were re-verified on origin/main, but one supporting claim in the issue body did not — content/docs/core/report-schema.mdx:327 is cited there as authoring 'the same spelling on a table node', and it is not the same defect. That node is a report SECTION rendered by ReportViewer.tsx:345's own markup against ReportField, and packages/types/src/reports.ts:57-66 DECLARES name and label on ReportField. It is correctly spelled for its own contract, so it was excluded rather than migrated. Same for schema-reference.md:1131 (DetailViewSchema related[], forwarded to a RelatedList by DetailView.tsx:1526-1533, never the component-registry table key). SCOPE FENCES HELD: data-table.tsx / ObjectDataTable.tsx / ObjectGrid.tsx and the object-grid doc examples untouched (#5120+#5351 in flight). CRUD DECISION, as instructed: checked #5373 before deciding and left the type:'crud' examples alone — its maintainer ruling (2026-08-20) retires CRUDSchema under ADR-0049 and rewrites that section around shapes that render, so it is scheduled for DELETION rather than re-spelling, crud has no renderer at all, and re-spelling it would be work that gets deleted plus a collision in the one file both cards edit.",
"tests": "All at final commit 455bbec9c, the tree the PR ships; the union below was run after that commit and the PR body cites the same sha. NEW: packages/components/src/renderers/complex/__tests__/table-column-contract.test.tsx, 5 tests, verbose run shows all 5 green. PACKAGE SUITE from the repo root (a package-scoped run uses a different config than CI): 'pnpm exec vitest run packages/components --maxWorkers=2' -> 'Test Files 172 passed (172) / Tests 1565 passed (1565)'. PINNING: the pre-existing shadowed-renderer-behaviour.test.tsx authors columns the declared way and stays green (11 passed across both files in a verbose confirmation run) — behaviour the retirement must not move. TYPECHECK: 'pnpm --filter @object-ui/components type-check' -> exit 0; output echoes 'tsc --noEmit && tsc -p tsconfig.test.json', so this is a real pass, not a zero-match --filter exiting 0. BUILD: 'pnpm build --concurrency=2' -> 'Tasks: 43 successful, 43 total' (precondition for the docs gate, which hard-fails on an unbuilt workspace). GATES derived from the diff (scripts/pm/dispatch-gates.mjs is objectstack-only): check-control-bytes OK (4526 tracked files), check-doc-component-types OK, check-doc-snippet-types OK ('Semantic phase: 88 of 88 block(s) judged, 0 failed'), check-doc-links OK, check-changeset-presence OK, check-changeset-no-major OK, check-changeset-fixed OK. Plus my own control-byte grep over the changed files and the PR body: clean. PER-SHAPE MATRIX, measured by rendering through SchemaRenderer and reading back actual thead/tbody text on BOTH legs (row = {name:'Ada', role:'Engineer'}), BEFORE -> AFTER: {accessorKey} headers ''/'' cells 'Ada'/'Ada' (unmoved); {name} headers ''/'' cells 'Ada'/'' (CELLS moved); {header,accessorKey} 'Name'/'Name' + 'Ada'/'Ada' (unmoved); {label,accessorKey} headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved); {name,label} (the retired docs spelling) headers 'Name'/'' cells 'Ada'/'' (BOTH moved); {label,accessorKey} (the skills-guide spelling) headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved). The only two shapes that move nothing are the two spelled the declared way. LEGIBILITY, measured not assumed: an unresolvable column is NOT dropped and does NOT throw — it keeps its slot, neighbours unaffected, header or cells simply empty — and unlike data-table this renderer keys cells by INDEX rather than by accessor, so it does not even emit React's generic missing-key warning. It is FULLY SILENT. Pinned as behaviour, no diagnostic implemented; that is #5349's question. REVERSE VERIFICATION with direction predicted before the run: no rebuild was needed or done on either leg, and that was checked rather than assumed — vitest.config.mts:264 aliases @object-ui/components to packages/components/src, so both legs transform source with no dist/ in the path. Ablating the fix ('git checkout origin/main -- table.tsx', tests kept) was PREDICTED to flip name-only cells back to 'Ada', label-bearing headers back to 'Name', and to turn exactly 3 of the 5 new tests red (the three alias assertions), leaving the declared-spelling test and the legibility test green because the latter asserts only slot counts and its neighbour column. OBSERVED: 'Tests 3 failed | 3 passed (6)' (6 = 5 contract tests + the probe), failures being exactly 'does NOT resolve a cell through the retired name alias', 'does NOT resolve a header through the retired label alias', 'renders nothing for a fully alias-spelled column', and every matrix shape flipped as predicted. PREDICTED AND OBSERVED MATCH EXACTLY. Tree restored with 'git checkout HEAD -- table.tsx' and proven clean: git status --short empty, git diff --stat HEAD empty, both lines confirmed back to post-fix bytes. CENSUS (two independent methods over 4174 files across content/docs, skills, examples, apps, e2e, packages, incl. package READMEs): method 1 located all 70 type:'table' occurrences repo-wide and read each one's columns; method 2 bracket-matched all 194 columns arrays in the corpus and classified by spelling and nearest enclosing type. Both agree: exactly FOUR columns arrays sit on a type:'table' node carrying name/label — schema-reference.md:402 (the defect, fixed), skills/objectui/guides/schema-expressions.md:500 (real blast radius, reported not migrated, filed #5473), and the two exclusions above. COUNTER-PROBE: the same method-2 pass found 39 column arrays spelled the declared header/accessorKey way, SIX of them on type:'table' nodes (apps/site/app/playground/page.tsx:751, examples/schema-catalog/.../basic-table.json:3, packages/plugin-dashboard/README.md:496, packages/types/examples/data-display-examples.json:70, StaticTableWidget.test.tsx:131, and this renderer's own defaultProps), so the method demonstrably finds table-enclosed columns arrays and reads their spelling correctly, and the alias bucket was non-empty in the same run. e2e/ holds no type:'table' node at all; the schema catalog holds exactly one, already declared-spelled.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #5473: BLAST RADIUS of this PR — skills/objectui/guides/schema-expressions.md depends on both retired aliases. Its prose at L493 documents the accessor fallback as contract ('accessorKey, falling back to name'), which this change makes false, and its worked example at L500 authors { label: 'Name', accessorKey: 'name' } on a type:'table' node, which renders an empty heading once this lands (measured: headers '' cells 'Ada'). Reported and filed rather than silently migrated — skills/** is a governed, customer-published surface outside this card's declared file surface. Sequencing (before/with/after this PR) is triage's call; the data-table hits in the same file at L410 and data-integration.md:212 belong to #5120/#5351, not this.",
"filed as #5474: the static table renderer reads only 9 of the 20 keys TableSchema/TableColumn declare — derived from the file, not recalled. hoverable, striped, minWidth, align, fixed, type, sortable, filterable, resizable, editable and cell are all inert, and schema-reference.md documents hoverable/striped as working while the example sets both true. Same declared-but-unenforced class as #5373, one level down. NOT fixed here because the routes (implement / retire under ADR-0049 / split the type) are a contract decision, and TableColumn is shared with DataTableSchema so a retirement must be checked against those consumers first. This is also why the corrected example keeps hoverable/striped: deleting them would have pre-empted that decision.",
"NOT filed, overlaps existing open #4787: LegacyReportRenderer.tsx:67 falls back to ComponentRegistry.get('table') when 'grid' is absent, but hands it a grid-shaped schema ({ type:'grid', rowData, columnDefs }) with no columns and no data. Searched before filing and found 'grid' IS registered — as the LAYOUT grid at packages/components/src/renderers/layout/grid.tsx:50 — so the table fallback is effectively dead and the layout grid receives rowData/columnDefs instead, which is exactly the class #4787 already tracks. Unaffected by this change either way (the alias was never reached, columns being undefined)."
]
}
(Supersedes the previous comment — identical payload. That one opened with the HTML-comment marker, which GitHub's sanitizer stripped on write, leaving it invisible to a marker scan. Reposted with the literal-text marker per the fallback protocol.)
{
"issue": 5350,
"status": "done",
"branch": "claude/issue-5350-table-alias-fourth-site",
"pr": "https://github.com/objectstack-ai/objectui/pull/5475",
"premise_still_valid": true,
"summary": "Both halves landed in one PR, per the family ruling recorded on #5120 (2026-08-20, maintainer verbatim 「其他接受你的建议。」). CODE: renderers/complex/table.tsx retires both undeclared aliases — the header now resolves as col.header (was col.header || col.label, :33) and the cell as row[col.accessorKey] (was row[col.accessorKey || col.name], :43). DOCS: schema-reference.md TableSchema now authors accessorKey/header in its copyable example and its property row states the declared contract, resolving the docs-vs-types contradiction the ruling settled in favour of packages/types. Both columns.map callbacks are typed TableColumn instead of any, so reintroducing an alias here is a type error rather than a reviewer's catch. PREMISE REFINEMENT, not a refutation: the card's two load-bearing claims both held and were re-verified on origin/main, but one supporting claim in the issue body did not — content/docs/core/report-schema.mdx:327 is cited there as authoring 'the same spelling on a table node', and it is not the same defect. That node is a report SECTION rendered by ReportViewer.tsx:345's own markup against ReportField, and packages/types/src/reports.ts:57-66 DECLARES name and label on ReportField. It is correctly spelled for its own contract, so it was excluded rather than migrated. Same for schema-reference.md:1131 (DetailViewSchema related[], forwarded to a RelatedList by DetailView.tsx:1526-1533, never the component-registry table key). SCOPE FENCES HELD: data-table.tsx / ObjectDataTable.tsx / ObjectGrid.tsx and the object-grid doc examples untouched (#5120+#5351 in flight). CRUD DECISION, as instructed: checked #5373 before deciding and left the type:'crud' examples alone — its maintainer ruling (2026-08-20) retires CRUDSchema under ADR-0049 and rewrites that section around shapes that render, so it is scheduled for DELETION rather than re-spelling, crud has no renderer at all, and re-spelling it would be work that gets deleted plus a collision in the one file both cards edit.",
"tests": "All at final commit 455bbec9c, the tree the PR ships; the union below was run after that commit and the PR body cites the same sha. NEW: packages/components/src/renderers/complex/__tests__/table-column-contract.test.tsx, 5 tests, verbose run shows all 5 green. PACKAGE SUITE from the repo root (a package-scoped run uses a different config than CI): 'pnpm exec vitest run packages/components --maxWorkers=2' -> 'Test Files 172 passed (172) / Tests 1565 passed (1565)'. PINNING: the pre-existing shadowed-renderer-behaviour.test.tsx authors columns the declared way and stays green (11 passed across both files in a verbose confirmation run) — behaviour the retirement must not move. TYPECHECK: 'pnpm --filter @object-ui/components type-check' -> exit 0; output echoes 'tsc --noEmit && tsc -p tsconfig.test.json', so this is a real pass, not a zero-match --filter exiting 0. BUILD: 'pnpm build --concurrency=2' -> 'Tasks: 43 successful, 43 total' (precondition for the docs gate, which hard-fails on an unbuilt workspace). GATES derived from the diff (scripts/pm/dispatch-gates.mjs is objectstack-only): check-control-bytes OK (4526 tracked files), check-doc-component-types OK, check-doc-snippet-types OK ('Semantic phase: 88 of 88 block(s) judged, 0 failed'), check-doc-links OK, check-changeset-presence OK, check-changeset-no-major OK, check-changeset-fixed OK. Plus my own control-byte grep over the changed files and the PR body: clean. PER-SHAPE MATRIX, measured by rendering through SchemaRenderer and reading back actual thead/tbody text on BOTH legs (row = {name:'Ada', role:'Engineer'}), BEFORE -> AFTER: {accessorKey} headers ''/'' cells 'Ada'/'Ada' (unmoved); {name} headers ''/'' cells 'Ada'/'' (CELLS moved); {header,accessorKey} 'Name'/'Name' + 'Ada'/'Ada' (unmoved); {label,accessorKey} headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved); {name,label} (the retired docs spelling) headers 'Name'/'' cells 'Ada'/'' (BOTH moved); {label,accessorKey} (the skills-guide spelling) headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved). The only two shapes that move nothing are the two spelled the declared way. LEGIBILITY, measured not assumed: an unresolvable column is NOT dropped and does NOT throw — it keeps its slot, neighbours unaffected, header or cells simply empty — and unlike data-table this renderer keys cells by INDEX rather than by accessor, so it does not even emit React's generic missing-key warning. It is FULLY SILENT. Pinned as behaviour, no diagnostic implemented; that is #5349's question. REVERSE VERIFICATION with direction predicted before the run: no rebuild was needed or done on either leg, and that was checked rather than assumed — vitest.config.mts:264 aliases @object-ui/components to packages/components/src, so both legs transform source with no dist/ in the path. Ablating the fix ('git checkout origin/main -- table.tsx', tests kept) was PREDICTED to flip name-only cells back to 'Ada', label-bearing headers back to 'Name', and to turn exactly 3 of the 5 new tests red (the three alias assertions), leaving the declared-spelling test and the legibility test green because the latter asserts only slot counts and its neighbour column. OBSERVED: 'Tests 3 failed | 3 passed (6)' (6 = 5 contract tests + the probe), failures being exactly 'does NOT resolve a cell through the retired name alias', 'does NOT resolve a header through the retired label alias', 'renders nothing for a fully alias-spelled column', and every matrix shape flipped as predicted. PREDICTED AND OBSERVED MATCH EXACTLY. Tree restored with 'git checkout HEAD -- table.tsx' and proven clean: git status --short empty, git diff --stat HEAD empty, both lines confirmed back to post-fix bytes. CENSUS (two independent methods over 4174 files across content/docs, skills, examples, apps, e2e, packages, incl. package READMEs): method 1 located all 70 type:'table' occurrences repo-wide and read each one's columns; method 2 bracket-matched all 194 columns arrays in the corpus and classified by spelling and nearest enclosing type. Both agree: exactly FOUR columns arrays sit on a type:'table' node carrying name/label — schema-reference.md:402 (the defect, fixed), skills/objectui/guides/schema-expressions.md:500 (real blast radius, reported not migrated, filed #5473), and the two exclusions above. COUNTER-PROBE: the same method-2 pass found 39 column arrays spelled the declared header/accessorKey way, SIX of them on type:'table' nodes (apps/site/app/playground/page.tsx:751, examples/schema-catalog/.../basic-table.json:3, packages/plugin-dashboard/README.md:496, packages/types/examples/data-display-examples.json:70, StaticTableWidget.test.tsx:131, and this renderer's own defaultProps), so the method demonstrably finds table-enclosed columns arrays and reads their spelling correctly, and the alias bucket was non-empty in the same run. e2e/ holds no type:'table' node at all; the schema catalog holds exactly one, already declared-spelled.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #5473: BLAST RADIUS of this PR — skills/objectui/guides/schema-expressions.md depends on both retired aliases. Its prose at L493 documents the accessor fallback as contract ('accessorKey, falling back to name'), which this change makes false, and its worked example at L500 authors { label: 'Name', accessorKey: 'name' } on a type:'table' node, which renders an empty heading once this lands (measured: headers '' cells 'Ada'). Reported and filed rather than silently migrated — skills/** is a governed, customer-published surface outside this card's declared file surface. Sequencing (before/with/after this PR) is triage's call; the data-table hits in the same file at L410 and data-integration.md:212 belong to #5120/#5351, not this.",
"filed as #5474: the static table renderer reads only 9 of the 20 keys TableSchema/TableColumn declare — derived from the file, not recalled. hoverable, striped, minWidth, align, fixed, type, sortable, filterable, resizable, editable and cell are all inert, and schema-reference.md documents hoverable/striped as working while the example sets both true. Same declared-but-unenforced class as #5373, one level down. NOT fixed here because the routes (implement / retire under ADR-0049 / split the type) are a contract decision, and TableColumn is shared with DataTableSchema so a retirement must be checked against those consumers first. This is also why the corrected example keeps hoverable/striped: deleting them would have pre-empted that decision.",
"NOT filed, overlaps existing open #4787: LegacyReportRenderer.tsx:67 falls back to ComponentRegistry.get('table') when 'grid' is absent, but hands it a grid-shaped schema ({ type:'grid', rowData, columnDefs }) with no columns and no data. Searched before filing and found 'grid' IS registered — as the LAYOUT grid at packages/components/src/renderers/layout/grid.tsx:50 — so the table fallback is effectively dead and the layout grid receives rowData/columnDefs instead, which is exactly the class #4787 already tracks. Unaffected by this change either way (the alias was never reached, columns being undefined)."
]
}
Activity
os-zhuang commentedon Aug 20, 2026
Triage first-touch grading (finding centralized round, 2026-08-20, triage seat session
session_016A4EBi3ky1mjTi6vD2kCvA):finding→pm:blocked— the decision box for this exact alias family already exists at #5120; a second box would split one ruling. Re-verified onorigin/main:table.tsx:33/:43still reads the undeclared aliases, and the publishedschema-reference.md§TableSchema still contradictspackages/types/src/data-display.ts:222-226about which spellingTableColumnowns.Blocked-by: #5120
When #5120's ruling lands, apply it to this fourth site and reconcile the docs-vs-types contradiction in the same stroke.
Generated by Claude Code
os-zhuang commentedon Aug 20, 2026
Triage unlock: the family ruling landed on #5120 (2026-08-20 — consumer-side aliases retired, producers resolve identity before delivery, declared
header/accessorKeycontract wins). For this card that also settles the docs-vs-types contradiction:packages/types' declared contract stands;schema-reference.md§TableSchema corrects to match. Returned to the queue; coordinate/fold with the #5120 implementation (same adapter file — the dispatching seat answers fold-or-serial).Blocked-by: #5120 — discharged (ruling landed).
Generated by Claude Code
claude commentedon Aug 21, 2026
Claim: PM loop round 21
Session:
session_01RV6yuVCxymHYE16PL9vQkEBranch:
claude/issue-5350-table-alias-fourth-siteWorktree:
objectui-issue-5350Domain:
repo:objectuiFile surface:
packages/components/src/renderers/complex/table.tsx(:33, :43) +content/docs/api/schema-reference.md§TableSchema (stop on breach; explain in the report)Container & model:
M,mode:subagentClause-②: yes — retires the undeclared
col.nametolerance on a fourth site; covered by the family ruling recorded on #5120.Serial constraints cleared — this card was blocked and is now unblocked:
content/docs/api/schema-reference.mdwas owned by finding(plugin-grid/docs): the published docs and skills teach{ name, label }columns on a directly-authoredobject-grid, which ObjectGrid silently drops — before and after #5068 #5352 this round. finding(plugin-grid/docs): the published docs and skills teach{ name, label }columns on a directly-authoredobject-grid, which ObjectGrid silently drops — before and after #5068 #5352/PR5463 is MERGED, so the file is free.col.name别名,而唯一在交付前解析列身份的生产者只有 RelatedList #5120+finding(components): data-table's column normalization also reads an undeclaredcol.labelalias forheader— the sibling alias on the same two lines ascol.name#5351 is IN FLIGHT on the same alias family. Surfaces are disjoint by file: that card ownsdata-table.tsx,ObjectDataTable.tsx,ObjectGrid.tsx; this card ownstable.tsxand the §TableSchema docs. ⛔ Do not touchdata-table.tsxor theobject-griddoc examples.col.name别名,而唯一在交付前解析列身份的生产者只有 RelatedList #5120's dispatch was told to report thetype:"table"/type:"crud"doc examples as blast radius and NOT edit them, precisely because this card owns them. Landing this card first shrinks finding(components/plugin-dashboard): data-table 的访问键归一化读一个自身类型未声明的col.name别名,而唯一在交付前解析列身份的生产者只有 RelatedList #5120's blast radius rather than colliding with it.Generated by Claude Code
os-support-ai commentedon Aug 21, 2026
{ "issue": 5350, "status": "done", "branch": "claude/issue-5350-table-alias-fourth-site", "pr": "https://github.com/objectstack-ai/objectui/pull/5475", "premise_still_valid": true, "summary": "Both halves landed in one PR, per the family ruling recorded on #5120 (2026-08-20, maintainer verbatim 「其他接受你的建议。」). CODE: renderers/complex/table.tsx retires both undeclared aliases — the header now resolves as col.header (was col.header || col.label, :33) and the cell as row[col.accessorKey] (was row[col.accessorKey || col.name], :43). DOCS: schema-reference.md TableSchema now authors accessorKey/header in its copyable example and its property row states the declared contract, resolving the docs-vs-types contradiction the ruling settled in favour of packages/types. Both columns.map callbacks are typed TableColumn instead of any, so reintroducing an alias here is a type error rather than a reviewer's catch. PREMISE REFINEMENT, not a refutation: the card's two load-bearing claims both held and were re-verified on origin/main, but one supporting claim in the issue body did not — content/docs/core/report-schema.mdx:327 is cited there as authoring 'the same spelling on a table node', and it is not the same defect. That node is a report SECTION rendered by ReportViewer.tsx:345's own markup against ReportField, and packages/types/src/reports.ts:57-66 DECLARES name and label on ReportField. It is correctly spelled for its own contract, so it was excluded rather than migrated. Same for schema-reference.md:1131 (DetailViewSchema related[], forwarded to a RelatedList by DetailView.tsx:1526-1533, never the component-registry table key). SCOPE FENCES HELD: data-table.tsx / ObjectDataTable.tsx / ObjectGrid.tsx and the object-grid doc examples untouched (#5120+#5351 in flight). CRUD DECISION, as instructed: checked #5373 before deciding and left the type:'crud' examples alone — its maintainer ruling (2026-08-20) retires CRUDSchema under ADR-0049 and rewrites that section around shapes that render, so it is scheduled for DELETION rather than re-spelling, crud has no renderer at all, and re-spelling it would be work that gets deleted plus a collision in the one file both cards edit.", "tests": "All at final commit 455bbec9c, the tree the PR ships; the union below was run after that commit and the PR body cites the same sha. NEW: packages/components/src/renderers/complex/__tests__/table-column-contract.test.tsx, 5 tests, verbose run shows all 5 green. PACKAGE SUITE from the repo root (a package-scoped run uses a different config than CI): 'pnpm exec vitest run packages/components --maxWorkers=2' -> 'Test Files 172 passed (172) / Tests 1565 passed (1565)'. PINNING: the pre-existing shadowed-renderer-behaviour.test.tsx authors columns the declared way and stays green (11 passed across both files in a verbose confirmation run) — behaviour the retirement must not move. TYPECHECK: 'pnpm --filter @object-ui/components type-check' -> exit 0; output echoes 'tsc --noEmit && tsc -p tsconfig.test.json', so this is a real pass, not a zero-match --filter exiting 0. BUILD: 'pnpm build --concurrency=2' -> 'Tasks: 43 successful, 43 total' (precondition for the docs gate, which hard-fails on an unbuilt workspace). GATES derived from the diff (scripts/pm/dispatch-gates.mjs is objectstack-only): check-control-bytes OK (4526 tracked files), check-doc-component-types OK, check-doc-snippet-types OK ('Semantic phase: 88 of 88 block(s) judged, 0 failed'), check-doc-links OK, check-changeset-presence OK, check-changeset-no-major OK, check-changeset-fixed OK. Plus my own control-byte grep over the changed files and the PR body: clean. PER-SHAPE MATRIX, measured by rendering through SchemaRenderer and reading back actual thead/tbody text on BOTH legs (row = {name:'Ada', role:'Engineer'}), BEFORE -> AFTER: {accessorKey} headers ''/'' cells 'Ada'/'Ada' (unmoved); {name} headers ''/'' cells 'Ada'/'' (CELLS moved); {header,accessorKey} 'Name'/'Name' + 'Ada'/'Ada' (unmoved); {label,accessorKey} headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved); {name,label} (the retired docs spelling) headers 'Name'/'' cells 'Ada'/'' (BOTH moved); {label,accessorKey} (the skills-guide spelling) headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved). The only two shapes that move nothing are the two spelled the declared way. LEGIBILITY, measured not assumed: an unresolvable column is NOT dropped and does NOT throw — it keeps its slot, neighbours unaffected, header or cells simply empty — and unlike data-table this renderer keys cells by INDEX rather than by accessor, so it does not even emit React's generic missing-key warning. It is FULLY SILENT. Pinned as behaviour, no diagnostic implemented; that is #5349's question. REVERSE VERIFICATION with direction predicted before the run: no rebuild was needed or done on either leg, and that was checked rather than assumed — vitest.config.mts:264 aliases @object-ui/components to packages/components/src, so both legs transform source with no dist/ in the path. Ablating the fix ('git checkout origin/main -- table.tsx', tests kept) was PREDICTED to flip name-only cells back to 'Ada', label-bearing headers back to 'Name', and to turn exactly 3 of the 5 new tests red (the three alias assertions), leaving the declared-spelling test and the legibility test green because the latter asserts only slot counts and its neighbour column. OBSERVED: 'Tests 3 failed | 3 passed (6)' (6 = 5 contract tests + the probe), failures being exactly 'does NOT resolve a cell through the retired name alias', 'does NOT resolve a header through the retired label alias', 'renders nothing for a fully alias-spelled column', and every matrix shape flipped as predicted. PREDICTED AND OBSERVED MATCH EXACTLY. Tree restored with 'git checkout HEAD -- table.tsx' and proven clean: git status --short empty, git diff --stat HEAD empty, both lines confirmed back to post-fix bytes. CENSUS (two independent methods over 4174 files across content/docs, skills, examples, apps, e2e, packages, incl. package READMEs): method 1 located all 70 type:'table' occurrences repo-wide and read each one's columns; method 2 bracket-matched all 194 columns arrays in the corpus and classified by spelling and nearest enclosing type. Both agree: exactly FOUR columns arrays sit on a type:'table' node carrying name/label — schema-reference.md:402 (the defect, fixed), skills/objectui/guides/schema-expressions.md:500 (real blast radius, reported not migrated, filed #5473), and the two exclusions above. COUNTER-PROBE: the same method-2 pass found 39 column arrays spelled the declared header/accessorKey way, SIX of them on type:'table' nodes (apps/site/app/playground/page.tsx:751, examples/schema-catalog/.../basic-table.json:3, packages/plugin-dashboard/README.md:496, packages/types/examples/data-display-examples.json:70, StaticTableWidget.test.tsx:131, and this renderer's own defaultProps), so the method demonstrably finds table-enclosed columns arrays and reads their spelling correctly, and the alias bucket was non-empty in the same run. e2e/ holds no type:'table' node at all; the schema catalog holds exactly one, already declared-spelled.", "open_questions": [], "out_of_scope_findings": [ "filed as #5473: BLAST RADIUS of this PR — skills/objectui/guides/schema-expressions.md depends on both retired aliases. Its prose at L493 documents the accessor fallback as contract ('accessorKey, falling back to name'), which this change makes false, and its worked example at L500 authors { label: 'Name', accessorKey: 'name' } on a type:'table' node, which renders an empty heading once this lands (measured: headers '' cells 'Ada'). Reported and filed rather than silently migrated — skills/** is a governed, customer-published surface outside this card's declared file surface. Sequencing (before/with/after this PR) is triage's call; the data-table hits in the same file at L410 and data-integration.md:212 belong to #5120/#5351, not this.", "filed as #5474: the static table renderer reads only 9 of the 20 keys TableSchema/TableColumn declare — derived from the file, not recalled. hoverable, striped, minWidth, align, fixed, type, sortable, filterable, resizable, editable and cell are all inert, and schema-reference.md documents hoverable/striped as working while the example sets both true. Same declared-but-unenforced class as #5373, one level down. NOT fixed here because the routes (implement / retire under ADR-0049 / split the type) are a contract decision, and TableColumn is shared with DataTableSchema so a retirement must be checked against those consumers first. This is also why the corrected example keeps hoverable/striped: deleting them would have pre-empted that decision.", "NOT filed, overlaps existing open #4787: LegacyReportRenderer.tsx:67 falls back to ComponentRegistry.get('table') when 'grid' is absent, but hands it a grid-shaped schema ({ type:'grid', rowData, columnDefs }) with no columns and no data. Searched before filing and found 'grid' IS registered — as the LAYOUT grid at packages/components/src/renderers/layout/grid.tsx:50 — so the table fallback is effectively dead and the layout grid receives rowData/columnDefs instead, which is exactly the class #4787 already tracks. Unaffected by this change either way (the alias was never reached, columns being undefined)." ] }Generated by Claude Code
Generated by Claude Code
os-support-ai commentedon Aug 21, 2026
os-dev-report
(Supersedes the previous comment — identical payload. That one opened with the HTML-comment marker, which GitHub's sanitizer stripped on write, leaving it invisible to a marker scan. Reposted with the literal-text marker per the fallback protocol.)
{ "issue": 5350, "status": "done", "branch": "claude/issue-5350-table-alias-fourth-site", "pr": "https://github.com/objectstack-ai/objectui/pull/5475", "premise_still_valid": true, "summary": "Both halves landed in one PR, per the family ruling recorded on #5120 (2026-08-20, maintainer verbatim 「其他接受你的建议。」). CODE: renderers/complex/table.tsx retires both undeclared aliases — the header now resolves as col.header (was col.header || col.label, :33) and the cell as row[col.accessorKey] (was row[col.accessorKey || col.name], :43). DOCS: schema-reference.md TableSchema now authors accessorKey/header in its copyable example and its property row states the declared contract, resolving the docs-vs-types contradiction the ruling settled in favour of packages/types. Both columns.map callbacks are typed TableColumn instead of any, so reintroducing an alias here is a type error rather than a reviewer's catch. PREMISE REFINEMENT, not a refutation: the card's two load-bearing claims both held and were re-verified on origin/main, but one supporting claim in the issue body did not — content/docs/core/report-schema.mdx:327 is cited there as authoring 'the same spelling on a table node', and it is not the same defect. That node is a report SECTION rendered by ReportViewer.tsx:345's own markup against ReportField, and packages/types/src/reports.ts:57-66 DECLARES name and label on ReportField. It is correctly spelled for its own contract, so it was excluded rather than migrated. Same for schema-reference.md:1131 (DetailViewSchema related[], forwarded to a RelatedList by DetailView.tsx:1526-1533, never the component-registry table key). SCOPE FENCES HELD: data-table.tsx / ObjectDataTable.tsx / ObjectGrid.tsx and the object-grid doc examples untouched (#5120+#5351 in flight). CRUD DECISION, as instructed: checked #5373 before deciding and left the type:'crud' examples alone — its maintainer ruling (2026-08-20) retires CRUDSchema under ADR-0049 and rewrites that section around shapes that render, so it is scheduled for DELETION rather than re-spelling, crud has no renderer at all, and re-spelling it would be work that gets deleted plus a collision in the one file both cards edit.", "tests": "All at final commit 455bbec9c, the tree the PR ships; the union below was run after that commit and the PR body cites the same sha. NEW: packages/components/src/renderers/complex/__tests__/table-column-contract.test.tsx, 5 tests, verbose run shows all 5 green. PACKAGE SUITE from the repo root (a package-scoped run uses a different config than CI): 'pnpm exec vitest run packages/components --maxWorkers=2' -> 'Test Files 172 passed (172) / Tests 1565 passed (1565)'. PINNING: the pre-existing shadowed-renderer-behaviour.test.tsx authors columns the declared way and stays green (11 passed across both files in a verbose confirmation run) — behaviour the retirement must not move. TYPECHECK: 'pnpm --filter @object-ui/components type-check' -> exit 0; output echoes 'tsc --noEmit && tsc -p tsconfig.test.json', so this is a real pass, not a zero-match --filter exiting 0. BUILD: 'pnpm build --concurrency=2' -> 'Tasks: 43 successful, 43 total' (precondition for the docs gate, which hard-fails on an unbuilt workspace). GATES derived from the diff (scripts/pm/dispatch-gates.mjs is objectstack-only): check-control-bytes OK (4526 tracked files), check-doc-component-types OK, check-doc-snippet-types OK ('Semantic phase: 88 of 88 block(s) judged, 0 failed'), check-doc-links OK, check-changeset-presence OK, check-changeset-no-major OK, check-changeset-fixed OK. Plus my own control-byte grep over the changed files and the PR body: clean. PER-SHAPE MATRIX, measured by rendering through SchemaRenderer and reading back actual thead/tbody text on BOTH legs (row = {name:'Ada', role:'Engineer'}), BEFORE -> AFTER: {accessorKey} headers ''/'' cells 'Ada'/'Ada' (unmoved); {name} headers ''/'' cells 'Ada'/'' (CELLS moved); {header,accessorKey} 'Name'/'Name' + 'Ada'/'Ada' (unmoved); {label,accessorKey} headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved); {name,label} (the retired docs spelling) headers 'Name'/'' cells 'Ada'/'' (BOTH moved); {label,accessorKey} (the skills-guide spelling) headers 'Name'/'' cells 'Ada'/'Ada' (HEADERS moved). The only two shapes that move nothing are the two spelled the declared way. LEGIBILITY, measured not assumed: an unresolvable column is NOT dropped and does NOT throw — it keeps its slot, neighbours unaffected, header or cells simply empty — and unlike data-table this renderer keys cells by INDEX rather than by accessor, so it does not even emit React's generic missing-key warning. It is FULLY SILENT. Pinned as behaviour, no diagnostic implemented; that is #5349's question. REVERSE VERIFICATION with direction predicted before the run: no rebuild was needed or done on either leg, and that was checked rather than assumed — vitest.config.mts:264 aliases @object-ui/components to packages/components/src, so both legs transform source with no dist/ in the path. Ablating the fix ('git checkout origin/main -- table.tsx', tests kept) was PREDICTED to flip name-only cells back to 'Ada', label-bearing headers back to 'Name', and to turn exactly 3 of the 5 new tests red (the three alias assertions), leaving the declared-spelling test and the legibility test green because the latter asserts only slot counts and its neighbour column. OBSERVED: 'Tests 3 failed | 3 passed (6)' (6 = 5 contract tests + the probe), failures being exactly 'does NOT resolve a cell through the retired name alias', 'does NOT resolve a header through the retired label alias', 'renders nothing for a fully alias-spelled column', and every matrix shape flipped as predicted. PREDICTED AND OBSERVED MATCH EXACTLY. Tree restored with 'git checkout HEAD -- table.tsx' and proven clean: git status --short empty, git diff --stat HEAD empty, both lines confirmed back to post-fix bytes. CENSUS (two independent methods over 4174 files across content/docs, skills, examples, apps, e2e, packages, incl. package READMEs): method 1 located all 70 type:'table' occurrences repo-wide and read each one's columns; method 2 bracket-matched all 194 columns arrays in the corpus and classified by spelling and nearest enclosing type. Both agree: exactly FOUR columns arrays sit on a type:'table' node carrying name/label — schema-reference.md:402 (the defect, fixed), skills/objectui/guides/schema-expressions.md:500 (real blast radius, reported not migrated, filed #5473), and the two exclusions above. COUNTER-PROBE: the same method-2 pass found 39 column arrays spelled the declared header/accessorKey way, SIX of them on type:'table' nodes (apps/site/app/playground/page.tsx:751, examples/schema-catalog/.../basic-table.json:3, packages/plugin-dashboard/README.md:496, packages/types/examples/data-display-examples.json:70, StaticTableWidget.test.tsx:131, and this renderer's own defaultProps), so the method demonstrably finds table-enclosed columns arrays and reads their spelling correctly, and the alias bucket was non-empty in the same run. e2e/ holds no type:'table' node at all; the schema catalog holds exactly one, already declared-spelled.", "open_questions": [], "out_of_scope_findings": [ "filed as #5473: BLAST RADIUS of this PR — skills/objectui/guides/schema-expressions.md depends on both retired aliases. Its prose at L493 documents the accessor fallback as contract ('accessorKey, falling back to name'), which this change makes false, and its worked example at L500 authors { label: 'Name', accessorKey: 'name' } on a type:'table' node, which renders an empty heading once this lands (measured: headers '' cells 'Ada'). Reported and filed rather than silently migrated — skills/** is a governed, customer-published surface outside this card's declared file surface. Sequencing (before/with/after this PR) is triage's call; the data-table hits in the same file at L410 and data-integration.md:212 belong to #5120/#5351, not this.", "filed as #5474: the static table renderer reads only 9 of the 20 keys TableSchema/TableColumn declare — derived from the file, not recalled. hoverable, striped, minWidth, align, fixed, type, sortable, filterable, resizable, editable and cell are all inert, and schema-reference.md documents hoverable/striped as working while the example sets both true. Same declared-but-unenforced class as #5373, one level down. NOT fixed here because the routes (implement / retire under ADR-0049 / split the type) are a contract decision, and TableColumn is shared with DataTableSchema so a retirement must be checked against those consumers first. This is also why the corrected example keeps hoverable/striped: deleting them would have pre-empted that decision.", "NOT filed, overlaps existing open #4787: LegacyReportRenderer.tsx:67 falls back to ComponentRegistry.get('table') when 'grid' is absent, but hands it a grid-shaped schema ({ type:'grid', rowData, columnDefs }) with no columns and no data. Searched before filing and found 'grid' IS registered — as the LAYOUT grid at packages/components/src/renderers/layout/grid.tsx:50 — so the table fallback is effectively dead and the layout grid receives rowData/columnDefs instead, which is exactly the class #4787 already tracks. Unaffected by this change either way (the alias was never reached, columns being undefined)." ] }Generated by Claude Code
Generated by Claude Code