fix(app-shell,plugin-view): relay a per-view rowColor to ListView (objectui#7218) - #7558
Merged
Merged
Conversation
…objectui#7218)
`rowColor` is a declared `ListViewSchema` member (by reference from
`@objectstack/spec`, `{ field, colors? }`) that `ListView` reads to seed its
`rowColorConfig` state. Neither object-view host relayed it, so an authored
per-view row colour was unreachable on that route: authored, validated, built
and served, then dropped at the relay, with no error and every authoring gate
green. Same "declared and inert" shape objectui#7199 fixed for `description`.
The interface route (`InterfaceListPage.tsx`) has shipped
`rowColor: view.rowColor` next to `grouping`/`pagination` since ADR-0047, with
no fence — so the delivery path already existed and two of three hosts simply
did not use it. The legacy shorthand for the same feature (bare `color`) already
had a rung in both literals; only the canonical spelling was missing.
Both rungs are view-sourced only. Neither adds a cast read off the object-view
node, which would have put a 28th name on the objectui#5097 HOST-COMPOSITION
exemption the 2026-08-18 ruling fixed at 27 — a ruling, not a refactor.
`grouping` is the in-fence precedent for a view-only rung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-project-manager
marked this pull request as ready for review
September 3, 2026 21:24
os-project-manager
enabled auto-merge
September 3, 2026 21:24
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 free
to 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 #7218
Scope of record:
rowColoronly. The card's TITLE names two keys and is outof date; the authority is triage comment
5528535616
(R+122, 2026-09-03), which split the card and says so in terms: "Scope now:
rowColoronly.fieldOrdermoved to #7516 … This comment is the scope ofrecord."
fieldOrderis untouched here — it is #7516's, it isneeds-user-decision, and it got the opposite answer (no producer on any host,and
columnsalready expresses order), so it needs a ruling rather than arelay. Nothing in this branch touches it.
Clause-②: no
What was wrong
rowColoris a declared member ofListViewSchema— imported by reference from@objectstack/spec(RowColorConfigSchema, shapefieldplus an optionalcolorsmap) — andListViewREADS it: it seedsrowColorConfigstate fromschema.rowColorand hands it to the grid, which colours whole rows from thenamed field's value.
Neither object-view host relayed it:
rowColorrungapp-shellObjectView.renderListViewfullSchemaplugin-viewObjectViewobject-view HOST-COMPOSITION SURFACEfenceapp-shellInterfaceListPageSo an authored per-view row colour was unreachable on the object route:
authored, validated, built and served correctly, then dropped at the relay.
Nothing errored, every authoring gate passed, and the only symptom was that the
rows were not coloured — an author cannot notice short of diffing the DOM. Same
"declared and inert" shape as #7199's
description, whose PR is the shape thisone follows.
Why this is mechanical, and not an intent question
The card concluded
rowColorwas "authorable metadata with a live reader andno delivery path". That conclusion does not hold: the triage measured a THIRD
host, and it already relays the key.
packages/app-shell/src/views/InterfaceListPage.tsxrelays
rowColor: view.rowColornext togroupingandpaginationinto aschema typed
ListViewSchema, and has since ADR-0047. There IS a deliverypath — two of three hosts simply did not use it.
Re-measured on the tree this branch cuts from (
origin/main045d20ba8, thetriage read
0e3b3be), and the precedent still stands atInterfaceListPage.tsx:465.One measurement of my own strengthens it: the LEGACY shorthand for this same
feature — bare
color, whichlist-view-spec-parityrecords as "legacyrow/text coloring shorthand (spec-canonical:
rowColor)" — was alreadyrelayed by both host literals. Only the spec-canonical spelling was missing. The
hosts were not withholding row colour; they were carrying only its deprecated
name.
Stop-condition: searched for, not found
The brief said to stop and report if the object-view route deliberately
excludes this key. Four independent readings, all negative:
rowColoras excluded anywherein the repo — a full-tree grep of the identifier returns the type
declarations, the
ListView/ObjectGridreaders, theInterfaceListPagerelay, the i18n keys, and the
userActionstoggle. No fence.git log -S rowColoron both host files returns ZERO commits. The keywas never relayed and never removed — it was simply never added. A deliberate
exclusion would have left a deletion.
reads off the object-view NODE — the 27 undeclared keys the 2026-08-18 ruling
exempted. A view-sourced rung adds no cast read, and
groupingis thein-fence precedent for exactly that shape.
rowColoris published authoring surface elsewhere already — theobject-gridregistration declares it as an input, with the description"Rules that colour whole rows from a field value."
The change
Two rungs, one per host, both inside the existing region markers where markers
exist:
plugin-view/src/ObjectView.tsx—rowColor: activeView?.rowColor,inside thefence, next to the legacy
colorrung. 46 keys becomes 47.app-shell/src/views/ObjectView.tsx—rowColor: viewDef.rowColor ?? listSchema.rowColor,in
fullSchema, the same two-rung shape as the neighbouringdescription.47 named keys becomes 48.
⛔ Both rungs are view-sourced only, and that is load-bearing. Neither adds a
cast read off the object-view node. Such a read would put a 28th name on the
objectui#5097 HOST-COMPOSITION exemption whose count the 2026-08-18 maintainer
ruling fixed at 27 — a ruling, not a refactor. A pin in the new
plugin-viewtest asserts the node-level key stays unreachable, so the shortcut cannot be
taken later without a test going red and naming the ledger.
On the two traps carried from the triage
Trap 1 — the key count.⚠️ One half of this did not survive re-measurement,
and the correction matters for anyone reading the triage later. Only
plugin-view's literal is fenced byobject-view HOST-COMPOSITION SURFACEmarkers;
app-shell'sfullSchemahas NO region markers at all (grepfor themarker returns only
plugin-viewsources and its ledger test). And the twoliterals do not carry the same count:
plugin-viewcarries 46, matching thetriage, but
app-shellcarries 47 named keys plus the host spread, not 46.Measured with a brace-depth scan of each literal, before and after.
No gate hand-copies either count. The one ledger that counts anything —
plugin-view/src/__tests__/objectViewHostSurface.test.tsx— derives its set fromthe fence at test time and pins the CAST reads (27 exempt + 4 declared), not the
literal's key count, so a view-sourced rung leaves it untouched. It is green,
and is included in the runs below precisely because a red there would have been
the #7448 shape.
Trap 2 — #5435. Confirmed independently and NOT held behind it. That card
concerns
userActions.rowColor, a boolean permission toggle thatnormalizeListViewSchemafolds from the legacyshowColorflag. This cardconcerns top-level
rowColor, the row-colour configuration. "May the user openthe colour panel" versus "what the colours are" — same name, different nesting
level, different key. The last case in the new
app-shellpin authors thetoggle OFF alongside a colour config and asserts both survive independently, so
a future crossed wire fails instead of passing.
Scheduling. The earlier reading that PR #7332 touches
ListView.tsxwasre-checked and is moot either way: this branch does not touch
ListView.tsx.Verification
All runs from the repo root, at the final commit
79e31a597.Measured in both directions. The pins were written and run BEFORE the fix
existed, so the before-state is a measurement rather than a claim:
plugin-viewpin, before:2 failed | 3 passed—expected undefined to deeply equal { field: 'stage', colors: … }app-shellpin, before:4 failed | 2 passed, includingexpected { field: 'priority', … } to deeply equal { field: 'stage', … }—the host-supplied value arriving where the view's own was expected, which is
the defect stated as an assertion.
Reverse verification, on the committed tree, each rung deleted in turn:
plugin-viewrung2 failed | 17 passedgit diff HEADemptyapp-shellrung4 failed | 2 passedgit diff HEADemptyEach leg asserted the anchor was present exactly once before mutating and absent
after, and each restore was proven by comparing the worktree blob hash against
the HEAD blob hash AND requiring an empty
git diff HEAD— never by an exitcode. No build or
distis involved: this repo's vitest config aliases everypackage specifier to its
src, so there is no stale-artifact leg to preflight.Green at
79e31a597:pnpm exec vitest run packages/plugin-view/—Test Files 34 passed,Tests 297 passedpnpm exec vitest run packages/app-shell/src/views/—Test Files 370 passed,Tests 3535 passed | 1 skippedpnpm --filter @object-ui/plugin-view type-check— clean; echoedtsc --noEmit && tsc -p tsconfig.test.jsonpnpm --filter @object-ui/app-shell type-check— clean, same two-program scripttsc -p tsconfig.test.json --listFiles(1 hit each), so the green covers them rather than merely excluding themeslinton the four changed source files: 0 errors (warnings are thepre-existing
no-explicit-anypopulation every sibling test carries)pnpm --filter @object-ui/plugin-view --filter @object-ui/app-shell lint— 0 errorscheck:control-bytes,check:vi-mock-specifiers,check:vi-mock-inherit,check:phantom-deps,check:self-import,check:changeset-no-major— all OKcheck:governed-queue-guard --teston the five changed paths: NOT GOVERNEDNarrowings, declared
pnpm lint(turbo run lint) was not run locally; the twoaffected packages were linted in full instead, which is the same unit turbo
invokes for them. Safe to narrow here because this repo enables NO type-aware
linting (no
projectServiceand noparserOptions.projectanywhere ineslint.config.js), so a change confined to two files cannot move the verdicton a file it does not touch, and no rule block is scoped to either changed
path. What it could have caught and did not: a lint regression in a package
this diff does not touch. CI runs the full farm regardless.
check:*farm was not enumerated locally. The aggregatepnpm checkneeds the CLI built; the gates whose inputs this diff actuallymoves are listed above and are green. What this could have caught: a gate
keyed on a path class I did not recognise as implicated.
plugin-list/plugin-gridtests were not re-run. No file in eitherpackage changed, and vitest resolves package specifiers to
src, so norebuilt artifact can reach them. The consumer half — that a
rowColorwhichARRIVES then colours a row — is
useRowColor's, already covered there, and isnot re-pinned here; this PR pins the relay, exactly as A list view's
descriptionis served but never rendered — ObjectView's relay drops it on the way to ListView #7199 split the sametwo halves.
both pins capture the schema at the boundary rather than rendering it.
Reviewer notes
⛔ Draft, per the dispatch: not marked ready, no auto-merge, no self-review.
#7516 is NOT addressed here and remains open — it is the other half of the split
card and is awaiting a ruling. #5435 also remains open and is neither a
duplicate of this nor a blocker on it, for the reason measured above.
🤖 Generated with Claude Code
https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Generated by Claude Code