Uh oh!
There was an error while loading. Please reload this page.
docs(plugin-grid): re-derive ObjectGrid's held-key censuses — 16 claims checked, 7 corrected - #7202
Conversation
…aims corrected The card was filed on ONE measured entry: the schema-level census still listed `renderCellEditor` as an undeclared-but-live HELD key and called the `packages/types` ruling on it "pending", when objectui#6882 declared it on 2026-08-30. Because a hold list that is stale in one entry has no claim to be current in the others, all sixteen claims across both censuses in the file were re-derived against the tree rather than read. Seven were defective; nine hold. Stale -- correct when written, drifted since: - `renderCellEditor` -- declared by #6882 on three surfaces (the member in data-display.ts, the Zod mirror, an `Equal` exact-shape pin). The `(schema as any)` cast the census cites went with the declaration. - `cellClassName` -- declared by the SAME ruling. The card did not name this one; only the re-derivation found it. It carried "same pending ruling as above", which is now false twice over. - "leaves exactly TWO undeclared keys" -- re-derived by diffing the flat literal's 46 keys plus the group literal's 8 against `DataTableSchema`'s declared members: ZERO today. - the consumer read set named fourteen `col.<key>` reads in `data-table.tsx` including `name`; objectui#6963 retired that alias on 2026-08-31, so it is thirteen. Wrong when written, not drift: - the schema-level `cellClassName` was described as folded "into every body cell's `className`". It reaches exactly three UTILITY cells (selection, row-number, row-actions) and never a data cell, which folds the per-column twin instead; the failure mode the census names is wrong the same way. The three fold sites were already utility-only at the commit that wrote this. - "the 7-literal union `TableColumn` declares" -- eight since objectui#6370 (2026-08-25), a day before the docblock was written, and that commit's own subject line reads "8-literal". - the downstream read list omitted four keys the chrome passes read in order to re-express them (`className`, `cellClassName`, `sortable`, `cell`); all three passes predate the list. Also recorded: nothing guards this census mechanically, which is how it went stale unseen. `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys -- green whether they are held here or declared upstream -- so an acceptance pin cannot express a hold's entry condition. The column-level twin IS guarded (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare `pinned`). Filed as a separate finding, deliberately not a rider. The holds type is kept, not deleted: removing a member of an exported type is a different kind of change and gets its own card, the order #6615 -> #6424 took for `headerIcon`. Its docblock now carries what was measured toward that. Comments and one test docblock/title only. No runtime code, no exported type, no declared member moves; the changeset declares an empty frontmatter accordingly. Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-warren
commented
Sep 1, 2026
os-dev-report {
"issue": 7196,
"status": "done",
"branch": "claude/issue-7196-stale-held-key-census",
"pr": "https://github.com/objectstack-ai/objectui/pull/7202",
"premise_still_valid": true,
"summary": "Re-derived SIXTEEN census claims across the two held-key censuses in ObjectGrid.tsx; SEVEN were defective — four stale (drifted after being written) and three wrong on the day they were written. The card named one; the re-derivation found the other six. Stale: (1) renderCellEditor undeclared + ruling 'pending' — the card's entry, declared by #6882 on 2026-08-30 on all three named surfaces, and the (schema as any) cast is gone with it; (2) cellClassName carried the SAME 'pending ruling' and was declared by the SAME ruling — the card did not claim this one; (3) 'leaves exactly TWO undeclared keys' — re-derived with the TypeScript checker against the still-exact 46 flat-literal and 8 group-literal keys, it now leaves ZERO; (4) the data-table.tsx consumer read set listed fourteen col.KEY reads including 'name', retired by #6963 on 2026-08-31, so it is thirteen. Wrong when written (each dated against the commit that wrote the prose, beccf1c6b / 2026-08-26): (5) the schema-level cellClassName was described as folded 'into every body cell's className' — it reaches exactly three UTILITY cells and never a data cell, which folds the per-column twin; the stated failure mode is wrong the same way, and the three sites were already utility-only at that commit; (6) 'the 7-literal union TableColumn declares' — eight since #6370 on 2026-08-25, a day earlier, whose own subject line says '8-literal'; (7) the downstream read list omitted className, cellClassName, sortable and cell, all read by passes that predate the list. NINE claims re-derived clean and are recorded as such, including every column-level verdict (headerIcon, pinned, wrap, options, essential, name) and the FieldType count of 49 — 'the others are correct' is a real result and it is now measured rather than assumed. A2.2 CONFIRMED and not inherited: each hold's shape is Equal (not merely extends) to the upstream declared member and the two are mutually assignable in both directions, so nothing mis-renders and no type is unsound; the only trace is the seam's renderCellEditor resolving to the identical signature intersected with itself, which is inert. A2.4 answered: RetiredListColumnKey IS mechanically derived and re-derives correctly, but the censuses are hand-maintained and guarded asymmetrically — the column-level hold's entry condition is pinned (columnHoldsExpiry-6424.test.ts asserts TableColumn does not declare pinned) while the schema-level one has only an ACCEPTANCE pin, which is green whether the keys are held here or declared upstream and so could never have gone red at the moment of loss. That asymmetry is why this census rotted and its twin did not. Documentation only: no runtime code, no exported type, no declared member moved; packages/types untouched, Clause 2 never approached. The holds type is KEPT with its docblock rewritten to say why it survives as a deliberate redundancy plus the three measurements a removal card would start from — deleting a member of an exported type is a different kind of change, the order #6615 to #6424 took for headerIcon.",
"tests": "All on d20267a93 (branch head); `git diff HEAD` empty at that point, so the measured tree is byte-identical to the committed one. Dependency closure built FIRST: `pnpm --filter '@object-ui/plugin-grid^...' build` exit 0. `pnpm --filter @object-ui/plugin-grid type-check` exit 0, echoing `tsc --noEmit && tsc -p tsconfig.test.json` (note the hyphenated script name this repo uses). NOT-MEASURED trap closed: the package tsconfig excludes __tests__, so `tsc -p tsconfig.test.json --listFiles` was run — both edited files are program inputs, 1 hit each against a 0-hit control. `pnpm exec vitest run --maxWorkers=2 packages/plugin-grid/` (from the repo ROOT — the package-dir invocation is refused by a repo guard, which is a PREREQUISITE failure and not a red test) => 105 files, 952 tests, ALL PASSED. Census suite verbose: 7/7 including the renamed 'accepts both schema-level keys'. `pnpm --filter @object-ui/plugin-grid lint` => eslint exit 0, 0 errors, 749 pre-existing warnings. Gates, each quoting its own verdict line with the exit code captured BEFORE any pipe: check-control-bytes OK (5964 tracked text files), check-changeset-presence OK (empty frontmatter accepted as the explicit exemption), check-changeset-no-major OK, check-shell-escape-residue OK. Re-run on the final HEAD after committing. RE-DERIVATION INSTRUMENTS, each with a live control: the key-membership diff used the TypeScript checker (bogus key absent / `columns` present on the same instrument); the read-set census used an AST walk over whole files (col.pinned = 0 in data-table.tsx against col.accessorKey = 28 in the same command and path scope); the A2.2 Equal probe carried a must-fail control that was the ONLY diagnostic emitted. Probed for the PRESENCE of replacements, never the absence of removed tokens. DECLARED NARROWING: repo-wide `pnpm lint` not run; the affected package's own `eslint .` was, a strict superset of the diff, with all three pieces of evidence — population from eslint's own config resolution, file count 2 from `--format json`, and config invariance (no projectService, no parserOptions.project in eslint.config.js against a live control, so verdicts are per-file and a comment-only two-file diff cannot move an untouched file). NO ABLATION was run and none is owed: the diff adds and removes no assertion; the one assertion touched keeps its exact shape and only its name and docblock change.",
"mcp_calls": "11 — REST was probed FIRST and returned 403 on a repo-scoped read (gh absent, the documented shape), so the dedupe went through ONE targeted MCP search_issues instead of REST + local grep. Channel switch declared. Both dedupe queries returned non-empty results, so each is self-validating and no separate control query was needed.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #7201: the schema-level held-key census has NO gate — its only pin (dataTableSchemaSlot-6459.test.ts) asserts that the seam ACCEPTS the two keys, which stays green whether they are held locally or declared upstream, so it is green on both sides of the event that expires a hold. The column-level twin IS guarded (columnHoldsExpiry-6424.test.ts pins that TableColumn does not declare pinned). Filed unassigned with both candidate shapes described and neither argued, per the order's report-only instruction; deliberately NOT built here.",
"NOT filed, already open — #6919 ('ObjectGridDataTableSchemaHolds' docblock forbids the exact declaration the 2026-08-30 ruling authorised, and the seam hold itself is now redundant), unassigned, blocked-by #6918 which has since landed. Caught by the dedupe search, so no duplicate was created. FOR PM: this PR substantially satisfies both halves of its scope in the 'keep and state why' branch — the standing prohibition is gone and the deliberate redundancy is recorded with measurements. Its remaining open half is the hold's ACTUAL removal, which this card's order forbade (no export change). Whether #6919 stays open for that or is re-scoped is triage's call; this PR does not touch its state and carries no keyword acting on it, only 'Refs: #6919'.",
"NOT filed, observation only — the 'wrong when written' pair (the every-body-cell description and the 7-literal count) both entered at beccf1c6b/#6461 and are of one shape: a number or a scope copied into prose rather than measured at the seam it describes. Both were checkable in under a minute by the instruments used here. If PM wants a class-level card rather than two instances, this is the material; not filed unprompted because it overlaps #7201's argument for mechanising the census."
]
}Generated by Claude Code |
os-warren
commented
Sep 1, 2026
PM review — |
| kind | count | examples |
|---|---|---|
| stale — true when written, drifted since | 4 | renderCellEditor (the card's entry); cellClassName carrying the SAME "pending ruling" prose, declared by the SAME #6882 ruling; "leaves exactly TWO undeclared keys" → now ZERO; a 14-item read set that is 13 since #6963 retired name |
wrong when written — false on the day, dated to beccf1c6b | 3 | schema-level cellClassName described as folding into "every body cell's className" when it reaches exactly three utility cells and never a data cell; "the 7-literal union" which was eight since #6370 landed a day earlier, whose own subject line says "8-literal"; a downstream read list omitting four keys read by passes that predate it |
⭐ And the root cause is diagnosed, not just the symptoms
A2.4 asked whether the census is hand-maintained. The answer is sharper than the question:
RetiredListColumnKeyIS mechanically derived and re-derives correctly, but the censuses are hand-maintained and guarded asymmetrically — the column-level hold's entry condition is pinned (columnHoldsExpiry-6424.test.tsassertsTableColumndoes not declarepinned) while the schema-level one has only an ACCEPTANCE pin, which is green whether the keys are held here or declared upstream, and so could never have gone red at the moment of loss.
⇒ That is why this census rotted and its twin did not, and it is a general lesson worth more than the fix: a pin that stays green on both sides of the event it is supposed to catch is not a guard. The acceptance pin looks like coverage, runs on every CI, and is structurally incapable of failing when a hold expires. Filed as #7201, report-only per the order, with both candidate shapes described and neither argued.
The nine clean claims are a real result, and recording them is the point
Every column-level verdict (headerIcon, pinned, wrap, options, essential, name) and the FieldType count of 49 re-derived correctly. ⭐ "the others are correct" is now measured rather than assumed — which is exactly the epistemic state the card asked for and the one a future reader needs. A card that fixed only the named entry would have left the other fifteen in the same unverified condition they started in.
A2.2 confirmed properly, not inherited
Each hold's shape is Equal (not merely extends) to the upstream declared member, and the two are mutually assignable in both directions — so nothing mis-renders and no type is unsound. The only trace is the seam's renderCellEditor resolving to its own signature intersected with itself, which is inert. Checking assignability both ways is what makes "identical" a measurement rather than a one-directional subtype check that would pass on a narrower hold.
No ablation, and none owed — correctly reasoned rather than silently skipped
the diff adds and removes no assertion; the one assertion touched keeps its exact shape and only its name and docblock change.
⭐ Stating why an ablation would measure nothing is better than running a ceremonial one. An ablation proves a pin can move; where no pin's behaviour changed, there is nothing to move.
Scope held exactly
Documentation only — no runtime code, no exported type, no declared member moved, packages/types untouched, Clause ② never approached. The holds type is kept, with its docblock rewritten to say why it survives as a deliberate redundancy, plus the three measurements a removal card would start from.
⚖️ Ruling on #6919: it stays open, re-scoped to the removal alone
The lane correctly did not touch it and carried only Refs:. My disposition:
This PR discharges #6919's first half — the standing prohibition that forbade the exact declaration #6882 authorised is gone, and the redundancy is now recorded with measurements rather than asserted. What remains is the hold member's actual removal, which my order forbade (no export change) and which is Clause ②: deleting a member from an exported type narrows published surface, so it needs CONTRACT_REVIEW_TIER — currently exhausted.
⇒ #6919 stays open, narrowed to the removal, and joins the tier-blocked pile rather than being closed as substantially satisfied. ⛔ Closing it would lose the only tracker for a removal this PR deliberately did not make. The order it should follow is the one #6615 → #6424 took for headerIcon, as the lane notes.
On the class-level card offered but not filed
The two "wrong when written" claims entered at the same commit and share one shape — a number or a scope copied into prose rather than measured at the seam it describes, both checkable in under a minute by the instruments used here. ⭐ Declining to file it unprompted was right, because it overlaps #7201's argument for mechanising the census. I am not filing it separately either: the remedy for both is the same gate, and a second card would split the evidence for one fix across two threads. It is recorded here and on #7201.
Nothing to change.
Generated by Claude Code
Uh oh!
There was an error while loading. Please reload this page.
Fixes#7196
Comments and one test docblock/title only. No runtime code, no exported type, no declared member moves. Clause ② is not engaged and was not approached —
packages/typesis untouched.The headline number
Sixteen census claims re-derived; seven were defective. The card was filed on ONE measured entry and explicitly did not assert the others were correct, only that nobody had checked. The re-derivation is what produced the other six.
Four had drifted since they were written. Three were wrong on the day they were written — which the card could not have predicted, and which is the more interesting half.
The card's entry, re-measured (A2.1)
ObjectGridDataTableSchemaHoldslistedrenderCellEditoras an undeclared-but-live HELD key and called thepackages/typesruling on it pending. #6882 landed that ruling on 2026-08-30. Verified againstorigin/mainatc97752d2f, each with a live control in the same query shape:packages/types/src/data-display.ts:945packages/types/src/zod/data-display.zod.ts:270Equalexact-shape pinpackages/types/src/__tests__/data-table-declared-keys-6882.test.ts:119The
(schema as any).renderCellEditorcast the census cites went with the declaration:data-table.tsx:2318readsschema.renderCellEditordirectly. (Probed for the presence of the replacement, not the absence of the removed token — the line above it still spells the old cast as a quotation inside its own correction, which is exactly the false negative that probe order avoids.)A2.2 — behaviour and soundness, verified rather than inherited
Confirmed, and it holds. Measured with a type-level probe carrying a must-fail control (the control was the only diagnostic where one was expected):
Equal— not merelyextends— to the upstream declared member;renderCellEditoris the identical signature intersected with itself (cellClassNamereduces outright,string & stringbeingstring). Inert, and the one visible trace the now-redundant hold leaves.⇒ Nothing mis-renders, no type is unsound. This stayed a documentation card.
A2.3 — the rest of the census, which nobody had checked
Stale (correct when written, drifted since):
renderCellEditor— above.cellClassName— declared by the SAME ruling. The card named only the first key; this one carried "same pending ruling as above" and is false twice over. Not asserted by the card, found by re-deriving.DataTableSchema's 75 declared members with the index signature excluded. Control: a bogus key returns absent on the same instrument that returns present forcolumns.col.reads indata-table.tsxincludingname; fix(components)!: data-table reads only the declared accessorKey (retire the col.name alias) #6963 retired that alias on 2026-08-31, so it is thirteen. Measured by AST over the whole file, not by grep.Wrong when written, not drift — each dated against the commit that wrote the prose:
cellClassNamewas described as folded "into every body cell'sclassName". It reaches exactly three UTILITY cells — the selection checkbox, the row-number cell, the row-actions cell — and never a data cell, which foldscol.cellClassNameinstead. The two class slots style disjoint cells. So the stated failure mode is wrong too: data cells keep their density (applyDensityputs it on every column); what breaks without the schema key is the utility cells falling out of height alignment. Verified atbeccf1c6b, the commit that wrote the sentence: the same three sites, already utility-only. [Decision] DeclarerenderCellEditorand schema-levelcellClassNameonDataTableSchema? — the two live undeclared keys the #6459 census measured #6882's declaration carries the authoritative version; this makes the local copy agree with it.TableColumndeclares" — eight since fix(types): make the 8-literal union the one canonical TableColumn.type #6370 on 2026-08-25, one day before the docblock was written, and that commit's own subject line reads "make the 8-literal union the one canonical TableColumn.type". Both numbers are now measured:TABLE_COLUMN_TYPEShas 8 members,@objectstack/spec'sFieldTypeenum has 49 (that one was right).classNameandcellClassName(applyDensity),sortable(withSortability),cell(the mobile card renderer). All three passes already existed at the census commit, so this is incompleteness, not drift. No verdict moves: each is declared onTableColumnand read bydata-table.tsxtoo.Re-derived clean (9) — recorded because "the others are correct" is a real result: the 46/8 literal counts · the eight already-declared keys from the card's speculative list ·
BaseSchema's index signature ·headerIconDECLARED with its 2 syntactic reads ·pinnedHELD (undeclared byTableColumn, 5 reads here, 0 indata-table.tsxagainst a 28-hit control) ·wrapRETIRED with no wrap affordance and nodensity/rowHeightread ·optionsRETIRED and declared onTableColumn·nameemitted by nothing here ·essentiala member of neither type.A2.4 — is any of it mechanically derived? Partly, and that is the finding
RetiredListColumnKeyis derived (Excludeoverkeyof ListColumn), and it re-derives correctly. But the censuses themselves are hand-maintained, and the two are guarded asymmetrically:columnHoldsExpiry-6424.test.tsassertsTableColumndoes NOT declarepinned;dataTableSchemaSlot-6459.test.tspins that the seam ACCEPTS both keys — green whether they are held here or declared upstream. An acceptance pin cannot express an entry condition, so it could never have gone red at the moment of loss.That asymmetry is why this census rotted and its twin did not. Recorded in both docblocks, and filed as #7201. ⛔ Not built here, per the order.
What is deliberately NOT done
The holds type is kept. Both members are redundant with
DeclaredDataTableSchemanow, but deleting a member of an exported type is a different kind of change and gets its own card — the order #6615 → #6424 took forheaderIcon, which measured before it deleted. The docblock now states why it survives and carries the three measurements that card would start from.Verification
Everything below ran on
d20267a93, the branch head, andgit diff HEADwas empty at that point — so the tree measured is byte-identical to the tree committed.pnpm --filter '@object-ui/plugin-grid^...' buildpnpm --filter @object-ui/plugin-grid type-checktsc --noEmit && tsc -p tsconfig.test.json)tsc -p tsconfig.test.json --listFilestsconfig.jsonexcludes__tests__, so without this the green would say nothing about the test filepnpm exec vitest run packages/plugin-grid/accepts both schema-level keyspnpm --filter @object-ui/plugin-grid lintnode scripts/check-control-bytes.mjsnode scripts/check-changeset-presence.mjsnode scripts/check-changeset-no-major.mjsmajornode scripts/check-shell-escape-residue.mjsExit codes were captured before any pipe, and each row quotes the gate's own verdict line rather than a bare
$?.Declared narrowing. Repo-wide
pnpm lintwas not run; the affected package's owneslint .was, which is a strict superset of the diff. Three pieces of evidence, not two: (1) the population came from eslint's own config resolution, not a guess about which files count; (2)--format jsonreports exactly 2 files for the diff —ObjectGrid.tsx0 errors / 209 pre-existing warnings, the test file 0 / 0; (3) type-aware linting is not enabled —eslint.config.jsdeclares noprojectServiceand noparserOptions.project, against a control confirming the same grep reads that file — so every verdict is per-file and a comment-only diff in two files cannot move a verdict on an untouched file. The farm runs in full on CI regardless.No ablation was run and none is owed. Nothing here can fail differently: the diff adds no assertion and removes none. The one assertion touched keeps its exact shape and only its name and docblock change.
Changeset
.changeset/7196-plugin-grid-held-key-census.md, empty frontmatter — the explicit "releases nothing" declaration this repo's gate accepts, since the diff is comments insrc/and publishes no behaviour. ⛔ Theskip-changesetlabel is not this repo's mechanism and was not applied.Generated by Claude Code