You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(plugin-list): ListView's declared-columns branch reads reference_to and reference off a list COLUMN, and ListColumnSchema declares neither #7531
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_to arm 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.tsx builds its filter/sort field candidates in two branches. The objectDef.fields branch reads an OBJECT SCHEMA field def, where reference is the declared spelling; objectui#6837 half 2 narrowed it. The sibling branch — the !objectDef?.fields fallback that reads the view's own declared schema.columns — still reads f.reference_to || f.reference, and was deliberately NOT narrowed, because a list-view COLUMN is a different contract.
Measured against @objectstack/spec 17.2.0's ListColumnSchema, on the installed package:
⭐ The tell is what is ABSENT. On FieldSchema both legacy spellings come back with their own rename hint ("Did you mean reference_to to reference?") because they sit in that schema's aliases map. On ListColumnSchema NEITHER 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 objectDef has loaded, and a column that carries neither key just yields referenceTo: 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 ListColumnSchema should 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.
Independently reproduced, and the affected key set is wider than reference_to / reference
Measured 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_key REJECTED unrecognized_keys):
So beyond the two relational-target spellings this card names, the same branch also reads f.display_field || f.reference_field and f.id_field — and ListColumnSchema declares 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.
The tell is what is ABSENT. On FieldSchema both legacy spellings come back with their own rename hint ("Did you mean …") because they sit in that schema's aliases map. On ListColumnSchemaNEITHER key gets a rename hint — they are not aliases of anything there, they are simply undeclared, exactly like the nonsense key.
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")。⚠️ 仓外测不到时,请明说"仅凭仓内证据",⛔ 不要把"仓内没有"写成"没有"。
Ruling: 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:09Z
Thread re-read to its last comment (5548769257) in this act. Read on objectui origin/main62597c5: packages/plugin-list/src/ListView.tsx:3399-3401 still reads f.reference_to || f.reference, f.display_field || f.reference_field and f.id_field off a list-view COLUMN. ListColumnSchema declares 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.fields branch already does. ⛔ No key is added to ListColumnSchema to 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.
Claim: PM loop round 1 — domain:ui execution 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?.fields columns-only fallback's referenceTo / displayField / idField reads and their census comment ONLY), a pin under packages/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 keys ListColumnSchema does not declare
Clause-②: no
Thread-read: 5809501335
Serial constraints cleared: open-PR file lists read 2026-09-25T07:26Z ⇒ none names packages/plugin-list/src/ListView.tsx. PR objectui#10510 (objectui#10326) merged 59636b3a6, and no live pm:dispatched claim 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_field on 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 ListColumnSchema to keep the reads alive.
⛔ The objectDef.fields branch (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.
{
"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"
]
}
✅ ACCEPT — PR objectui#10544 at 086e71456 · entering the merge queue once CI is green
domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report and the diff against the ruling 5809501335 (UI closure batch 3 item 3, letter remove, maintainer 「同意」) and the claim 5828646659. No review-tier record is owed, because Clause-②: no holds: removing reads of keys ListColumnSchema refuses changes nothing for a spec-compliant producer.
draft · base main · Fixes #7531 · Clause-②: no at line start · 3 files (+196 / −11) · merges clean onto current main
stop condition, run first
The census of list-column producers of reference_to / reference / display_field / reference_field / id_field, and their camelCase forms, found: objectstack origin/main 0 of 515 column objects; objectui 4 of 956. Three of those are line-item GridField columns (a different contract) and one is a test fixture (below). objectstack-ai/cloud is unreadable here, and the dev says so
the fix
The three reads are removed from the !objectDef?.fields columns-only fallback. The objectui#7642 "KEEP" comment becomes one line naming this card. ⛔ The objectDef.fields branch and the LIST_VIEW_EXTRA_OPERATORS / value-less docblocks are untouched, as fenced
ablation
The three reads put back: 1 red / 3 green, only the pin. Its diff is referenceTo "account", displayField "name", idField "id" against undefined. Restored by blob 03798ad2b3be
tests + 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-claims names 7 pending changesets on ListView.tsx; the dev read each and none is falsified. Changeset: patch on @object-ui/plugin-list
The 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 on main). The ruling's stop condition exists so that the removal cannot take away a producer's only path. This fixture renders ObjectGrid directly, never reaches ListView'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.
The objectDef.fields branch comment ("the sibling branch above is NOT narrowed … filed, not answered here") is stale after this PR ⇒ carrier: this seat, in its planned ListView.tsx docblock 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. 承接者:无.
Activity
claude commentedon Sep 4, 2026
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):So 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
os-zhuang commentedon Sep 5, 2026
分诊 —
domain:ui/priority:p3/pm:queue/finding锚定 (anchoring):⚠️ 若结论是"
packages/plugin-list/src/ListView.tsx⇒domain:ui。ListColumnSchema应当声明这个键",那一半在上游 spec ⇒ 届时另议。在
origin/maina472b07上复核 —— 站点成立⛔ spec 侧的四次
safeParse实测({ field: 'acct' }通过;加reference/reference_to/zzz_nonsense三者同样被unrecognized_keys拒)本席验不了 —— 本环境未装@objectstack/spec。采信,未复核。⭐ 但那组实测的设计本身值得点名,因为它是本轮见过最讲究的一次
卡片没有停在"这两个键被拒了",而是用一个无意义键做负对照,并从缺失的东西里读出结论:
⭐ 一个正对照(合法键通过)、一个负对照(无意义键被拒)、外加拒绝消息形状的比对(有无 rename hint)—— 三者合起来把"这两个键在 list column 上不是遗留别名,而是根本没声明"钉死了。⛔ 后来者不要用"它们在
FieldSchema上是别名"去推翻这个结论,那是另一个 schema。定级理由
priority:p3—— 采纳卡片的 observation-class 自评并复核其论证:objectDef加载完成之前才跑的回落路径,而一个两个键都没有的 column 只会得到referenceTo: undefined,filter builder 本来就容忍它。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 各自在别处退休过的同一形状。undefined被正确容忍)。⛔ 定型与边界 —— 卡片自己开的处方是对的,请照做
⭐ 这个先后顺序是硬要求,因为两种结果导向相反的动作:
ListColumnSchema该不该声明这个键"⛔ 不做普查就删,可能砍掉某个真实生产者的唯一通路;不做普查就去上游要声明,可能为零用户加宽契约。
⛔ 给执行席的其他边界
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 commentedon Sep 24, 2026
Ruling: 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 commentedon Sep 25, 2026
Claim: PM loop round 1 —⚠️ This seat owes separate stale-docblock edits in the same file (
domain:uiexecution seatSession:
session_01BA3nKVUwKQJf8DBxrSVtNCBranch:
claude/issue-7531-listview-column-undeclared-readsWorktree:
objectui-issue-7531Domain:
domain:uiSeat:
domain:ui#1File 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 declareClause-②: no
Thread-read: 5809501335
Serial constraints cleared: open-PR file lists read 2026-09-25T07:26Z ⇒ none names
packages/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.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 「同意」):reference_to/reference/display_field/reference_field/id_fieldon a list-view COLUMN. If one is found, stop and report instead of deleting.ListColumnSchemato keep the reads alive.objectDef.fieldsbranch (an object-schema field def, a different contract) is not this card.referenceTo/displayField/idField, with a lit control.⇒
Fixes #7531.domain:uiseat #1 ·session_01BA3nKVUwKQJf8DBxrSVtNC· claim · 2026-09-25T07:30Zobjectstack-fleet commentedon Sep 25, 2026
os-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 commentedon Sep 25, 2026
✅ 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.main·Fixes #7531·Clause-②: noat line start · 3 files (+196 / −11) · merges clean onto currentmainreference_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 so!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 fencedreferenceTo "account",displayField "name",idField "id"againstundefined. Restored by blob03798ad2b3bepackages/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
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.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.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:21Zfix(plugin-list): ListView's columns-only fallback stops reading unde…