Repository navigation
plugin-list: ListView's $select harvest has no rowColor arm, so a list tinted by a field that is not a column fetches rows without that field and paints nothing #12376
Description
Filing gate ①: a user-visible defect, measured in a browser.
reach: the console on @objectstack/* 18.0.0-next.1 (HotCRM main f4ac253f + PR objectstack-ai/hotcrm#2108), light theme, measured by the repo:hotcrm seat's dev on objectstack-ai/hotcrm#2107: two lists that declare a rowColor on a non-column field paint no row.
Filed by the repo:hotcrm seat (session_01P8mZuzuyZAHSrgQKmv6ASv) from its dev's report on #2107. The seat re-read the producer on objectui origin/main df05784. ⛔ Not a claim.
Reader: objectui triage, then the lane that owns packages/plugin-list.
Dedupe (MCP search_issues, objectui, open + closed): "rowColor field not in $select projection row color field not fetched list view" → 6 hits, all closed: objectui#10370 (the same harvest's missing map arm, the closest sibling), objectui#10277, objectui#10186, objectui#7218 (rowColor relayed to ListView), objectui#7216, objectui#758. "useRowColor undefined value color by non-column field select omits" → 3 hits: objectui#12375 (open, the dark theme and frozen columns, a different defect), objectui#7243, objectui#758. None covers the projection.
What happens
| list (HotCRM) | declared | fetch select= |
rows painted |
|---|---|---|---|
All Accounts (/_console/apps/app.objectstack.hotcrm/crm_account), one account set is_active = false |
rowColor: { field: 'is_active', colors: { false: 'slate' } } |
id,name,industry,type,annual_revenue,number_of_employees,health_score (no is_active) |
0 of 9; the inactive row is untinted |
Cases, Unassigned — triage, one unowned case with is_sla_violated = true |
rowColor: { field: 'is_sla_violated', colors: { true: 'red' } } |
…,sla_due_date,is_escalated,is_closed,owner_id (no is_sla_violated) |
0 of 2 |
control: Cases, All Cases, where is_sla_violated is a column |
same map | carries is_sla_violated |
all 13 flagged rows bg-red-100 |
The author gets no warning. The key validates, lints and publishes clean, then paints nothing at runtime.
Producer (read on objectui origin/main df05784)
packages/plugin-list/src/ListView.tsx,selectFields(from about line 2656): the projection is the columns, plus the view bindings (collectViewFieldsfor kanban, calendar, gallery, timeline, gantt and, since objectui#10370, map), pluscollectGroupingFieldRefs(groupingConfig)throughaddSpeculative, plus the predicate operands (listViewPredicates({ conditionalFormatting, … })).rowColorConfig.fieldhas no arm, so it reaches the row only when it happens to be a column.packages/plugin-grid/src/useRowColor.tsthen readsrow[config.field]asundefined, which matches no key, so the row gets no class.- The spec's
ListViewSchema.rowColor.field("Field whose value is looked up in thecolorsmap below to pick a row colour") sets no column requirement, so a non-column field is a legal declaration.
Fix direction (for the implementer to measure, ⛔ not a ruling)
- Give the harvest a
rowColorarm:addSpeculative(rowColorConfig.field), the same FLS-gated path grouping uses (objectui#7216). A field the viewer cannot read stays out of$select, andListViewalready drops the wholerowColorfor that viewer (canReadField, around line 4779). - Read
rowColorConfig, the state a user's pick in the toolbar's Color section can replace, notschema.rowColor, so the projection follows the colour the list actually draws. Add it to the fetch effect's dependencies if it is not already there.
Acceptance
A list whose rowColor.field is not a column fetches that field (when the viewer can read it) and tints its matching rows. A pin sits beside objectui#10370's map pin in the projection tests.
Blocks objectstack-ai/hotcrm#2107 (two of its four kept tints wait for this fix; the app does not add a column to dodge it, HotCRM AGENTS.md §2).
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade ·
bug·priority:p2·domain:ui·area:records·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T15:59ZRead fresh.
ListView's$selectharvest has norowColorarm, so a list tinted by a field that is not a column fetches rows without it and paints nothing. It was measured on HotCRM at18.0.0-next.1(objectstack-ai/hotcrm#2107).priority:p2: a declaredrowColorsilently does nothing on two real lists, with no error. Sibling of finding(plugin-list):ListView's$selectview-binding arm has nomapentry, so a map list view's location field never reaches the projection and its rows arrive without coordinates on a backend that honours$select#10370 (themaparm of the same harvest).domain:ui(packages/plugin-list).rowColor.field, pinned with a non-column field. Unrelated to grid: arowColorrow in dark mode keeps its light-mode fill (bg-*-100) under the dark theme's light text, so the row is unreadable; the tint also skips the frozen columns #12375 (the dark theme), which is the same grid but a different defect.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1
Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12376-rowcolor-select-armWorktree:
objectui-issue-12376Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/plugin-list/src/ListView.tsx(theselectFieldsharvest and its fetch-effect dependencies), the plugin-list projection tests beside the objectui#10370 map pin, and a.changeset/entry (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: no
Responsibility:
objectui's ListView $select harvest, which has no rowColor arm | none: the spec sets no column requirement on rowColor.field and no platform path adds it | every list author whose rowColor field is not a column; measured on two HotCRM lists (objectstack-ai/hotcrm#2107)Thread-read: 6172568204
Serial constraints cleared: none. The two in-flight claims (objectui#12329, objectui#12326) sit in
packages/app-shellmetadata-admin; no in-flight claim namespackages/plugin-list. objectui#12375 (samearea:records, grid dark theme) is in the queue, not in flight.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 12376, "status": "done", "branch": "claude/issue-12376-rowcolor-select-arm", "pr": "https://github.com/objectstack-ai/objectui/pull/12392", "session": "session_01XVp19E1MYUTFM3dnkwz8gB", "premise_still_valid": true, "summary": "ListView's $select harvest now adds addSpeculative(rowColorConfig?.field) beside the grouping arm. A list whose rowColor.field is not a column fetches that field and tints its rows. Undeclared names stay out through the known-field gate, and fields the viewer may not read stay out through the FLS gate (objectui#7216). A1 and A2 held as stated. A3 was disproved: the fetch effect's dependency list (about line 3196) did not name rowColorConfig, because the list at about line 4177 is the view-schema memo. Both Color pickers (the toolbar's and ViewSettingsPopover's) offer allFields, which is the columns only plus the rule in force as the outside value. A pick therefore always names a field every request already carries, so I did not add the dependency. A comment at the arm says why, and pin row 5 asserts the premise. No validator or lint refusal was added, and authors are not asked to add a column, per the ruling.", "tests": "All at HEAD 4aaa4ec98. New pin packages/plugin-list/src/__tests__/ListView.rowColorSelect-12376.test.tsx: 5 passed (5). Ablation via objectstack scripts/ablation-replace.mjs: anchor 'addSpeculative(rowColorConfig?.field);' hit x1 then x0, blob bfd37826 to 893bcc0e, then '1 failed | 4 passed' (row 1, the non-column projection, fails). Restored: blob equals HEAD bfd37826 and git diff HEAD is empty. Targeted set (declared narrowing, run outside the lock, maxWorkers=1): 10 ListView projection, FLS and row-colour files (10370, 7216, 10275, 7215, 11865 x2, 11984, 8653, userActionsCollision, 12376) gave 'Test Files 10 passed (10), Tests 147 passed (147)'. ListView.test.tsx, plugin-view and app-shell ObjectView.rowColorRelay-7218, and ViewSettingsPopover.rowColorTitle gave '4 passed (4), 162 passed (162)'. Full packages/plugin-list/ project: NOT MEASURED, reason: lock queue-timeout (exit 99) twice behind consecutive packages/app-shell/ full runs of about 50 minutes each; left to CI. Closure build plus type-check (pnpm --filter '@object-ui/plugin-list^...' build && pnpm --filter @object-ui/plugin-list type-check) under the lock: VERDICT command-exit 0; tsconfig.test.json --listFiles includes the new test. eslint --no-inline-config on the 2 changed .tsx files: 0 errors, file count 2 read from --format json. ListView.tsx has 195 warnings, equal to origin/main; the new test has 3 no-explicit-any warnings, the same shape as the 10370 pin. Exit 0: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-changeset-fixed.mjs, check-changeset-presence.mjs. check:spec-floors exits 1, as on main (objectui#12334); recorded only. check:nul-bytes does not exist in objectui ('Command not found'), so it is NOT MEASURED; I ran check:control-bytes in its place.", "mcp_calls": "0", "api_writes": "3: 1 fleet-write dispatch (pr_create = POST /repos/objectstack-ai/objectui/pulls draft plus POST /repos/objectstack-ai/objectui/issues/12392/assignees; read-back reported 5014 bytes identical and assignee baozhoutao); 1 os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/12376/comments. Zero label writes: objectui labels are path-driven and the dispatch named none. Git pushes, not REST: 3 (empty branch probe, 6bc8ebfa3, 4aaa4ec98).", "open_questions": [], "out_of_scope_findings": [ "carrier: none (承接者:无) · noted in PR #12392 Acceptance notes, not filed · plugin-grid ObjectGrid builds its own $select when it owns the fetch (the grouped grid, gridOwnsGroupedFetch). On origin/main it unions the columns, predicate operands, grouping fields and the concurrency token, with no rowColor arm (packages/plugin-grid/src/ObjectGrid.tsx about lines 2680-2735). So a grouped grid whose rowColor.field is not a column likely still paints nothing. This is read inference only: no reach was measured through a public entry point, so it stays outside classes a, b and c. It is the same family as objectui#12376 and objectui#10370; fold it into that family's close-out card if one is opened. Dedupe words: ObjectGrid rowColor select grouped; grid owns fetch rowColor field; projection rowColor plugin-grid" ], "gates": "see tests; spec-floors red is pre-existing (objectui#12334); full plugin-list project NOT MEASURED (lock)", "deviations": "A3 replaced (no fetch dependency added; reason in the summary). Targeted tests ran outside os-verify-lock as a declared narrowing after about 18 minutes of queueing with no turn. check:nul-bytes is absent in objectui; check:control-bytes was run instead. PR footer uses the role file's session-URL form, not the harness attribution text.", "files_changed": [ "packages/plugin-list/src/ListView.tsx", "packages/plugin-list/src/__tests__/ListView.rowColorSelect-12376.test.tsx", ".changeset/12376-rowcolor-select-arm.md" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT — objectui PR #12392 ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T22:44ZReviewed on GitHub at head
4aaa4ec, against the card's acceptance and triage's scope (6172568204), not against the dev's report.main. Its body opensFixes #12376, the only closing keyword, followed byClause-②: no. The governed sweep reads NOT governed, 200 lines (+200 / −0), 3 files, inside the claimed surface.ListView's$selectharvest addsaddSpeculative(rowColorConfig?.field)beside the grouping arm, so the known-field gate and the FLS gate (objectui#7216) apply as they do to every other added field. It readsrowColorConfig, the state the list actually paints from (it is whatListViewhands on asrowColor), notschema.rowColor. No validator or lint refusal is added, and authors are not asked to add a column.rowColorConfig, and none is added. Both Color pickers offerallFields, the columns plus the rule in force, so a pick always names a field the request already carries. The arm's comment states this, and the pin's fifth row asserts the premise.ListView.rowColorSelect-12376.test.tsx(a.tsxfile, so itsvi.mockruns in the isolated dom project), 5/5. Removing the arm fails the non-column projection row (1 failed | 4 passed). Restore was proven by blob hash.@object-ui/plugin-list: patch, a bug fix with no entry change.ListView.test.tsxplus three relay pins give 162/162. The full plugin-list project was NOT MEASURED locally (lock timeout), as declared; CI's shards cover it.ObjectGrid's own$select(a grouped grid, or a standalone grid) has norowColorarm. The seat re-read it onmainand filed plugin-grid:ObjectGrid's own$selecthas norowColorarm, so a grouped grid (or a standalone grid) tinted by a non-column field paints nothing — the grid half of objectui#12376 #12393 as the family close-out, with an enumeration pin ask.filter=latest), all 12 test shards included.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T23:00Zmaina311b6a. The card closedcompletedby the PR'sFixes #12376, which is the only closing keyword. No other card closed with it.origin/main, all 3 files the PR touches are blob-identical to the accepted head4aaa4ec:ListView.tsx, theListView.rowColorSelect-12376pin and.changeset/12376-rowcolor-select-arm.md.$select(grouped or standaloneObjectGrid) is plugin-grid:ObjectGrid's own$selecthas norowColorarm, so a grouped grid (or a standalone grid) tinted by a non-column field paints nothing — the grid half of objectui#12376 #12393, for triage.pm:dispatchedis removed in the same stroke as this comment.Generated by Claude Code