Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

ObjectGrid's column width/order persistence never fires — saveColumnState's only two call sites hang off onColumnResize/onColumnReorder, which data-table.tsx never invokes #6175

Closed

Description

@yinlianghui-tw

Filed unassigned, no pm:* label — triage owns grading and routing. Found while measuring the callback subset of #6152 (the 23 on* keys in UnmirroredDeclared); out of scope there and deliberately not fixed in that round.

The defect

ObjectGrid's column-state persistence has a fully wired inbound half and a dead outbound half.

Inbound (works, and is pinned): ObjectGrid.tsx:632-659 seeds columnState from (schema as any).columnState, falls back to localStorage, and re-syncs on external change. The docblock at :610-630 records the 2026-08-18 maintainer ruling on #5091 that this is user-state, not authoring surface, and __tests__/gridNonAuthorKeys.test.tsx pins it.

Outbound (dead): saveColumnState (ObjectGrid.tsx:661) writes localStorage and notifies the host via onColumnStateChange, which app-shell/src/views/ObjectView.tsx:1867 turns into dataSource.updateViewConfig. It has exactly two call sites, both in the same object literal:

packages/plugin-grid/src/ObjectGrid.tsx:3084   onColumnResize:  (columnKey, width) => { saveColumnState({ ...columnState, widths: {...} }) }
packages/plugin-grid/src/ObjectGrid.tsx:3090   onColumnReorder: (newOrder)         => { saveColumnState({ ...columnState, order: newOrder }) }

Both are written into const dataTableSchema: any = { … } (:2937) and handed to the renderer via SchemaRenderer schema={dataTableSchema} (:3735). packages/components/src/renderers/complex/data-table.tsx invokes neither.

  • onColumnResize — zero occurrences in data-table.tsx. Repo-wide, the only non-declaration, non-comment occurrence is the ObjectGrid producer above.
  • onColumnReorder — zero occurrences in data-table.tsx. The renderer calls the near-duplicate key onColumnsReorder (with the s) at data-table.tsx:1346-1347, and that is a different declared key with a different signature: DataTableSchema declares both onColumnReorder?: (newOrder: string[]) => void (data-display.ts:796) and onColumnsReorder?: (columns: TableColumn[]) => void (data-display.ts:718).

Net user-visible effect: a user drags a column wider or reorders columns, and nothing is persisted — not to localStorage, not through onColumnStateChange to dataSource.updateViewConfig. The saved state is read back correctly forever; it is simply never written.

packages/plugin-grid/CHANGELOG.md:713 states the intent that this contradicts: "the table vocabulary reports per-event onColumnResize / onColumnReorder rather than the merged { order, widths } layout this reports."

Why it stayed invisible

Same class as #6004 and #5453, and the same enabling cause: the handoff object is untyped. #6004 is about generateColumns() returning any[]; this is its sibling one level up — const dataTableSchema: any at ObjectGrid.tsx:2937. With any on the producer side, writing a key the consumer never reads is not a type error, and DataTableSchema declaring both spellings means even a typed producer would have accepted the wrong one.

Ruled out before filing: no dynamic handler dispatch in data-table.tsx (no schema[...] indexing, no Object.keys(schema) walk), and data-table.tsx has no column-width/order persistence of its own.

Suggested shape of a fix (not decided here)

Two independent questions, and they should probably be answered together:

  1. Which spelling survives — onColumnReorder(newOrder: string[]) or onColumnsReorder(columns: TableColumn[])? Two declared keys for one event is itself the bug that let this land; one of them should be retired under the usual route.
  2. Wire onColumnResize in data-table.tsx (the resize handler currently persists nothing), or move persistence to the merged onColumnStateChange payload the CHANGELOG describes.

Repro is static — the grep above is the whole reproduction. A behavioural repro would be: drag a column in any object-grid, reload, observe the width is not retained.

Refs: #6152 (where this was found, and where it is out of scope) · #6004 / #5453 (same untyped-handoff class) · #5091 (the ruling that columnState is user-state, not authoring surface).

Activity

  1. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    ContributorAuthor

    Lane note from the domain:devx @ objectui seat (#5748) — ⛔ not claiming it, and one measurement to carry

    PM session session_019b5UBNMtTzKbVtZZGvFuxe. Recording this so the card does not sit unrouted and so the next reader does not re-derive the boundary call.

    ⛔ Why this seat is not taking it

    The fix lands in packages/components/src/renderers/complex/data-table.tsx — a renderer, not gate tooling. This seat declined #6151, #6157, #6165 and #6167 tonight on that same boundary. The enabling cause the card identifies (const dataTableSchema: any at ObjectGrid.tsx:2937, the untyped handoff, same class as #6004 / #5453) is squarely this lane's kind of problem — but the defect cannot be closed without changing what the renderer calls, so splitting off the typed-handoff half alone would leave the user-visible bug live and claim progress.

    Grading and routing are triage's. Recording severity: the user-visible effect is real — a user drags a column wider or reorders columns and nothing persists, not to localStorage and not through onColumnStateChange to dataSource.updateViewConfig. The saved state is read back correctly forever; it is simply never written.

    ⭐ One reading that narrows the card's first question

    The card asks which spelling survives — onColumnReorder(newOrder: string[]) or onColumnsReorder(columns: TableColumn[]) — and leaves it open. Independent evidence landed tonight that bears on it:

    The live, working path is onColumnsReorder. data-table.tsx:1346-1347 calls it; onColumnResize and onColumnReorder have zero occurrences in that file. So one spelling is wired end to end and the other is the dead producer-side one. That is not a decision — retiring a declared key is still a contract change and still needs the usual route — but it means the two spellings are not symmetric candidates, and whoever rules should start from the one the running code already uses.

    ⚠️ Cross-reference worth having: PR #6181 (card #6152, armed tonight) records this exact near-duplicate in the new RuntimeOnlyDeclared ledger as "one event, two declared spellings, different signatures … an open ruling and deliberately not settled here." Both onColumnReorder and onColumnsReorder are declared on DataTableSchema (data-display.ts:796 and :718). This card is where that ruling belongs — the ledger deliberately deferred it here, so settling it in passing on some other card would strand a written-down deferral.

    ⛔ Note that #6181 reclassifies these keys, it does not repair them. onColumnResize / onColumnReorder remain in the ledger as runtime-only declared keys; nothing in that PR wires anything.

    Still open, and not this seat's to answer

    Whether to wire onColumnResize in data-table.tsx or move persistence to the merged onColumnStateChange payload packages/plugin-grid/CHANGELOG.md:713 describes. The card states both; I am not choosing between them from outside the lane.

    Unlabelled and unassigned by design — this is a routing note, not a claim.


    Generated by Claude Code

  2. os-zhuang commented on Aug 25, 2026

    @os-zhuang
    Contributor

    Triage: lands in packages/components/src/renderers/complex/data-table.tsx (wire the outbound half) with the producer side in plugin-grid/ObjectGrid.tsx; routed pm:queue + domain:ui, type Bug — user-visible (column width/order never persists). Scope constraint: the minimal fix wires persistence without retiring anything (honour the spelling ObjectGrid emits, or move to the merged onColumnStateChange payload the CHANGELOG describes). Retiring one of the two declared spellings (onColumnReorder vs onColumnsReorder) is a declared-surface change — if the fix cannot avoid it, stop and report rather than picking one silently; that half then goes through the usual retirement route. Same untyped-handoff class as #6004/#5453 (the dataTableSchema: any producer) — a typed handoff is the structural fix but is #6004's card, not a rider here.


    Generated by Claude Code

  3. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 25, 2026
  4. added theissue type on Aug 25, 2026
  5. self-assigned this
    on Aug 25, 2026
  6. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Contributor

    Claim: PM loop round 39
    Session: session_01CSoz9uGhaaSgiq3hshtN7L
    Branch: claude/issue-6175-column-state-persistence
    Worktree: objectui-issue-6175
    Domain: domain:ui
    Container & model: M, mode:subagent, model: opus
    Clause-②: conditional — ⛔ see the stop condition. Wiring the outbound half changes no declared surface; retiring one of the two spellings does, and that is not granted here.

    File surface: packages/components/src/renderers/complex/data-table.tsx (the outbound wiring) and packages/plugin-grid/src/ObjectGrid.tsx (the producer side), plus tests and a changeset.

    裁决 — triage (2026-08-25 00:26Z), binding

    lands in data-table.tsx (wire the outbound half) with the producer side in plugin-grid/ObjectGrid.tsx; pm:queue + domain:ui, type Bug — user-visible (column width/order never persists). Scope constraint: the minimal fix wires persistence without retiring anything (honour the spelling ObjectGrid emits, or move to the merged onColumnStateChange payload the CHANGELOG describes). Retiring one of the two declared spellings (onColumnReorder vs onColumnsReorder) is a declared-surface change — if the fix cannot avoid it, stop and report rather than picking one silently; that half then goes through the usual retirement route. Same untyped-handoff class as #6004/#5453 — a typed handoff is the structural fix but is #6004's card, ⛔ not a rider here.

    The defect, as measured

    The inbound half works and is pinned; the outbound half is dead. saveColumnState (ObjectGrid.tsx:661) writes localStorage and notifies the host via onColumnStateChange → ObjectView.tsx:1867 → dataSource.updateViewConfig. It has exactly two call sites (ObjectGrid.tsx:3084, :3090), both inside const dataTableSchema: any (:2937), handed to SchemaRenderer at :3735 — and data-table.tsx invokes neither.

    • onColumnResize — zero occurrences in data-table.tsx.
    • onColumnReorder — zero. The renderer calls the near-duplicate onColumnsReorder (with the s) at :1346-1347, a different declared key with a different signature: onColumnReorder?: (newOrder: string[]) => void (data-display.ts:796) vs onColumnsReorder?: (columns: TableColumn[]) => void (:718).

    ⇒ A user drags a column wider or reorders columns and nothing persists — not to localStorage, not through to updateViewConfig. The state is read back correctly forever; it is simply never written.

    ⭐ A reading from the domain:devx seat that narrows question 1

    The live, working path is onColumnsReorder. data-table.tsx:1346-1347 calls it; onColumnResize and onColumnReorder have zero occurrences in that file. One spelling is wired end to end and the other is the dead producer-side one.

    ⚠️ That is not a decision — retiring a declared key is still a contract change — but the two spellings are not symmetric candidates, and whoever rules should start from the one the running code already uses.

    ⚠️ And there is a written-down deferral you must not strand: PR #6181 (card #6152) records this exact near-duplicate in the new RuntimeOnlyDeclared ledger as "one event, two declared spellings, different signatures … an open ruling and deliberately not settled here" — deferring the ruling to THIS card. ⛔ #6181 reclassifies, it does not repair: onColumnResize / onColumnReorder remain runtime-only declared keys and nothing in that PR wires anything.

    ⇒ ✅ If your fix needs the ruling, stop and report — and say so explicitly, because a silent pick here strands #6181's deferral.

    PM mechanism assumptions — measure, do not inherit

    1. ⛔ Re-derive every line number. These were measured 2026-08-24 22:29Z and packages/plugin-grid has moved since — feat(core,data-objectstack,plugin-grid): consume the platform's per-column sortability signal #6109 (per-column sortability, withSortability in ObjectGrid.tsx) landed, and packages/components has taken PRs too. Report each site as you find it.
    2. ⚠️ Both halves must be proven, and the inbound one is the trap. The inbound path already works, so a test that reloads and sees a width could pass on a seeded fixture with the outbound half still dead. ✅ The pin must observe the write: localStorage actually written and onColumnStateChange actually fired. ⛔ "The width came back" is not evidence.
    3. ⚠️ The two-spelling hazard is live in your own fix. If you wire the producer's onColumnReorder by making data-table.tsx call it, you now have two call paths for one event. ✅ State which spelling the renderer invokes after your change and whether the other becomes reachable, dead, or unchanged.
    4. ⚠️ onColumnStateChange reaches dataSource.updateViewConfig — a write to shared view config. ⚠️ Note Implement the objectstack#7494 ruling: view config is explicitly org-wide — console wording/UX + permission-gated write path #5232 is in flight adding a permission gate to exactly that write path (PR feat(data-objectstack,app-shell,i18n): view config is explicitly org-wide — permission-gated write path + console wording #6125). ⛔ Do not touch updateViewConfig; ✅ if your change makes it fire where it previously never did, say so — that is a real interaction with a permission change landing in parallel.
    5. ⚠️ const dataTableSchema: any is the enabling cause and is ⛔ NOT yours to fix (finding(plugin-grid): ObjectGrid's generateColumns() is untyped (any[]), so nothing type-checks what it writes into DataTableSchema.columns: TableColumn[] — the hole that hid #5853 and #5453 #6004). ✅ Note in your report whether a typed handoff would have caught this, but ⛔ do not type it here.

    Verification

    ⚠️ Reproduce before you fix: show the resize/reorder producing no write today, red for a value reason. Predict the direction before running.

    ⚠️ Commit before you ablate. ⛔ git checkout <ref> -- <path> writes the INDEX as well as the worktree — reading a baseline that way and restoring with git checkout -- <path> restores the baseline over your work. Prove every mutation on disk by grepping injected and separately removed text; print the landing site; assert anchor uniqueness before writing; restore under trap … EXIT INT TERM with a cwd-independent command; confirm git diff HEAD --stat empty.

    ⚠️ Six phantom-class defects were self-caught by devs yesterday, all one root: an assertion or control that cannot distinguish the two states of the world — including a shared mock object that carried a previous test's write into the next case, and an entire harness whose two principals degenerated to the same value. ✅ State explicitly which of your assertions would still pass if your change were reverted, and make each control a string present in both versions of the file.

    Gates by name, with exit codes: type-check for @object-ui/components and @object-ui/plugin-grid (⚠️ hyphenated; echo the script name; distinguish unbuilt-closure red from missing-node_modules red; ⚠️ build ./examples/** too), root-form vitest quoting per-test names, the downstream prefix-form sweep with direction demonstrated, eslint with merge-base delta (⚠️ plain eslint .; ⛔ no --no-inline-config).

    ⚠️ Use pnpm exec vitest run [paths] from the repo root — ⛔ not pnpm --filter … test -- [paths], which silently drops everything after -- (guarded, objectui#3288). ⚠️ Zero-match pnpm trap: a filter matching nothing prints "No projects matched the filters" and exits 0. ⚠️ A cached turbo green is not a measurement — use --force on any turbo gate you treat as evidence. Run node scripts/check-changeset-presence.mjs rather than guessing. Changeset patch; ⛔ never major. ⚠️ Derive your gate set by enumerating each CI job's own step list, ⛔ not top-level script names.

    Deliverable

    Draft PR, body with Fixes #6175. ⛔ Do not mark ready, ⛔ do not enqueue, ⛔ no auto-merge — the PM lands it. ⚠️ Audit your PR body for closing keywords near any sibling issue number — GitHub's parser ignores negation. Post the terminal report here (plain-text first line os-dev-report; ⛔ avoid short angle-bracket placeholders) and return it. State per gate: command, exit code, what a red would have meant; every re-derived line number; which spelling the renderer invokes after your change; whether you needed the ruling (and if so, that you stopped); which of your assertions would still pass on a revert; and any deviation with its reason.


    Generated by Claude Code

  7. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Contributor

    os-dev-report

    {
      "issue": 6175,
      "status": "done",
      "branch": "claude/issue-6175-column-state-persistence",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6211",
      "premise_still_valid": true,
      "summary": "The outbound half of ObjectGrid column-state persistence is wired, and nothing is retired. Two independent breaks, one per package. (a) @object-ui/components: DataTableSchema declared onColumnResize (data-display.ts:791) and data-table.tsx invoked it NOWHERE — the resize drag updated local columnWidths state and stopped; handleResizeEnd now reports the settled width once at mouseup via a lastResizeWidth ref (the document listeners are one render's closures and cannot read a later state update). Once, deliberately: the host turns this into a write to shared view config, so a per-mousemove callback would be a write storm. (b) @object-ui/plugin-grid: ObjectGrid emitted onColumnReorder (singular) while the renderer invokes the near-duplicate onColumnsReorder; the producer now emits the spelling the renderer actually invokes, mapping the reported TableColumn[] to the accessorKey order columnState stores. Producer-side per AGENTS.md #0.1 (fix the producer, do not add a lenient alias in the consumer) — teaching the renderer to also call the singular spelling would give one event two live call paths and would make the deferred ruling strictly harder, converting a dead-key removal into a behaviour change. I did NOT need the two-spelling ruling and did NOT take it: packages/types/src/data-display.ts is untouched, both spellings stay declared, onColumnReorder stays declared and unwired, so PR #6181's parked deferral is left standing. Re-derived line numbers all still held at merge-base 2c8474c04 (saveColumnState :661, notify :669-670, dataTableSchema: any :2937, producer call sites :3084/:3090, SchemaRenderer :3735, renderer's onColumnsReorder at data-table.tsx:1346-1347, zero occurrences of onColumnResize and onColumnReorder in data-table.tsx, declarations at data-display.ts:718/:791/:796). One site the card did not name and that mattered: ObjectGrid.tsx:2992 sets reorderableColumns: schema.reorderableColumns ?? false, so grid reorder is OFF by default and the reorder test must enable it or it measures nothing. Spelling the renderer invokes after the change: onColumnsReorder — the same one it invoked before; the renderer's reorder path is unchanged. onColumnReorder is unchanged in reachability (still declared, still invoked by nothing); what changed is that ObjectGrid no longer EMITS it either, so it is now dead on both ends rather than written-but-unread. PERMISSION INTERACTION, as asked: onColumnStateChange reaches dataSource.updateViewConfig, and that path was unreachable from a column drag before this branch and is reachable now — a resize or reorder now produces a real write to shared view config where it previously produced none, so the gate in flight for #5232 (PR #6125) will see traffic on a path that had never carried any. updateViewConfig itself is untouched. On the untyped handoff (#6004, NOT touched): a typed handoff would have caught the reorder half and NOT the resize half — with a real DataTableSchema on the producer, onColumnReorder still type-checks because it is genuinely declared with the exact signature ObjectGrid used, so the two declared spellings defeat the type; the resize half is different, since no type system flags a consumer that never reads a declared optional key. Worth carrying to #6004.",
      "tests": "All gates below ran in worktree /home/user/objectui-issue-6175; every exit code captured directly (never after a pipe), and every verdict quoted from the gate's own output line. Final head a31843d79, tree clean; the ratchet-family gates and eslint were re-run on that head AFTER the last commit. REPRO BEFORE FIX (direction predicted first: both cases red for a value reason): `pnpm exec vitest run packages/plugin-grid/src/__tests__/columnStatePersistence.test.tsx` -> exit 1, 'Test Files 1 failed (1) / Tests 2 failed (2)', both failures 'AssertionError: expected \"vi.fn()\" to be called at least once' — a value reason, not a query or harness error (the DOM dump shows the header cells and the resize handle were found). AFTER FIX same command -> exit 0, 'Tests 2 passed (2)'. ABLATION (run AFTER committing the fix, per the dispatch): reverted ONLY the two source files to the merge-base with `git checkout $BASE -- <2 files>`, tests kept, restore `git checkout HEAD -- <2 files>` under `trap ... EXIT INT TERM` with cwd-independent `git -C`. Mutation proven on disk in both directions and separately: injected 'lastResizeWidth' count 0, injected 'onColumnsReorder: (newColumns' count 0, removed 'onColumnReorder: (newOrder' count back to 1; controls present in BOTH versions of each file — 'handleResizeEnd' 4 in data-table.tsx, 'saveColumnState' 4 in ObjectGrid.tsx. Result: exit 1, 'Test Files 2 failed (2) / Tests 3 failed | 1 passed (4)'. Restore verified: 'git diff HEAD --stat' empty. NO BUILD WAS NEEDED for the ablation and that is a measured condition, not an assumption: the root vitest.config.mts aliases '@object-ui/components' -> ./packages/components/src (line 264), so the plugin-grid suite exercises the renderer SOURCE, and a source revert is therefore visible to it. WHICH ASSERTION SURVIVES A REVERT (asked explicitly): exactly one of four — 'does not fire when the drag never moved' in data-table-column-resize-callback.test.tsx. The unrepaired renderer fires nothing at all, so 'did not fire' is trivially true there; it is a guard against over-firing, not a pin of the change. The test file now says so inline. The three that DO distinguish the two states of the world are 'fires onColumnResize once at mouseup...' and both columnStatePersistence cases. The pins observe the WRITE, never the read-back — localStorage actually written AND onColumnStateChange actually fired — because the inbound half already works and would answer a seed-and-reload test with the outbound half still dead. GATES: (1) `pnpm exec turbo run type-check --filter=@object-ui/components --filter=@object-ui/plugin-grid --force` -> exit 0, 'Packages in scope: @object-ui/components, @object-ui/plugin-grid', 'Tasks: 15 successful, 15 total'. This gate went RED first for a real reason and is the proof it was measuring: 'src/__tests__/columnStatePersistence.test.tsx(68,21): error TS6133: container is declared but its value is never read', exit 2 — an unbuilt-closure red would have failed the ^build tasks instead (14 of 15 succeeded, including every dependency build) and a missing-node_modules red would have failed before tsc ran at all; fixed by dropping the unused binding, then green. A red here would have meant my change broke a type contract in either package. (2) DOWNSTREAM PREFIX-FORM SWEEP: `pnpm exec turbo run type-check --filter='...@object-ui/components' --filter='...@object-ui/plugin-grid' --force` -> exit 0, 'Tasks: 66 successful, 66 total', 31 packages in scope. DIRECTION DEMONSTRATED, not asserted: `pnpm --filter '...@object-ui/components' exec pwd` lists 31 packages including plugin-grid, app-shell, console, cli, site and all four example apps (byo-backend-console, console-starter, hello-world, schema-catalog) = CONSUMERS; `pnpm --filter '@object-ui/components...' exec pwd` lists 9 including types, core, react, i18n = DEPENDENCIES. The prefix (leading-dots) form is the downstream one and is what ran, which is also how ./examples/** got built and type-checked. A red would have meant a consumer's types broke. (3) `pnpm exec vitest run packages/plugin-grid packages/components --maxWorkers=2` -> exit 0, 'Test Files 273 passed (273) / Tests 2516 passed (2516)'. (4) `pnpm exec vitest run packages/types --maxWorkers=2` -> exit 0, 'Test Files 58 passed (58) / Tests 632 passed (632)' — run because the RuntimeOnlyDeclared ledger names both keys; membership is unchanged (that ledger is about zod MIRRORING, not about being read), and the ledger file's own suite is green at 5 passed. (5) `pnpm exec eslint` over the 5 merge-base-delta paths, plain, no --no-inline-config -> exit 0, '276 problems (0 errors, 276 warnings)'; warnings are pre-existing in these large files plus three no-explicit-any in my new test, and package lint sets no --max-warnings. (6) `node scripts/check-control-bytes.mjs` -> exit 0, 'check-control-bytes: OK (scanned 5125 tracked text file(s); skipped 85 binary)', plus a separate self-scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over every changed file -> no match. (7) `node scripts/check-lint-coverage.mjs` -> exit 0, 'lint coverage: 46/46 packages linted, 0 with outstanding errors'. (8) `node scripts/check-type-check-coverage.mjs` -> exit 0, 'test type-check coverage: 41/41 packages compile their tests, 0 declared debt'. (9) `node scripts/check-changeset-presence.mjs` (run, not guessed) -> exit 0, '5 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'. (10) `node scripts/check-changeset-no-major.mjs` -> exit 0, 'No changeset declares a major bump'; the changeset is patch for @object-ui/components and @object-ui/plugin-grid. TURBO CACHE: every turbo gate treated as evidence ran with --force ('Cached: 0 cached, 15 total' on the run that reports it), so no cached green stands in for a measurement. ZERO-MATCH TRAP: each turbo run echoed its 'Packages in scope' list, so no filter silently matched nothing. NOT RUN LOCALLY, deliberately and declared: the whole-repo `pnpm lint` / `pnpm type-check` / sharded `pnpm test` farm — CI runs it exactly once and the PM reviews CI convergence; I ran the affected packages plus the demonstrated downstream consumer closure instead.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Two notes the JSON cannot carry cleanly

    Deviation from the declared file surface, declared. The dispatch named data-table.tsx, ObjectGrid.tsx, tests and a changeset. One file beyond that is touched: packages/types/src/__tests__/zod-mirror-parity.test.ts, a doc comment only — no assertion changed, no ledger membership changed, that file's 5 tests pass and @object-ui/types type-check is green (forced, 2/2). Reason: the RuntimeOnlyDeclared note stated that onColumnResize is "read NOWHERE" and that column-state persistence "never fires", citing this card by number as unrepaired. After this branch both sentences are false, and the note names this very card — leaving it would have left a documented falsehood pointing at its own repair, which is exactly the kind of stale ledger prose a later reader trusts. The note now restates the two-spelling residue as the deliberate open ruling it is. No changeset bump for @object-ui/types since the change is a comment; check-changeset-presence agrees (exit 0). Nothing in flight holds that file — #6181 landed, and no open PR touches it.

    The ruling was not needed, and is left exactly where #6181 parked it. Wiring persistence required no retirement: packages/types/src/data-display.ts is untouched, both spellings stay declared, and onColumnReorder stays declared and unwired. Which spelling survives remains open.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions