Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-grid): give a dropped ObjectGrid column an address instead of silence - #5456
Merged
os-support-ai merged 2 commits intoAug 21, 2026
Merged
Conversation
… of silence objectui#5068 made `ListColumnSchema`'s `field` / `label` the only column spelling `ObjectGrid` reads, which is right — but it relocated a failure mode instead of removing it. A column authored in a spelling the renderer does not read contributed nothing and NOTHING SAID SO: no error, no warning, no empty state, just a grid with its row-number column and no data columns. Renderer and author disagree, author gets a success receipt — the same shape #5068 exists to fix, one level down. An authored column that can never resolve now emits one `console.warn` naming the address rather than the symptom: which block, which object, which `columns[i]`, the keys that entry carries, and the rewrite that works. The drop decision moves into `resolvesToDataColumn` so the reporter and the filter that drops the column cannot drift apart — one predicate, two readers. The diagnostic reads the `columns` input and nothing else. It never asks whether the grid found ROWS: `object-grid` draws them from five different places (bare `data` array, `data.provider: 'value'`, legacy `staticData`, `bind`, and a host that owns the fetch and hands the window down as a `data` React prop), and a predicate consulting those would eventually paint a configuration error over a working grid. `hidden: true` is authored intent and is never reported. Rendering is unchanged in every measured case; the diagnostic is additive. Fixes#5349 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
…stic Found by this card's own reverse-verification. Ablating the `hidden` carve-out made `describeEntry` reach its tail with a well-formed, non-empty `field` and tell the author it was "an empty string" — the tail was the empty-string message with no length check under it. The check is now explicit, and the branch below it states only what it verified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-support-ai
marked this pull request as ready for review
August 21, 2026 00:10
Uh oh!
There was an error while loading. Please reload this page.
os-support-ai
deleted the
claude/issue-5349-objectgrid-silent-no-columns
branch
August 21, 2026 00:10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#5349
#5068 retired
ObjectGrid's undeclaredaccessorKey/headertolerance branch, soListColumnSchema'sfield/labelis now the only column spelling the rendererreads. That was right. What it left behind was the receipt: a column authored in a
spelling the renderer does not read contributed nothing, and nothing said so — no
error, no warning, no empty state. The author got a grid with its row-number column and
no data columns, i.e. a success receipt for a disagreement between renderer and author.
The same shape #5068 exists to fix, one level down.
An authored column that can never resolve now emits one
console.warnnaming theaddress rather than the symptom.
The diagnostic
Verbatim, from the
accessorKey-only leg of the test suite:Which block (the
view:gridalias is named asview:grid), which object and label,which
columns[i], the keys that entry actually carries, and the rewrite that works —built from what was authored, not from a template.
Channel.
ObjectGridalready had exactly one channel for "you declared it, therenderer dropped it": the export-format warning (
[ObjectUI] ObjectGrid export: unsupported format(s) hidden from the menu…) — auseEffectkeyed on the schema slice,one
console.warn, noNODE_ENVbranch. This is deliberately the same shape ratherthan a second, differently-shaped one next to it, which the card explicitly warned
against. A visible in-grid message would be user-facing copy needing all ten locale packs
in
@object-ui/i18n— outside this card's file surface — and a throw was rejected on thecard's own reasoning: a grid that renders nothing today would become a page that renders
nothing.
Measured matrix, before and after
Rendering is unchanged in every leg — the diagnostic is additive. Readings are the
literal
getAllByRole('columnheader')/getAllByRole('cell')output over two inlinerows (
#is the built-in row-number column,Openthe row affordance):[{field:'name'},{field:'amount'}]["#","Name","Amount"][{accessorKey:'name'},{accessorKey:'amount'}]["#"], no data cellscolumns[0]andcolumns[1][{field:'name'},{accessorKey:'amount'}]["#","Name"]columns[1]only[{accessorKey:'amount'},{field:'name'}]["#","Name"]columns[0]only['name','amount']["#","Name","Amount"][{field:'name',hidden:true}]["#"]columnskey["#","Id","Name","Amount"]columns: []["#","Id","Name","Amount"]The
hiddenrow is the one that decides the design. It renders identically to theaccessorKey-only row — header-only — and it is correct. So the predicate cannot be"the grid ended up with no columns".
What the predicate asks, and what it refuses to ask
It reads the
columnsinput and nothing else: the author wrote this entry and itcan never resolve — say which one. It never asks whether the grid found rows,
because
object-gridlegitimately draws them from five different places, and a"needs columns" predicate consulting any of them would eventually paint a configuration
error over a working grid.
All five are pinned by test, in both directions — clean columns must render and stay
silent, then the same configuration with one mis-spelled column must produce exactly one
diagnostic (that second leg is the counter-probe that makes the first leg's zero mean
something):
dataarraycolumnSpellingDiagnosticRender.test.tsxdata: { provider: 'value', items }staticDatabind— rows from the surrounding data scopedataReact prop (plugin-list'sListView)The host-prop case is why the tests pass the rows as a React prop with no
datakeyon the schema at all:
SchemaRendererspreads every unstripped schema key as a prop, soa bare
'data' in propswould be true of the schema's owndataobject too. Nothinghere tests
datapresence in either form; the predicate never looks.Column-side fallbacks that keep their silence, each also counter-probed: the
string[]spelling, an absent
columns, an emptycolumns, andhidden: true(including a hiddencolumn that also mis-spells its identity — it contributes nothing either way and the
author asked for nothing).
One predicate, two readers. The drop decision moved out of the inline filter into
resolvesToDataColumn, whichgenerateColumnsand the reporter now share, so thediagnostic cannot drift from the filter that drops the column. A test pins the extracted
predicate against the inline expression it replaced, case for case.
Reverse verification
Two legs, direction predicted before running. Both ablations are directly effective: the
tests import
../ObjectGridand../columnSpellingDiagnosticsas source, and vitestaliases every
@object-ui/*specifier topackages/*/src— nodistis involved oneither leg, and each ablation flipped results, which is itself the proof it reached the
code under test. Both were restored and the tree proved clean (
git status --porcelainempty).
Leg 1 — remove the emitter (
console.warn(message)in the effect).Predicted: the pure-module file stays fully green (it never renders); the render files
go red only where a diagnostic is asserted — 3 of 4 matrix legs, the
hiddencounter-probe, all 5 row-side counter-probes (9 of 13), plus 1 of 6 in the baseline file.
Observed:
Tests 10 failed | 35 passed (45)— exactly those ten, test for test.Leg 2 — remove the
hiddencarve-out (hidden columns counted as unresolved).Predicted: this direction is not "goes quiet" but more diagnostics — a false
positive over authored intent. 3 red in the unit file, 1 in the render file, baseline
untouched.
Observed:
Tests 4 failed | 41 passed (45), exactly those four, and the render failureis the predicted direction:
Leg 2 found a real defect in this PR's own message and it is fixed in the second
commit:
describeEntry's tail was the "fieldis an empty string" message with nolength check under it, so with the carve-out gone it told a well-formed column that its
non-empty
fieldwas empty. The check is now explicit and the branch below states onlywhat it verified. Unreachable today; a message must never assert something it did not
check.
Verification
Run on
57cda96a8(the final commit):pnpm exec vitest run packages/plugin-grid/— 84 files, 786 tests, all passingpnpm exec vitest run packages/plugin-list/— 43 files, 644 tests, all passing (the host that passes rows down as a prop)pnpm --filter @object-ui/plugin-grid type-check— clean (tsc --noEmit && tsc -p tsconfig.test.json)pnpm --filter @object-ui/plugin-grid lint— 0 errors (655 pre-existing warnings, the repo baseline; the gate is errors-only by design)check:control-bytes,check:spec-symbols,check:phantom-deps,check:self-import,check:i18n-keys,check:i18n-drift,check:i18n-dead-keys,changeset:check,check-changeset-presence.mjs— all passFile surface held: every source change is under
packages/plugin-grid/src/**, plus therequired changeset. No new user-facing copy, so no locale pack changes — the diagnostic
is developer-facing English on the console, which is also what commandment #-1 requires
of console messages.
The one updated test
columnDeclaredSpellingOnly.test.tsxis the baseline the card said to measure against,and its docblock claimed the drop "produces no console line". That is the sentence this
PR makes false, so it is rewritten rather than left as a stale claim. Its last case now
asserts the diagnostic and keeps pinning the escalation that was rejected: still no
throw, still no
alertrole. Every rendering assertion in that file is byte-identical.Generated by Claude Code
Generated by Claude Code