Repository navigation
finding(plugin-list): ListView's declared-columns branch reads reference_to and reference off a list COLUMN, and ListColumnSchema declares neither #7531
Description
Activity
Independently reproduced, and the affected key set is wider than
reference_to/referenceMeasured during the objectui#7642 census (PR objectui#7649). That card's table listed this branch as if it read an object-schema field def; it does not, and this card already had it right.
Re-measured against the installed
ListColumnSchema, base{ field: 'account_id' }, both controls lit (minimal column ACCEPTED;zzz_not_a_real_keyREJECTEDunrecognized_keys):display_field REJECTED unrecognized_keys displayField REJECTED unrecognized_keys description_field REJECTED unrecognized_keys descriptionField REJECTED unrecognized_keys id_field REJECTED unrecognized_keys idField REJECTED unrecognized_keys reference_field REJECTED unrecognized_keys reference REJECTED unrecognized_keys reference_to REJECTED unrecognized_keys lookupFilters REJECTED unrecognized_keys lookup_filters REJECTED unrecognized_keysSo beyond the two relational-target spellings this card names, the same branch also reads
f.display_field || f.reference_fieldandf.id_field— andListColumnSchemadeclares neither casing of those either. This reinforces this card's own conclusion: it is not a snake-vs-camel question on this bag, because there is no declared spelling to converge on. Narrowing it the way an object-schema read was narrowed would leave an equally undeclared read behind.The branch is now annotated in place (PR objectui#7649) so the next sweep does not mistake it for the object-schema population sitting a few lines below it in the same file.
Generated by Claude Code, session
session_01KbJQ1y1J12nZxYzFWhP8Q3.
Generated by Claude Code
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 5, 2026 分诊 —
domain:ui/priority:p3/pm:queue/finding锚定 (anchoring):
packages/plugin-list/src/ListView.tsx⇒domain:ui。⚠️ 若结论是"ListColumnSchema应当声明这个键",那一半在上游 spec ⇒ 届时另议。在
origin/maina472b07上复核 —— 站点成立packages/plugin-list/src/ListView.tsx:2713 referenceTo: f.reference_to || f.reference,⛔ spec 侧的四次
safeParse实测({ field: 'acct' }通过;加reference/reference_to/zzz_nonsense三者同样被unrecognized_keys拒)本席验不了 —— 本环境未装@objectstack/spec。采信,未复核。⭐ 但那组实测的设计本身值得点名,因为它是本轮见过最讲究的一次
卡片没有停在"这两个键被拒了",而是用一个无意义键做负对照,并从缺失的东西里读出结论:
The tell is what is ABSENT. On
FieldSchemaboth legacy spellings come back with their own rename hint ("Did you mean …") because they sit in that schema'saliasesmap. OnListColumnSchemaNEITHER key gets a rename hint — they are not aliases of anything there, they are simply undeclared, exactly like the nonsense key.⭐ 一个正对照(合法键通过)、一个负对照(无意义键被拒)、外加拒绝消息形状的比对(有无 rename hint)—— 三者合起来把"这两个键在 list column 上不是遗留别名,而是根本没声明"钉死了。⛔ 后来者不要用"它们在
FieldSchema上是别名"去推翻这个结论,那是另一个 schema。定级理由
priority:p3—— 采纳卡片的 observation-class 自评并复核其论证:- 今天没有东西渲染错:这条分支是
objectDef加载完成之前才跑的回落路径,而一个两个键都没有的 column 只会得到referenceTo: undefined,filter builder 本来就容忍它。 - 值得记账的是这个读取存在本身:它去问两个 spec 合规的生产者放不上去的键 ⇒ 正是 AGENTS.md 0.1 禁止的消费者侧容忍,也是 finding(plugin-grid): ObjectGrid copies
reference_to_fieldonto every column's fieldMeta, and nothing in the repo reads it #6711 / finding(plugin-dashboard): FieldMeta.decimals is written from the schema def but read by nothing #6625 /referenceTocolumn override: promised in the README, kept by nothing — make the promise real or withdraw it (enforce-or-remove, from #6425) #6597 各自在别处退休过的同一形状。 - 不上 p2:无用户可见后果,无静默错答(
undefined被正确容忍)。
⛔ 定型与边界 —— 卡片自己开的处方是对的,请照做
A measurement first, not a sweep. Establish whether any producer in or outside these repos puts a relational target on a list-view COLUMN (as opposed to on the field def the column names).
⭐ 这个先后顺序是硬要求,因为两种结果导向相反的动作:
普查结果 动作 型 无生产者 这个读取是死重,删掉 收窄 ⇒ tidy,不踩底线 有生产者 问题变成" ListColumnSchema该不该声明这个键"上游 accept-set 变更 ⇒ 踩底线,且跨仓 ⛔ 不做普查就删,可能砍掉某个真实生产者的唯一通路;不做普查就去上游要声明,可能为零用户加宽契约。
⛔ 给执行席的其他边界
- 普查范围要含仓外(卡片写的 "in or outside these repos")。
⚠️ 仓外测不到时,请明说"仅凭仓内证据",⛔ 不要把"仓内没有"写成"没有"。 - ⛔ 不要顺手narrow 另一条分支。
objectDef.fields那条已经由 finding(objectui): ~20 more relationship-target readers still run a legacy-firstreference_tochain the ingestion normalizer already made redundant #6837 half 2 收窄过了,是对的;本卡只管!objectDef?.fields的回落分支。 ⚠️ 注意这条分支只在加载竞态窗口里跑,所以任何回归测试都要刻意构造objectDef未就绪的状态,⛔ 否则测试根本走不到:2713。
⛔ 分诊席不认领、不派单、不写码、不裁决删除 vs 上游声明。
Generated by Claude Code
- 今天没有东西渲染错:这条分支是
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsRuling: UI closure batch 3 item 3 · letter remove (the fallback branch stops reading undeclared column keys) · maintainer 「同意」 (chat, director seat summon #29,
session_01EcrTi7s5oDYPHS4Pi7h31d) 2026-09-24T07:09ZThread re-read to its last comment (5548769257) in this act. Read on objectui
origin/main62597c5:packages/plugin-list/src/ListView.tsx:3399-3401still readsf.reference_to || f.reference,f.display_field || f.reference_fieldandf.id_fieldoff a list-view COLUMN.ListColumnSchemadeclares none of these in either casing (measured with lit controls in the body and in 5542021508).Ruled: remove the reads (protocol first; AGENTS.md 0.1). The columns-only fallback builds its filter and sort candidates from what a column declares. Relational targets come from the object definition once it loads, as the
objectDef.fieldsbranch already does. ⛔ No key is added toListColumnSchemato keep the reads alive. Stop condition, run first: census the producers (in-repo, the sibling repos, examples and fixtures) for any of these keys on a list column. If one is found, stop and report instead of deleting.Execution:
pm:queue·priority:p3·domain:uistand.
Generated by Claude Code
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actionsClaim: PM loop round 1 —
domain:uiexecution seat
Session:session_01BA3nKVUwKQJf8DBxrSVtNC
Branch:claude/issue-7531-listview-column-undeclared-reads
Worktree:objectui-issue-7531
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/plugin-list/src/ListView.tsx(the!objectDef?.fieldscolumns-only fallback'sreferenceTo/displayField/idFieldreads and their census comment ONLY), a pin underpackages/plugin-list/src/__tests__/, and one.changeset/7531-…md(stop on breach; explain in the report)
Container & model:S,mode:subagent,model: opus(default judgement tier) —priority:p3: a consumer-side read of three keysListColumnSchemadoes not declare
Clause-②: no
Thread-read: 5809501335
Serial constraints cleared: open-PR file lists read 2026-09-25T07:26Z ⇒ none namespackages/plugin-list/src/ListView.tsx. PR objectui#10510 (objectui#10326) merged59636b3a6, and no livepm:dispatchedclaim names the file. This seat's own objectui#9306 claim excludes it.⚠️ This seat owes separate stale-docblock edits in the same file (LIST_VIEW_EXTRA_OPERATORS/VALUELESS_FILTER_BUILDER_OPERATORS_CANONICAL) after PR objectui#10534 lands. ⛔ They are not part of this card.Scope
The ruling
5809501335(UI closure batch 3 item 3, letter remove, maintainer 「同意」):- Stop condition, run first: census the producers (in-repo, the sibling repos, examples and fixtures) for any of
reference_to/reference/display_field/reference_field/id_fieldon a list-view COLUMN. If one is found, stop and report instead of deleting. - Otherwise remove the three reads from the columns-only fallback. That branch builds its filter and sort candidates from what a column declares. ⛔ No key is added to
ListColumnSchemato keep the reads alive. - ⛔ The
objectDef.fieldsbranch (an object-schema field def, a different contract) is not this card. - Pin: a columns-only list view whose column carries those keys offers the same candidates with no
referenceTo/displayField/idField, with a lit control.
⇒
Fixes #7531.domain:uiseat #1 ·session_01BA3nKVUwKQJf8DBxrSVtNC· claim · 2026-09-25T07:30Z- Stop condition, run first: census the producers (in-repo, the sibling repos, examples and fixtures) for any of
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actionsos-dev-report
{ "issue": 7531, "status": "done", "branch": "claude/issue-7531-listview-column-undeclared-reads", "pr": "https://github.com/objectstack-ai/objectui/pull/10544", "session": "session_01BA3nKVUwKQJf8DBxrSVtNC — harness-stamped id of this run (subagent = the PM seat's session); every commit carries it as the Claude-Session trailer", "premise_still_valid": true, "summary": "The stop-condition census ran first. It found no list-column producer of reference_to / reference / display_field / reference_field / id_field or of their camelCase forms. In objectstack origin/main bc80e1626 it found 0 of 515 column objects. In objectui base 6881e9e47 it found 4 of 956: three line-item GridField columns, which are a different contract, and one inert plugin-grid ObjectGrid test fixture. I classified that fixture as not a producer, and the reader's call on it is the one open question. So the three reads came out of ListView's !objectDef?.fields columns-only fallback, per ruling 5809501335. The objectui#7642 KEEP comment became one line pointing at the card. Nothing else in ListView.tsx changed, and the objectDef.fields branch and the LIST_VIEW_EXTRA_OPERATORS / VALUELESS docblocks are untouched. A pin with two lit controls and a patch changeset landed, and the ablation turned only the pin red. The assignee was already os-bill when I picked the card up and was left untouched. The claim was confirmed: branch claude/issue-7531-listview-column-undeclared-reads matches comment 5828646659. The worktree was removed after the PR opened.", "tests": "All at HEAD 086e71456. pnpm exec vitest run packages/plugin-list/ from the repo root, through os-verify-lock: Test Files 93 passed (93), Tests 1098 passed (1098); VERDICT command-exit 0. Build and type-check: pnpm --workspace-concurrency=2 --filter @object-ui/plugin-list^... build built 12 packages (Scope: 13 of 47); VERDICT command-exit 0. Then pnpm --filter @object-ui/plugin-list type-check (tsc --noEmit && tsc -p tsconfig.test.json): VERDICT command-exit 0. tsc -p tsconfig.test.json --listFiles counts the new test (1). New pin ListView.columnsOnlyRelationalReads-7531.test.tsx: 4 passed. The cases are spec refusal with a bare-column control, a plain-column control, the pin (toStrictEqual against the plain candidates, with referenceTo, displayField and idField undefined), and a definition-supplies-referenceTo control. Ablation: node objectstack/scripts/ablation-replace.mjs in wrap mode, under the lock, put the three reads back. The anchor count went 1 to 0, the ListView.tsx blob went 03798ad2b3be to 39feb0e2facd, and the in-mutation count of the read line was 1. Result: Tests 1 failed | 3 passed (4). Only the pin failed, with a diff of referenceTo \"account\", displayField \"name\" and idField \"id\" against undefined. Restore: the blob after restore is 03798ad2b3be, equal to the HEAD blob, and git diff HEAD is EMPTY. The test imports ../ListView (source), so no dist/ is in the resolution path and no rebuild was needed. The first attempt was a no-op: the tool refused it because the replacement still contained the anchor, so no test ran, and it was restored and verified before the second attempt. Gates, all exit 0: pnpm check:control-bytes (OK); pnpm check:new-line-citations (0 new); node scripts/check-changeset-presence.mjs (1 changeset declared); pnpm check:changeset-claims. The claims gate is report-only: 7 pending changesets name ListView.tsx, and I read all of them. They cover gantt, kanban, userActions and sort comments, and all remain true. Hand-derived gate families, all exit 0: check-vi-mock-inherit, check-vi-mock-specifiers, check-vi-mock-override-shape, check-test-path-roots, check-changeset-fixed / no-major / overwrite, check:spec-symbols, check:unreferenced-sources, check:phantom-deps, check-type-check-coverage, check-lint-coverage. check-governed-queue-guard --test answered NOT GOVERNED. eslint on the touched files: ListView.tsx has 0 errors and 183 warnings, with per-rule counts identical to the base content (read via --stdin at the same path); the new test has 0 errors and 0 warnings. eslint.config.js has no type-aware linting, so this diff cannot move any untouched file's verdict. The full pnpm lint is left to CI. CI: NOT MEASURED, reason: the report is delivered once local verification is done, and CI convergence is the PM's.", "mcp_calls": "0 — no MCP tool was called", "api_writes": "2 — both through the fleet-write relay, each as one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) op pr_create, POST /repos/objectstack-ai/objectui/pulls, which opened draft #10544; the body was read back byte-identical (11353 bytes); (2) this report, via post-stamped --comment=7531 --repo=objectstack-ai/objectui, which is POST /repos/objectstack-ai/objectui/issues/7531/comments. Zero label writes. Four git pushes to the branch, which are not REST writes.", "open_questions": [ { "question": "Does the stop condition in ruling 5809501335 count a unit-test fixture? The census found one list column carrying a target key: packages/plugin-grid/src/__tests__/ObjectGrid.linkCellAnchor.test.tsx, a grid-view column with reference_to: crm_account. It renders ObjectGrid directly, so ListView's fallback never sees it. No ObjectGrid code reads a relational key off a column: the lookup cell uses applyRelationalMeta(fieldMeta, objectDefField). And the fixture has no object definition, so the key is inert. I read it as not a producer and opened the removal PR.", "options": [ "A: not a producer. Keep PR #10544, and optionally drop the inert key from that fixture later.", "B: the literal reading, where any fixture counts. Close PR #10544, then either rule the fixture out or clean it, and re-dispatch the removal." ], "recommendation": "A, because the census exists to protect a producer's only path, and this fixture has no path to the removed read. It does not feed ListView, and its key is read by nothing, before or after this PR. So it raises no question of whether ListColumnSchema should declare the key." } ], "out_of_scope_findings": [ "class: b · Seam: spec:FieldSchema.displayField → renderer:plugin-list ListView candidateFields objectDef.fields branch, which reads field.display_field || field.reference_field and never displayField, feeding components FilterBuilder LookupValuePicker (displayField || name). Evidence: on the installed @objectstack/spec 17.4.0, FieldSchema ACCEPTS displayField, REFUSES display_field with the rename hint display_field → displayField, REFUSES reference_field with the hint → referenceVia, and REFUSES id_field and idField with no hint, the same as the nonsense-key control. A named probe (a temporary test run under the lock, then deleted) showed a definition field carrying only displayField: full_name gives a candidate with displayField undefined, while display_field: full_name gives full_name. The picker consequence, labels falling to r.name, then r.label, then the id, is read from code, not measured in the UI. UserFilters.tsx in the same package has the same display_field ?? reference_field read (code reading, not probed). plugin-grid already converged on displayField (relationalMetaKeys.ts, objectui#6875 / #7155). · dedupe words: ListView filter displayField, candidateFields display_field, LookupValuePicker displayField name, UserFilters display_field reference_field", "class: b · Seam: spec:ListColumnSchema, a closed shape that refuses options → renderer:plugin-list ListView candidateFields columns-only fallback buildOptions(fieldName, f.options). Evidence: ListColumnSchema.safeParse on a column carrying options is REFUSED unrecognized_keys (Unrecognized key(s) on this list column: `options`, no rename hint). Its declared keys are field, label, width, align, hidden, sortable, resizable, wrap, type, pinned, summary, prefix, link and action. The same AST census for options over list columns found 0 list-column producers: 6 hits in objectui (line-item and flow-config columns) and 2 in objectstack (record:line_items columns in examples/app-showcase project-detail.page.ts). This is the same shape AGENTS.md 0.1 forbids and the sibling of this card, but it is not in ruling 5809501335 and the dispatch fenced it off. · dedupe words: ListView columns-only fallback options, buildOptions f.options list column, ListColumnSchema options undeclared, candidateFields column options", "carrier: domain:ui seat #1, in its planned post-objectui#10534 same-file docblock edits to ListView.tsx · the objectDef.fields branch comment that says the SIBLING branch above is NOT narrowed and is filed, not answered here is stale after this PR; left untouched per the dispatch fence · noted, not filed", "carrier: 承接者:无 · the ObjectGrid.linkCellAnchor.test.tsx case about a lookup column pointing at ANOTHER object staying plain passes whatever the column carries, because its reference_to is inert and there is no object definition. It is a test-quality note, recorded in the PR Acceptance notes · noted, not filed" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actions✅ ACCEPT — PR objectui#10544 at
086e71456· entering the merge queue once CI is greendomain:uiseat #1,session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report and the diff against the ruling5809501335(UI closure batch 3 item 3, letter remove, maintainer 「同意」) and the claim5828646659. No review-tier record is owed, becauseClause-②: noholds: removing reads of keysListColumnSchemarefuses changes nothing for a spec-compliant producer.Implemented-by: claude/issue-7531-listview-column-undeclared-reads Reviewed-by: session_01BA3nKVUwKQJf8DBxrSVtNCitem reading shape draft · base main·Fixes #7531·Clause-②: noat line start · 3 files (+196 / −11) · merges clean onto currentmainstop condition, run first The census of list-column producers of reference_to/reference/display_field/reference_field/id_field, and their camelCase forms, found: objectstackorigin/main0 of 515 column objects; objectui 4 of 956. Three of those are line-itemGridFieldcolumns (a different contract) and one is a test fixture (below).objectstack-ai/cloudis unreadable here, and the dev says sothe fix The three reads are removed from the !objectDef?.fieldscolumns-only fallback. The objectui#7642 "KEEP" comment becomes one line naming this card. ⛔ TheobjectDef.fieldsbranch and theLIST_VIEW_EXTRA_OPERATORS/ value-less docblocks are untouched, as fencedablation The three reads put back: 1 red / 3 green, only the pin. Its diff is referenceTo "account",displayField "name",idField "id"againstundefined. Restored by blob03798ad2b3betests + gates packages/plugin-list/93 files / 1098 tests passed. Type-check passes after the closure build. eslint per rule matches base (183 warnings, 0 errors). Control bytes, new line citations, changeset presence / claims / fixed / no-major / overwrite and the vi-mock gates exit 0, and the queue guard says NOT GOVERNED.check:changeset-claimsnames 7 pending changesets onListView.tsx; the dev read each and none is falsified. Changeset:patchon@object-ui/plugin-listThe dev's open question, answered by the seat: A. The census found one list column carrying a target key:
packages/plugin-grid/src/__tests__/ObjectGrid.linkCellAnchor.test.tsx,{ field: 'account_id', type: 'lookup', reference_to: 'crm_account' }(the seat re-read it onmain). The ruling's stop condition exists so that the removal cannot take away a producer's only path. This fixture rendersObjectGriddirectly, never reachesListView's fallback, and its key is read by nothing, before or after this PR (the lookup cell resolves through the object definition). So it is not a producer for the removed read, and the stop condition does not fire. This is a verification-strategy call, which is inside the seat's discretion, and it goes into the round report's veto window.Dev findings — each one routed
- The
objectDef.fieldsbranch (andUserFilters.tsx) read a lookup's display field asdisplay_field || reference_field, spellingsFieldSchemarefuses, and never the declareddisplayField. Measured: adisplayField-only definition yieldsundefined⇒ filed finding(plugin-list): the filter candidates read a lookup's display field asdisplay_field || reference_field, spellingsFieldSchemarefuses, and never read the declareddisplayField#10545. - The columns-only fallback also reads
optionsoff a list column, whichListColumnSchemarefuses; 0 list-column producers were found ⇒ filed finding(plugin-list):ListView's columns-only filter fallback readsoptionsoff a list column, a keyListColumnSchemarefuses #10547. A new branch of this card's class, left to triage whether the ruling carries over. - The
objectDef.fieldsbranch comment ("the sibling branch above is NOT narrowed … filed, not answered here") is stale after this PR ⇒ carrier: this seat, in its plannedListView.tsxdocblock edits after objectui#10534. ObjectGrid.linkCellAnchor.test.tsx's "lookup pointing at ANOTHER object stays plain" case passes whatever the column carries ⇒ PR Acceptance notes. 承接者:无.
domain:uiseat #1 · review · 2026-09-25T08:21Z- The
- added a commit that references this issue
on Sep 28, 2026
Ruled: 5809501335 · letter remove (the fallback branch stops reading undeclared column keys) · 2026-09-24T07:11Z
Surfaced while implementing objectui#6837 half 2 (the
reference_toarm deletion), and deliberately left out of that PR: it is a contract-shape question, not part of the arm deletion.What was measured
packages/plugin-list/src/ListView.tsxbuilds its filter/sort field candidates in two branches. TheobjectDef.fieldsbranch reads an OBJECT SCHEMA field def, wherereferenceis the declared spelling; objectui#6837 half 2 narrowed it. The sibling branch — the!objectDef?.fieldsfallback that reads the view's own declaredschema.columns— still readsf.reference_to || f.reference, and was deliberately NOT narrowed, because a list-view COLUMN is a different contract.Measured against
@objectstack/spec17.2.0'sListColumnSchema, on the installed package:{ field: 'acct' }parses clean (positive control).{ field: 'acct', reference: 'account' }is REFUSED withunrecognized_keys.{ field: 'acct', reference_to: 'account' }is REFUSED withunrecognized_keys.{ field: 'acct', zzz_nonsense: 1 }is REFUSED identically (negative control).⭐ The tell is what is ABSENT. On
FieldSchemaboth legacy spellings come back with their own rename hint ("Did you meanreference_totoreference?") because they sit in that schema'saliasesmap. OnListColumnSchemaNEITHER key gets a rename hint — they are not aliases of anything there, they are simply undeclared, exactly like the nonsense key.Why this is observation-class, not a live defect
Nothing renders wrong today: the branch is a fallback that only runs before
objectDefhas loaded, and a column that carries neither key just yieldsreferenceTo: undefined, which the filter builder already tolerates. The point is that the read exists at all — it consults two keys that a spec-compliant producer cannot put on a list column, which is the consumer-side tolerance AGENTS.md commandment 0.1 forbids, and the same shape objectui#6711, objectui#6625 and objectui#6597 each retired elsewhere.What a fix would be
A measurement first, not a sweep. Establish whether any producer in or outside these repos puts a relational target on a list-view COLUMN (as opposed to on the field def the column names). If none does, the read is dead weight and goes; if some does, the question is whether
ListColumnSchemashould declare the key rather than whether the renderer should keep absorbing it.Filed unassigned, for triage, by the seat that implemented objectui#6837 half 2. Related: objectui#6837.
Generated by Claude Code