Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/7196-plugin-grid-held-key-census.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
---
---

Re-derives the held-key censuses in `packages/plugin-grid/src/ObjectGrid.tsx` and corrects
the seven claims that no longer match the tree. Comments and one test docblock only — no
runtime code, no exported type, no declared member moves, so nothing publishes.

The card (objectui#7196) 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 had declared it on 2026-08-30. The card
deliberately did not claim the other entries were correct, only that nobody had checked, so
all sixteen claims across both censuses in the file were re-derived against `origin/main`
rather than read. Seven were defective:

Stale — correct when written, drifted since:

- `renderCellEditor` — declared by objectui#6882 on three surfaces (the member, 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; the
re-derivation did. The census called it a hold with a "pending ruling" too.
- "leaves exactly TWO undeclared keys" — diffing the 46 flat-literal keys plus the 8
group-literal keys against `DataTableSchema`'s declared members now leaves ZERO.
- the consumer read set listed 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. The failure mode
the census names is wrong in the same way. objectui#6882's declaration carries the
correct version upstream; this makes the local copy agree with it.
- "the 7-literal union `TableColumn` declares" — it has been eight since objectui#6370
(2026-08-25), a day before the docblock was written; that commit's own subject says
"8-literal".
- the downstream read list omitted four keys the chrome passes read to re-express
(`className`, `cellClassName`, `sortable`, `cell`). All three passes 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 holds type is left in place, with its docblock rewritten to record that it is now
redundant rather than load-bearing and what was measured toward removing it. Deleting a
member of an exported type is a different kind of change and gets its own card, the way
objectui#6615 was followed by objectui#6424 for `headerIcon`.
170 changes: 135 additions & 35 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -527,12 +527,24 @@ function normalizeColumns(
* Consumers measured for THIS producer — two of them, because the array is read
* twice before it reaches the slot:
*
* - `data-table.tsx`, comments stripped, every `col.<key>` read: `accessorKey`,
* `width`, `align`, `header`, `className`, `cellClassName`, `sortable`,
* `resizable`, `editable`, `type`, `cell`, `headerIcon`, `fitContent`, `name`.
* - `data-table.tsx`, comments stripped, every `col.<key>` read — THIRTEEN:
* `accessorKey`, `width`, `align`, `header`, `className`, `cellClassName`,
* `sortable`, `resizable`, `editable`, `type`, `cell`, `headerIcon`,
* `fitContent`. ⚠️ This said fourteen until objectui#7196 re-derived it:
* `name` was the fourteenth and was correct when written, but objectui#6963
* (2026-08-31) retired the `col.name` alias — the last undeclared spelling
* the adapter accepted — so the key left the consumer's read set that day.
* - THIS FILE's own downstream passes, which read the array before handing it
* on: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`.
* on — TEN: `pinned` (the left/right reorder + the frozen-column verdict),
* `accessorKey`, `header`, `width`, `type`, `fitContent`, plus the four the
* chrome passes read in order to RE-EXPRESS them: `className` and
* `cellClassName` (`applyDensity`, and again in the right-pinned literal),
* `sortable` (`withSortability`), `cell` (the mobile card renderer).
* ⚠️ Those four were missing from this list from the day it was written —
* all three passes already existed at that commit — so this is an
* incompleteness, not drift. They change no verdict: each is declared on
* `TableColumn` and read by `data-table.tsx` as well. Recorded because a
* list presented as MEASURED has to be one.
*
* Verdicts, each with the read-count behind it:
*
Expand DownExpand Up@@ -567,7 +579,10 @@ function normalizeColumns(
* fold still stands. It is the one member whose vocabulary differs between
* the two types below.
* - `name` — not emitted by this producer at all, so objectui#5120's alias
* needs no hold here. Tombstoned only in the sense that nothing writes it.
* never needed a hold here. Tombstoned only in the sense that nothing writes
* it — and since objectui#6963 (2026-08-31) nothing READS it either: the
* consumer-side alias is retired, so the key is absent from BOTH ends of
* this seam and the verdict now rests on two measurements, not one.
*
* `essential` is absent from both types on purpose: objectui#6004's suggested
* key list named it, but it was READ off the authored column and turned into a
Expand DownExpand Up@@ -627,10 +642,19 @@ export interface ObjectGridColumnHolds {
/**
* What `generateColumns()` returns: everything final EXCEPT `type`, which is
* still the producer's raw inference vocabulary (`@objectstack/spec`'s
* `FieldType`, 49 values) rather than the 7-literal union `TableColumn`
* declares. objectui#5853 folds it downstream, in a pass that is deliberately
* separate from the enrichment map — so the pre-fold shape needs a name, and
* this is it.
* `FieldType`, 49 values) rather than the EIGHT-literal union `TableColumn`
* declares (`TABLE_COLUMN_TYPES`: `text`, `number`, `date`, `datetime`,
* `currency`, `percent`, `boolean`, `action`). objectui#5853 folds it
* downstream, in a pass that is deliberately separate from the enrichment map —
* so the pre-fold shape needs a name, and this is it.
*
* ⚠️ This said "7-literal" until objectui#7196 re-derived it. `action` joined
* the union at objectui#6370 on 2026-08-25 — a day BEFORE this docblock was
* written, and that commit's own subject line reads "make the 8-literal union
* the one canonical TableColumn.type" — so the count was never right here; it
* was mis-copied, not drifted. Both numbers are now measured rather than
* recited: `TABLE_COLUMN_TYPES` in `@object-ui/types` has 8 members and
* `@objectstack/spec`'s `FieldType` enum has 49.
*/
/**
* ⭐ `options` — RETIRED at this emit (objectui#6004), and this explicit
Expand DownExpand Up@@ -720,30 +744,62 @@ export type ObjectGridColumn =
* tombstones remain the instrument for a key RETIRED by ruling, but an open
* census cannot be tombstoned, because a tombstone needs the key's name.
*
* ## The census this annotation surfaced (the substance, per the card)
* ## The census this annotation surfaced — CLOSED, re-derived by objectui#7196
*
* Diffing the 46 keys the flat literal writes (plus the 8 the group literal
* re-writes) against `DataTableSchema` + `BaseSchema` declared members leaves
* exactly TWO undeclared keys — the card's speculative list (`pagination`,
* `manualPagination`, `rowCount`, `frozenColumns`, `singleClickEdit`,
* `selectionResetKey`, `disableInnerScroll`, `borderless`) has since been
* declared on `DataTableSchema`, and only these survive:
* re-writes) against `DataTableSchema` + `BaseSchema` declared members left
* exactly TWO undeclared keys when this was written. Re-derived on 2026-09-01
* against the same two literals (still 46 and 8), it now leaves **ZERO**. Both
* halves closed:
*
* - the card's speculative list (`pagination`, `manualPagination`, `rowCount`,
* `frozenColumns`, `singleClickEdit`, `selectionResetKey`,
* `disableInnerScroll`, `borderless`) was already declared then, and is
* still declared now;
* - the two keys that survived that diff, `renderCellEditor` and
* `cellClassName`, were DECLARED by objectui#6882 (maintainer ruling
* 2026-08-30) on three surfaces:
* · `packages/types/src/data-display.ts` — the members themselves
* · `packages/types/src/zod/data-display.zod.ts` — the Zod mirror
* · `packages/types/src/__tests__/data-table-declared-keys-6882.test.ts`
* — an `Equal` (not `extends`) exact-shape pin
*
* ⇒ The ruling this census was filed for HAPPENED, and it went the declare way.
* `ObjectGridDataTableSchemaHolds` below is therefore redundant rather than
* load-bearing — the position `headerIcon` reached at objectui#6615 — and
* removing it is the same separate, MEASURED step objectui#6424 took there. Its
* docblock carries what #7196 measured toward that.
*
* - `renderCellEditor` — HELD. Live: `data-table.tsx` reads it via its own
* `(schema as any).renderCellEditor` cast and hands cell editing to the
* returned widget; absent, cells fall back to the built-in text/number/date
* inputs. Undocumented at schema level. Whether `DataTableSchema` should
* declare it is a `packages/types` (human-floor) ruling, not this card's —
* declared here at the seam meanwhile, so the hold is visible.
* - `cellClassName` — HELD. Live: `data-table.tsx` destructures it off the
* schema and folds it into every body cell's `className` (this is the
* SCHEMA-level key; the column-level twin IS declared, on `TableColumn`).
* Absent, the grid's row-height density styling stops reaching cells.
* Undocumented at schema level; same pending ruling as above.
* ### What the two entries used to say, and what is true instead
*
* ⛔ Do not "fix" either hold by declaring the key on `DataTableSchema` as a
* rider — that package is published surface with its own review floor, and the
* census above is filed for a ruling on exactly that question.
* - `renderCellEditor` — said HELD, read "via its own `(schema as any)`
* cast", and called the `packages/types` ruling still open. All three are
* over: the key is declared, the cast went with the declaration
* (`data-table.tsx` reads `schema.renderCellEditor` directly; the line where
* the cast stood still spells it, as a quotation inside its own
* correction), and the ruling landed. BEHAVIOUR is unchanged and always
* was — a returned widget takes the cell, `null` falls through to the
* built-in text / number / date inputs.
* - `cellClassName` — said HELD, and described the key as folded "into every
* body cell's `className`". The hold is over (declared by the same #6882);
* the DESCRIPTION was wrong from the day it was written, which is the more
* useful half of this correction. Measured: `data-table.tsx` folds the
* SCHEMA-level key at exactly three sites and every one is a UTILITY cell —
* the selection checkbox, the row-number cell, the row-actions cell. It
* never reaches a data cell; a data cell folds `col.cellClassName`, the
* per-column twin declared on `TableColumn`. The two class slots style
* DISJOINT cells and never combine on one. So what breaks when the schema
* key is absent is NOT "density stops reaching cells": data cells keep
* their density, because `applyDensity` below puts the same class on every
* column. What breaks is the checkbox / row-number / row-actions cells
* falling out of height alignment with the data beside them, which is why
* this grid sets BOTH slots. #6882's declaration is where the authoritative
* version of this now lives; this is the local copy agreeing with it.
*
* ⛔ The old closing note ("do not fix either hold by declaring the key on
* `DataTableSchema` as a rider — that package is published surface with its own
* review floor") governs nothing now. It was asking for the ruling to be taken
* deliberately at that package's floor, and that is exactly how #6882 took it.
*/
type RemoveIndexSignature<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
Expand All@@ -753,12 +809,50 @@ type RemoveIndexSignature<T> = {
export type DeclaredDataTableSchema = RemoveIndexSignature<DataTableSchema>;

/**
* The undeclared-but-live SCHEMA-level keys this grid holds at the seam — the
* schema-slot sibling of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's
* reads in `data-table.tsx`, not what this file happens to pass.
* The SCHEMA-level keys this grid holds at the seam — the schema-slot sibling
* of `ObjectGridColumnHolds`. Shapes mirror the CONSUMER's reads in
* `data-table.tsx`, not what this file happens to pass.
*
* ⚠️ "Undeclared by `DataTableSchema`" was this type's ENTRY CONDITION, and —
* exactly as `ObjectGridColumnHolds` warns about its own — it is a claim about
* ANOTHER package that can stop being true with nothing going red here. It
* stopped being true on 2026-08-30: objectui#6882 declared BOTH members. As of
* objectui#7196 this type holds nothing; every member is redundant with
* `DeclaredDataTableSchema`.
*
* ⛔ Kept rather than deleted, because deleting a member of an EXPORTED type is
* a change of a different kind and gets its own card — the order objectui#6615
* → #6424 took for `headerIcon`. What #7196 measured, so that card can start
* from a reading instead of a guess:
*
* - each member's shape is `Equal` (not merely assignable) to the upstream
* declared member, so removing it cannot narrow or widen the seam;
* - the seam's `cellClassName` is already reduced (`string & string` is
* `string`), while its `renderCellEditor` resolves to the SAME signature
* intersected with itself. That is inert — mutually assignable with the
* declared member, measured in both directions — but it is the one visible
* trace the redundant hold leaves, and it is what the exact-shape pin in
* `packages/types` would report if pointed at the seam type;
* - unlike `ObjectGridColumnHolds.pinned`, neither member is its own ONLY
* declaration on the emitted type, so deleting them deletes nothing from
* it. That is precisely the ⛔ contrast `ObjectGridColumnHolds` spells out,
* and this type is now on the other side of it.
*
* ⛔ Nothing above is mechanically checked, which is how it went stale unseen.
* `dataTableSchemaSlot-6459.test.ts` pins that the seam ACCEPTS both keys — an
* assertion that stays green whether they are held HERE or declared THERE, so
* it could not have caught this. The column-level twin IS guarded
* (`columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does NOT declare
* `pinned`), and that is the shape a guard for this type would take. #7196
* files it as a separate finding; ⛔ do not add it as a rider here.
*/
export type ObjectGridDataTableSchemaHolds = {
/** HELD (objectui#6459) — `data-table` calls it to render a host cell editor. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* calls it to render a host cell editor; returning `null` falls through to the
* built-in text / number / date inputs.
*/
renderCellEditor?: (ctx: {
column: any;
row: any;
Expand All@@ -767,7 +861,13 @@ export type ObjectGridDataTableSchemaHolds = {
commit: (v?: any) => void;
cancel: () => void;
}) => React.ReactNode;
/** HELD (objectui#6459) — `data-table` folds it into every body cell's class. */
/**
* REDUNDANT since objectui#6882 (2026-08-30) — `DataTableSchema` declares this
* key itself now, with a shape measured `Equal` to this one. `data-table`
* folds it into the three UTILITY body cells (selection, row-number,
* row-actions) and never into a data cell, which folds
* `TableColumn.cellClassName` instead.
*/
cellClassName?: string;
};

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,15 +130,26 @@ describe('objectui#6459 — the schema slot annotation is an instrument, not a d
});

/**
* The two HELD schema-level keys — the whole census, measured on `38a123cac`
* by diffing the 46 flat-literal keys (+ the 8 group-literal keys) against
* `DataTableSchema` + `BaseSchema` declared members. Each has a live reader
* in `data-table.tsx` (`renderCellEditor` via its `(schema as any)` cast,
* `cellClassName` via destructuring into every body cell's class), so the
* seam must ACCEPT them; whether `DataTableSchema` should DECLARE them is
* the ruling this card files, not this suite's call.
* The two schema-level keys that were the whole census, measured on
* `38a123cac` by diffing the 46 flat-literal keys (+ the 8 group-literal
* keys) against `DataTableSchema` + `BaseSchema` declared members.
*
* ⚠️ They are no longer HELD, and this assertion is why nobody noticed:
* objectui#6882 (2026-08-30) DECLARED both on `DataTableSchema`, and the
* assertion below stays green either way. It pins that the seam ACCEPTS the
* two keys — true while they are held here, equally true once they arrive
* through `DeclaredDataTableSchema`. An acceptance pin cannot express a hold's
* ENTRY CONDITION, so this suite could never have gone red at the moment of
* loss. Re-derived by objectui#7196; the diff now leaves ZERO undeclared keys,
* and `ObjectGrid.tsx`'s `ObjectGridDataTableSchemaHolds` carries the full
* record. The guard that WOULD have caught it is the column-level twin's:
* `columnHoldsExpiry-6424.test.ts` asserts `TableColumn` does not declare
* `pinned`. Filed by #7196 as a separate finding, ⛔ deliberately not a rider.
*
* The assertion itself keeps its value unchanged: both keys must remain
* writable at this seam, whichever side declares them.
*/
it('accepts the two held keys — renderCellEditor and cellClassName', () => {
it('accepts both schema-level keys — renderCellEditor and cellClassName', () => {
const held: ObjectGridDataTableSchema = {
type: 'data-table',
columns,
Expand Down
Loading