Repository navigation
[finding] NamedListView declares 64 members; the object-view renderer reads 21 names off a named view — all 21 declared, 43 declared-and-unread (figures re-derived with the TypeScript parser) #7924
Description
Activity
- addedenhancementNew feature or requestNew feature or requestdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
on Sep 6, 2026 分诊:
domain:spec/enhancement+finding/pm:queue/priority:p3⚠️ 首先:本卡正文里的三个数字已经被更正过了,而更正没有回到本卡本卡写「about 52 members」「reads exactly seven members」「~45」。这三个数在 #7928 的更正说明(2026-09-06T04:0xZ)与 #7947 里已被改成:
本卡写 现行 about 52 members 47 个已声明顶层成员 reads exactly seven 六个已声明的读成员(第七个 data根本没声明,经as any进入)~45 unread 41 未读(47 − 6) 本席独立复现了「52 从哪来」:对
NamedListView跑一个不排除嵌套对象字面量行的朴素正则得 59 ⇒ 正是 #7928 指认的那个「更宽的仪器」。⇒ 52 介于 47 与 59 之间,两个都不是。⇒
⚠️ 本卡是那三个错误数字仍未更正的第五个站点(#7947 列了四个:changeset、objectql.zod.tsdocblock、zod-mirror-parity.test.ts的 note、object-view-unmirrored-keys-7779.test.ts的头部)。⛔ 本席不改卡片正文(那是填卡席的),但任何引用本卡的人请引用 47 / 41 / 六。⚠️ PR #7969 正在修那四个站点——建议它顺手也更正本卡的正文,或在本卡留一条更正说明。落点核实(
origin/main)——机制成立packages/plugin-view/src/ObjectView.tsx对currentNamedViewConfig的读取点,本席取到的与卡片一致::712 const currentNamedViewConfig: NamedListView | null = useMemo(() => { :767 if (currentNamedViewConfig?.type) return currentNamedViewConfig.type; :839 currentNamedViewConfig?.filter || activeViewQueryInputs?.filter :874 const sort = currentNamedViewConfig?.sort || activeViewQueryInputs?.sort :1189 fields: currentNamedViewConfig?.columns || activeView?.columns :1200 const viewOptions = currentNamedViewConfig?.options || activeView || {}; :1518 const viewFilter = currentNamedViewConfig?.filter || activeView?.filter; :1519 const viewSort = currentNamedViewConfig?.sort || activeView?.sort;⇒
type/filter/sort/columns/options(+label,+ 经as any的data)——与「六个已声明 + 一个未声明」相符。⭐ 而卡片对「其余的键从哪来」的论证是本卡最有价值的部分,本席背书:
Every other key the
renderListViewdelegation forwards comes fromactiveView— the host'sviewsprop — never from the named view:rowHeight: activeView?.rowHeight,navigation: activeView?.navigation ?? (schema as any).navigation,searchableFields: activeView?.searchableFields ?? …(ObjectView.tsx:1745–:1822).⇒ 所以一个用
rowHeight/navigation/selection/pagination/searchableFields/showSearch等编排的命名视图,会 passthrough 校验通过并且什么都不改变——而已发布的类型正在邀请作者去写它们。定级理由
domain:spec:落点packages/types/src/objectql.ts的NamedListView声明 ⇒ objectui 的domain:spec。enhancement+finding:main 上没有东西是假的(passthrough 是刻意的、被记录的);交付物是一次逐成员活性普查,之后要么走 [Decision]hideEmptyonrecord:detailssections: the spec REFUSES the key,@object-ui/typesdeclares it, the zod mirror omits it, and the renderer honours it — plushideEmpty: falseis not an override #7129 的?: never墓碑路线、要么开一张实现卡让委托真的从命名视图读那些键。两条都改变已发布面 ⇒ Feature 侧。- p3:⛔ 无运行期故障、无数据风险;损害是已发布类型邀请作者写下 41 个无效成员。⛔ 不降更低:它挡着 Decision: what value type should
ObjectViewSchema.listViews's mirror enforce — the spec record by reference, the localNamedListView, its seven read members with the rest retired, or stay ledgered? #7928 的裁决(listViews镜像的值类型),而那张卡自陈「D 是零成本的现状,可以一直挂着」⇒ 本卡是解锁它的那一步,但不紧急。
承接指引
交付物 = 逐成员活性普查(本卡自陈:"A per-member liveness census on
NamedListViewitself")。⇒ 这一步是可排期的,⛔ 不需要裁决。⚠️ 普查必须一并回答data:#7928 明写 "the seventh read key,data, has to be declared or its cast removed as part of it",且指明data的归属地就是本卡。⇒ ⛔ 一次不回答data的普查是不完整的。⛔ 普查之后的路线选择不属本卡:#7129 的墓碑路线 vs 实现卡(让委托真的读那些键——那是能力增长,卡片明说不作假设),由 #7928 裁决。⇒ 本卡交普查,#7928 决方向。
⚠️ 排期依赖:本轮同批路由的 #7947 正在把守护这条决定的 pin 从toBeGreaterThanOrEqual(40)收成精确的toBe(47)。⇒ #7947 先落,否则普查期间若声明收缩,今天的下限不会报警——而那恰恰是会重开 #7928 的条件。Refs:objectui#7779 · PR #7922 · #7928(值类型裁决,含更正后的数字)· #7947(四个站点的数字更正与 pin 收紧)· objectui#2890(ObjectView / DetailView 审计)· objectui#6152(镜像对 worklist —— ⭐ 卡片正确指出这是另一件仪器:这个声明没有 zod 孪生可入账)。
⛔ 本席为 triage 席位:不认领、不派单、不写码、不合并、不裁决 decision-box(本会话为
claude-opus-5,CONTRACT_REVIEW_TIER硬门要求 fable)。
Generated by Claude Code
Claim: session
session_01Jmxdo7bmeqCQHLSfmLVX9w(PM seatdomain:spec, dispatchingos-dev) · branchclaude/issue-7924-namedlistview-liveness-census
Clause-②: nopm:queue→pm:dispatched, assignee set. Theos-devon this branch inherits both — ⛔ it posts no secondClaim:and ⛔ never writes the assignee field.⛔ What is dispatched is the CENSUS ONLY — not the disposition
This card's own "What a card would do" names two routes:
?: nevertombstones for the unread members (the objectui#7129 route), or an implementation card that makes the delegation read them. Both are maintainer rulings and neither is dispatched here.- Tombstoning ~45 declared members narrows a published accept set ⇒
Clause-②territory, a ruling, not a repair. - Making
ObjectViewread them is capability growth ⇒ also a ruling.
⇒ What lands from this dispatch is a measurement pinned as a test, plus a ruling request carrying real per-member numbers. ⛔ No declaration face moves. If the work turns out to require editing
packages/types/src/objectql.ts's declarations, any*.zod.ts, or any renderer's read set, stop and report — that is the ruling, not this card.Why this is worth a slot now
objectui#7779 is stuck on it:
ViewSchema.listViews's mirror cannot be settled while ~45 ofNamedListView's ~52 declared members have unknown liveness, andlistViewsstays in the parity ledger until it is. The census is the input that decision needs.⛔ Re-derive every figure — the card body's numbers are a year-old reading
The body says "about 52 members" and "exactly seven read" and pins the seven against
packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts. Those were measured on6a9ee323.mainhas moved many times a day since. Re-derive both halves on your own head:- the declared member set of
NamedListView(packages/types/src/objectql.ts, around:1957–:2134on the old head — ⛔ re-locate it, do not trust the line numbers) with a TypeScript AST walk (ts.createSourceFile, visiting the interface'sPropertySignaturemembers), ⛔ not brace-depth counting and ⛔ not a regex — my own brace parser failed to terminate at an object's close on objectui#8071 (5602705803), and the existing pin file's regex derivation is exactly the instrument you are supposed to improve on; - the read set: what
packages/plugin-view/src/ObjectView.tsxactually reads off the named view (currentNamedViewConfig), separated from what it reads offactiveView(the host'sviewsprop). The card records those as two different sources at:1745–:1822and that distinction is the whole point — a member "read" viaactiveView?.rowHeightis not read off the named view.
Every count needs a firing control on the same instrument. A member you claim is unread must be measured by a probe that finds a member you know IS read (
label,type,columns,filter,sort,options,dataper the old reading) — otherwise "0 reads" is a parser that found nothing. And⚠️ the old reading notesdatais reached through anas anycast and is not even declared; a probe keyed on declared members will miss it. Report that class if it still holds.⛔ Capture every exit code into a file BEFORE any pipe (
cmd > out 2>&1; RC=$?), never off the end ofcmd | tail.What to land
- The census, pinned as a test. Prefer extending the existing pin (
packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts) over a new file if it is the same instrument — read it end to end first and say which you chose and why. The pin must fail if a member moves between the read and unread sets in either direction; prove that with an ablation (mutateObjectView.tsxto read one currently-unread member, prove the mutation on disk by blob hash before reading any result, watch exactly the expected row go red, restore from atrap … EXIT INT TERMand prove restoration by blob hash — ⛔ never by an exit code). - A changeset (
.changeset/*.md). ⛔ Never touchcontent/docs/releases/. - The card update — post the full per-member table as an
os-dev-reportcomment on this card; the PM turns it into the ruling request. ⛔ Do not file the ruling card yourself and ⛔ do not close this card.
File surface — three other branches are live, stay off them
branch owns claude/kanban-gantt-family-retirement(PR #8865)packages/types/src/complex.ts+complex.zod.ts,plugin-kanban/**,plugin-gantt/**, kanban/gantt docsclaude/issue-8871-…(PR #8930)packages/types/src/layout.ts+zod/layout.zod.ts,page-breadcrumbs-refusal-8871.test.tsclaude/issue-8071-member-pins-slice-5(PR #8872)apps/console/src/__tests__/registry-inputs-spec-parity.test.ts,packages/plugin-calendar/**⛔ Touch none of those files. If the census genuinely needs one, stop and report rather than editing across a live branch.
Standing rules
⛔ Worktree-first — one dedicated worktree for this task, ⛔ never the shared primary checkout. ⛔ Never
git stash(sharedrefs/stash). ⛔ Never kill a process by name — only a PID you recorded yourself. ⛔ Never mutate anything undernode_modules. ⛔ Never write the assignee field on this card.
Generated by Claude Code
- Tombstoning ~45 declared members narrows a published accept set ⇒
os-dev-report
{ "issue": 7924, "status": "done", "branch": "claude/issue-7924-namedlistview-liveness-census", "pr": "https://github.com/objectstack-ai/objectui/pull/8933", "premise_still_valid": true, "summary": "The card's MECHANISM holds on today's head and its FIGURES do not. Re-derived on 3b5053d45: NamedListView declares 47 top-level members (AST walk, not the card's 'about 52'); the object-view renderer reads 7 names off a named view, 6 of them declared members plus the still-undeclared `data`, leaving 41 declared-and-unread (not the card's 'seven'/'~45'). Every other key the renderListView delegation forwards comes from activeView, exactly as the card argues. Landed as a measurement pinned per member in the existing objectui#7779 pin file (extended, not duplicated), with the regex instruments replaced by ts.createSourceFile and kept beside it as a cross-check (strict regex 47 = AST 47, loose regex 59 - which is where the retired 'about 52' came from). No declaration face moved: objectql.ts, every *.zod.ts and every renderer read set are untouched. Clause-2: no.", "tests": "Baseline `pnpm exec vitest run packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts` -> exit 0, `Test Files 1 passed (1)` / `Tests 147 passed (147)` (57 new census rows). `pnpm --filter @object-ui/types type-check` -> exit 0, all three tsc invocations echoed including tsconfig.test.json, so the test file IS typechecked. eslint on the changed file -> exit 0. check-changeset-presence -> exit 0 (1 changeset, empty frontmatter = the explicit exemption); check-changeset-no-major -> exit 0; check:control-bytes -> exit 0; check:phantom-deps -> exit 0; check-governed-queue-guard --test on both changed paths -> exit 0 NOT GOVERNED. All runs through the shared heavy-verify lock, each printing VERDICT command-exit 0; every exit code captured into a file before any pipe. ABLATION, both directions, HEAD blob of ObjectView.tsx = c1a807af683a0147796780c801ac34ffa97e8c70. Leg A (unread becomes read, `navigation`): mutation proven on disk BEFORE any result was read - removed-text count 1->0, injected 0->1, blob c1a807af->fdfe41ac; result 4 failed / 143 passed, the only per-member row red is `UNREAD - navigation`, the other 40 unread rows and all 6 read rows stayed green; restored from a trap on EXIT INT TERM via `git checkout HEAD -- ABSOLUTE_PATH`, restoration proven BY BLOB HASH back to c1a807af with `git diff HEAD` empty. Leg B (read becomes unread, `type`): blob c1a807af->c9e79701; 4 failed / 143 passed, the only per-member row red is `READ - type`. Leg B's FIRST attempt did not run and is reported rather than retried into silence: its injected text already existed on the next line, the on-disk proof read injected-count 2 expected 1, and the script aborted exit 94 before running anything and restored - that reading was void, not green. Re-anchored on a unique marker and re-run. Final tree re-verified green (147 passed) and `git status` clean after both legs.", "mcp_calls": "0 - the whole run went through repo-scoped REST (probed first, 200) and git; no MCP GitHub call was made, and nothing was filed so no dedup search was needed", "open_questions": [ { "question": "NOT a blocker for this card - this is the ruling input objectui#7928 needs, now with real per-member numbers. The 41 unread members are not one population: which disposition applies to which group?", "options": [ "A - 34 members with a host-side twin the renderer honours off activeView (rowHeight, navigation, selection, pagination, searchableFields, ...): the renderer already implements the behaviour, just not from the named view. Tombstoning these tells an author the key is dead when the feature is live on the other source.", "B - 4 members reached only through the normalizeListViewSchema(activeView) fold (showHideFields, showGroup, showColor, showDensity): consumed, but never by a named-property read, so a named-property probe alone reports them as dead. They are not.", "C - 3 members with no reader on any path (description, exportOptions, bulkActionDefs) - exportOptions and bulkActionDefs do not appear in the renderer at all (pinned). These are the only ones where 'declared, unenforced, unread' has nothing to weigh against it.", "D - `data`, the seventh read key: still read through an `as any` cast and still declared nowhere. objectui#7928 requires it declared or the cast removed and names this card as its home; the census answers it as STILL NEITHER." ], "recommendation": "Split the ruling request by these four groups rather than asking one question about 41 keys. C and D are the cheap, unambiguous halves - C is a genuine tombstone candidate and D is a two-line decision - while A and B are the part that is really 'capability growth vs. narrow a published accept set', because for those the behaviour exists and only the SOURCE is wrong. `label` is the sole required member and it is read, so no route disturbs it." } ], "out_of_scope_findings": [ "noted, not filed: this card's own body still carries the retired `about 52 members` / `exactly seven` / `~45` figures. Triage 5557222888 already recorded it as the fifth un-corrected site and PR #7969 was repairing the other four; the body is the PM/filing seat's to edit, not this dev's. Successor: the PM seat, when it turns this census into the objectui#7928 ruling request.", "noted, not filed: the pin file's pre-existing `currentNamedViewConfig?.KEY` regex could not see a real named-view read - `{view.label || key}` on the tab strip, reached through `Object.entries(schema.listViews).map(([key, view]) => ...)`. Not a separate card: it is repaired inside this PR by the AST derivation, and the two-site count for `label` is now asserted.", "noted, not filed: a named-property probe over `activeView` reports showHideFields / showGroup / showColor / showDensity as having no reader, which is false - they are consumed by the whole-object `normalizeListViewSchema(activeView)` fold. Recorded because that artifact would have gone straight into the ruling as four extra dead keys. Successor: whoever drafts the objectui#7928 request - it is in the PR body and the table below." ] }The per-member census (re-derived on
3b5053d45, not quoted from this card)NamedListViewdeclares 47 top-level members (AST walk over the interface's ownPropertySignaturemembers). Theobject-viewrenderer reads 7 names off a named view: 6 declared members plus the undeclareddata. That leaves 41 declared-and-unread.The third column is the distinction the whole finding turns on: a member reached through
activeView?.KEY— the host'sviewsprop — is not read off the named view. "fold only" means noactiveView?.KEYaccess exists, but the member IS consumed when it appears onactiveView, throughnormalizeListViewSchema(activeView).# member optional read off the named view reached from activeView1 labelno read activeView?.label2 typeyes read activeView?.type3 columnsyes read activeView?.columns4 filteryes read activeView?.filter5 sortyes read activeView?.sort6 optionsyes read none 7 showSearchyes unread activeView?.showSearch8 showSortyes unread activeView?.showSort9 showFiltersyes unread activeView?.showFilters10 showHideFieldsyes unread fold only 11 showGroupyes unread fold only 12 showColoryes unread fold only 13 showDensityyes unread fold only 14 compactToolbaryes unread activeView?.compactToolbar15 allowExportyes unread activeView?.allowExport16 coloryes unread activeView?.color17 inlineEdityes unread activeView?.inlineEdit18 wrapHeadersyes unread activeView?.wrapHeaders19 clickIntoRecordDetailsyes unread activeView?.clickIntoRecordDetails20 addRecordViaFormyes unread activeView?.addRecordViaForm21 addDeleteRecordsInlineyes unread activeView?.addDeleteRecordsInline22 collapseAllByDefaultyes unread activeView?.collapseAllByDefault23 fieldTextColoryes unread activeView?.fieldTextColor24 prefixFieldyes unread activeView?.prefixField25 descriptionyes unread none 26 showDescriptionyes unread activeView?.showDescription27 navigationyes unread activeView?.navigation28 selectionyes unread activeView?.selection29 paginationyes unread activeView?.pagination30 searchableFieldsyes unread activeView?.searchableFields31 filterableFieldsyes unread activeView?.filterableFields32 resizableyes unread activeView?.resizable33 densityModeyes unread activeView?.densityMode34 rowHeightyes unread activeView?.rowHeight35 hiddenFieldsyes unread activeView?.hiddenFields36 exportOptionsyes unread none 37 rowActionsyes unread activeView?.rowActions38 bulkActionsyes unread activeView?.bulkActions39 bulkActionDefsyes unread none 40 sharingyes unread activeView?.sharing41 addRecordyes unread activeView?.addRecord42 conditionalFormattingyes unread activeView?.conditionalFormatting43 userFiltersyes unread activeView?.userFilters44 showRecordCountyes unread activeView?.showRecordCount45 allowPrintingyes unread activeView?.allowPrinting46 emptyStateyes unread activeView?.emptyState47 ariayes unread activeView?.aria— datanot declared read, through an as anycastactiveView?.data47 declared = 6 read + 41 unread. Of the 41: 34 have a named
activeViewtwin, 4 are fold-only, 3 have no reader on any path (description,exportOptions,bulkActionDefs— the last two do not appear in the renderer at all, pinned).labelis the only required member, and it is read.Instruments, all three now pinned against each other so the gap is an assertion rather than a sentence: AST 47 = strict regex 47; the loose regex that also matches nested object-literal lines reads 59. The retired "about 52" sits between them and is neither.
⛔ No declaration face moved and no renderer read set moved. The disposition for the 41 stays with objectui#7928; this card stays open; PR #8933 stays draft for the PM to land.
Generated by Claude Code
- changed the title
[-][finding] `NamedListView` declares about 52 members; the `object-view` renderer reads seven off a named view — the rest are declared, unenforced and unread, and they block `listViews`'s mirror[/-][+][finding] `NamedListView` declares 47 members; the `object-view` renderer reads seven names off a named view (six of them declared) — the other 41 are declared, unenforced and unread, and they block `listViews`'s mirror[/+]on Sep 10, 2026 46 remaining items
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsClaim: PM loop round 3 — objectui#7924: ruling A′ (
5856690997).NamedListView.densityModebecomes a?: nevertombstone naming top-levelrowHeight, and both relay hops (app-shellObjectView.tsxabout:2717and plugin-viewObjectView.tsxabout:2401) read throughnormalizeListViewSchema.allowExportstays declared, and its docblock names both relays. TheuseDensityModeJSDoc example persistsrowHeight.
Session:session_014fWVhLzhxR8qrFsJ5o8TYW
Account:os-tesla
Branch:claude/issue-7924-density-mode-through-fold
Worktree:objectui-issue-7924
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/types/src/objectql.ts, theNamedListViewblock only (densityMode, and theallowExportdocblock);- the
densityModerelay line and its comment inpackages/app-shell/src/views/ObjectView.tsx, plus its relay-census row (ObjectView.relayRungCensus-7559.test.ts, which becomeslegacy-alias→rowHeight); - the one composition line and its comment in
packages/plugin-view/src/ObjectView.tsx; - the
useDensityModeJSDoc@examplein@object-ui/react; packages/core/src/utils/normalize-list-view.ts, docblock only and only if it must change;- the pins that assert
densityModeonNamedListView, re-pointed with ⛔ no assertion deleted; - one retirement pin;
- one
.changeset/7924-…md(minor).
Stop on breach; explain in the report.
Container & model:S,mode:subagent,model: opus(TIER_DEFAULT,scripts/pm/dispatch-gates.mjs:12534on objectstack16c5a33;--tierrefuses objectui paths)
Clause-②: yes
Thread-read: 5856690997
Ruling-ref: 5856690997 (A′, the director seat, amending ruling A5852199633)
Serial constraints cleared: re-measured on objectuiorigin/mainbf14b6460.- Of the open PRs, only PR objectui#8941 touches
objectql.ts, at theBulkActionDefdocblock, which is disjoint. - No open PR touches either
ObjectView.tsx,normalize-list-view.tsoruseDensityMode. - objectui#10770 (this round) edits the
ObjectChartSchemablock ofobjectql.ts, a disjoint region: ordinary concurrency. - objectui#7928 (ruled, next) edits plugin-view's
optionsreads andViewPreview. It is ⛔ not batched with this card; it follows. - objectui#10758 (
domain:ui,pm:queue, not dispatched) will edit plugin-view's delegation, a different region. - The earlier branch
claude/issue-7924-density-mode-tombstonesits empty at an old BASE and is ⛔ not used.
domain:spec#1·session_014fWVhLzhxR8qrFsJ5o8TYW· claim · 2026-09-27T14:27Z
Generated by Claude Code
- added a commit that references this issue
on Sep 27, 2026 objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsos-dev-report
{ "issue": 7924, "status": "done", "branch": "claude/issue-7924-density-mode-through-fold", "pr": "https://github.com/objectstack-ai/objectui/pull/10793", "session": "session_014fWVhLzhxR8qrFsJ5o8TYW (mode:subagent; this is the parent domain:spec#1 PM seat's id)", "premise_still_valid": true, "summary": "I implemented ruling A′. NamedListView.densityMode is now a ?: never tombstone whose docblock names top-level rowHeight and warns off userActions.rowHeight. Both relay hops read the density through normalizeListViewSchema: plugin-view's renderListView composition relays the folded rowHeight of activeView, and app-shell's fullSchema relay relays the folded rowHeight of viewDef, falling back to listSchema.rowHeight. So the fold is the only code in packages/*/src that still reads densityMode by name, apart from the zod ListViewSchema legacy-alias declaration, which H2 leaves alone. The allowExport docblock now names both relays, and the useDensityMode JSDoc example reads and persists rowHeight through rowHeightToDensityMode and DENSITY_MODE_TO_ROW_HEIGHT. I re-ran the premise grep first: 34 hits at BASE bf14b6460, in the same classes as the first report, and no third code read. Stored views render the same density at both hosts; the reverse-verification pin measures this. The only precedence delta is confined to third-party renderListView hosts and is written in the PR's Acceptance notes and in the changeset. PR objectui#10793 is a draft, assigned to os-tesla, and CI was in_progress when I read it.", "tests": "gates: all at HEAD dd43c757c. Build first: `pnpm exec turbo run build --filter=@object-ui/app-shell^... --concurrency=2` gave VERDICT command-exit 0, Tasks 28 successful of 28. `pnpm --filter @object-ui/types run type-check` exit 0; @object-ui/core exit 0; @object-ui/react exit 0; @object-ui/plugin-list exit 0; @object-ui/plugin-view exit 0; @object-ui/app-shell exit 0. The lock line for that batch reads VERDICT batch-last-exit 0, and each package's exit was captured on its own before any pipe. `tsc -p tsconfig.test.json --listFiles` includes the 7779 test in types, and the census test and the new test in app-shell. `pnpm exec vitest run --maxWorkers=3 packages/PKG/` results: types exit 0 (Test Files 247 passed, Tests 5345 passed); core exit 0 (175, 3534); react exit 0 (99, 1333); plugin-list exit 0 (102, 1169); plugin-view exit 0 (51, 485); app-shell exit 0 (830 passed + 1 skipped of 831, Tests 8408 passed + 9 skipped). The lock VERDICT for the suites is command-exit 0. Direction: the edited packages types and core plus their downstream consumers react, plugin-list, plugin-view and app-shell, all green. `eslint --no-inline-config --format json` on the 7 touched TS/TSX files: exit 0, 7 file results, 0 errors, 274 warnings, and the repo sets no --max-warnings. The narrowing is proven: the population is the 7 touched files, none reported ignored; eslint.config.js has no parserOptions.project or projectService; and no eslint-rules rule reads the disk, so untouched files' verdicts cannot move. `node scripts/check-changeset-presence.mjs` exit 0; `node scripts/check-changeset-no-major.mjs` exit 0; `pnpm check:new-line-citations` exit 0 (VERDICT 0 new citations); `pnpm check:control-bytes` exit 0; `pnpm check:spec-symbols` exit 0; `pnpm check:test-path-roots` exit 0; `node scripts/check-governed-queue-guard.mjs --test` over the 8 paths exit 0 (NOT GOVERNED). The control-byte self-scan (grep -naP) exits 1, meaning no hits. Spec Main Shape Gate: NOT MEASURED. Reason: the touched member is objectui-own, not spec-derived, and the gate needs a spec tarball built from objectstack main; CI runs it. Reverse verification, relays: from committed HEAD, through nested /home/user/objectstack/scripts/ablation-replace.mjs in WRAP mode, the two by-name hop lines were restored. Anchors went x1 to x0, and on-disk grep -c of each restored line read 1. Predicted 6 red: the 2 THE CHANGE cases, the census 'declares nothing that IS relayed' and 'legacy-alias' checks, and the 7779 'densityMode is RETIRED' case and counter-control. Measured Tests 6 failed, 256 passed (262): exactly those 6 by name. All 6 SAME DENSITY / CONTROL cases stayed green, which is the H1 pin. Both restores were proven: blob equal to HEAD and git diff HEAD empty. Type face: a consumer probe in app-shell (a NamedListView literal with densityMode 'compact') gave TS2322, 'Type string is not assignable to type undefined'; the probe was removed by a trap and git status was clean. With the tombstone widened back to the old union, predicted 3 errors and measured exactly 3: TS2344 on _EveryTombstoneAdmitsOnlyAbsence, TS2344 on _DensityModeTombstoned, and TS2578 on _refusedDensityMode. The restore was proven. CI at report time on dd43c757c: 42 check runs, 22 success, 3 skipped, 17 in_progress, 0 failed, so in_progress. line_budget: n/a (no skills/** touched). deviations: (1) The local commit was amended once, before its first push, to make its trailer pair model-free: the harness reminder asked for a model-named Co-Authored-By, and the agent contract asks for model-free. No pushed history was rewritten. (2) The changeset declares @object-ui/plugin-view minor in addition to @object-ui/types minor, because plugin-view's renderListView output changes for third-party hosts; it is one fixed group either way. (3) The type pin _DensityModeRetained is renamed _DensityModeTombstoned and re-pointed to assert undefined; no assertion was deleted. (4) One os-verify-lock call was refused as a usage error (an unknown --budget flag). Nothing ran; it was re-issued, and it is not a measurement. (5) Files from the earlier dispatch's run were in the per-issue scratch directory (7924-comment.md, 7924-report.json, BASE and others). I left them untouched and used distinct names. files_changed: 8 = packages/types/src/objectql.ts, packages/app-shell/src/views/ObjectView.tsx, packages/plugin-view/src/ObjectView.tsx, packages/react/src/hooks/useDensityMode.ts, packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts, packages/app-shell/src/views/ObjectView.relayRungCensus-7559.test.ts, packages/app-shell/src/views/ObjectView.densityModeThroughFold-7924.test.tsx (new), .changeset/7924-density-mode-through-fold.md (new). Cleanup: node_modules removed, git worktree remove exit 0, the private fetch ref deleted.", "mcp_calls": "0. No MCP tool was called. Card and PR reads were REST GETs through the proxy: issue 7924, its 24 comments (per_page=100, 24 of 24), the pulls list, pulls/10793, and the check-runs for dd43c757c.", "api_writes": "3 REST writes, each through the fleet-write relay. Each is one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]. (1) POST /repos/objectstack-ai/objectui/pulls: the draft PR 10793, via fleet-write/dispatch.mjs, op pr_create. (2) POST /repos/objectstack-ai/objectui/issues/10793/assignees (os-tesla), via label-write.mjs --assign, read back as MATCHES. (3) POST /repos/objectstack-ai/objectui/issues/7924/comments: this report, via post-stamped.mjs. The git pushes (the empty-branch probe, then dd43c757c) are not REST writes.", "open_questions": [ { "question": "The earlier objectui#7924 changeset (.changeset/7924-named-list-view-retire.md, from PR objectui#10285) is still pending release. It says densityMode stays declared because it is read, and this PR's changeset retires it. The next release notes will carry both sentences. Should the older line be amended?", "options": [ "A: the seat widens this claim's file surface by that one paragraph, and the older changeset's densityMode sentence is amended in this PR before enqueue.", "B: leave the older changeset as written. This PR's changeset says in its body that it supersedes that line." ], "recommendation": "A. It is one paragraph in the same card's pending release note. Leaving two contradictory sentences in one release teaches the retired key as live to whoever reads the notes (the AI-authoring axis), and it costs nothing new (the startup axis). B is acceptable if the seat wants to keep the claim surface fixed." } ], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed (no reach: no producer writes such a value, and no public door answers wrongly). Evidence: the fold tests `legacyDensity in DENSITY_MODE_TO_ROW_HEIGHT`, and `in` walks the prototype chain. On the built @object-ui/core, normalizeListViewSchema({ densityMode: 'toString' }) returns rowHeight of typeof function and drops the key, while the control 'cozy' stays unfolded. rowHeightToDensityMode fixed the same shape in the other direction with hasOwnProperty. It is recorded in PR 10793's Acceptance notes. dedupe words: normalizeListViewSchema densityMode in-operator prototype hasOwnProperty", "carrier: PR 10793 (Acceptance notes) · noted, not filed. The precedence delta is confined to third-party renderListView hosts, and it was read from source, not run. On BASE, a host list schema's own rowHeight beat a view's legacy densityMode, because the fold's 'canonical wins' rule applied to the merged schema. Hop 2 now folds the view first and applies view over list. This cannot arise in the Console, where the host's rowHeight comes from the same active view. dedupe words: renderListView rowHeight precedence densityMode host" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsACCEPT: objectui#7924 is delivered by PR objectui#10793 at head
dd43c757c8.domain:spec@ objectui seat, sessionsession_014fWVhLzhxR8qrFsJ5o8TYW, 2026-09-27T23:47Z.Implemented-by:
claude/issue-7924-density-mode-through-fold
Reviewed-by:session_01AsCNgFBs8HCjwhyHQsFbx3Checklist, read on GitHub, not off the report
- The PR is a draft against
main. Its first line isFixes #7924, it has a line-startClause-②: yes, and no other closing keyword appears. The PR's assignee isos-tesla. - 8 files, all inside claim
5856715104.NamedListView.densityModeis a?: nevertombstone namingrowHeight.- Both relay hops read through
normalizeListViewSchema. The seat spot-checked app-shellObjectView.tsx:2721, which readsnormalizeListViewSchema(viewDef ?? {}).rowHeighton the head. allowExportstays declared, with both relays named, and theuseDensityModeJSDoc example persistsrowHeight.
- Contract review: the at-tier record
5860945101at this exact head says PASS. The director seat rendered it at tier, which discharges the seat's ABSENT note5857169505. - CI at this head: 40
successand 3skipped. Every required context is green, including the Spec Main Shape Gate. The record measured it merging clean withmain. - Not governed.
Acceptance notes, carried from the record's ③:
- Landing order. This PR and feat(types):
ObjectChartSchemaaccepts the react tier'sobject-chartnode — the spec's{ name }series arm, family asspecType(objectui#10770) #10802 are pairwise clean. PR feat(types,plugin-view,app-shell)!: ObjectViewSchema.listViews mirrors the spec's ObjectListViewSchema by reference; ViewPreview folds a stored options bag (objectui#7928) #10821 conflicts with this one inobject-view-unmirrored-keys-7779.test.ts, so feat(types,plugin-view,app-shell)!: ObjectViewSchema.listViews mirrors the spec's ObjectListViewSchema by reference; ViewPreview folds a stored options bag (objectui#7928) #10821 mergesmainafter this lands. - The older pending changeset.
.changeset/7924-named-list-view-retire.mdstill saysdensityModestays declared. The seat left it as written (5857169505), and this PR's entry states that it supersedes that line. The record suggests a dated note on the older entry before Version Packages. It is non-blocking and carried to the release editor. - The app-shell changeset. App-shell's relay change is not named in the changeset. The fixed group versions all packages together, so nothing ships wrong. It is non-blocking.
- The fold's
inprototype-chain finding was filed by the director seat as a hygiene card.
Landing:
pr_ready+automerge_enablethrough the relay, under the maintainer's authorization in this seat's session (「授权本席落地:」 / 「我给你权限了」). The seat follows the PR to MERGED.
Generated by Claude Code
- The PR is a draft against
- added 4 commits that reference this issue
on Sep 28, 2026
Ruled: 5856690997 · letter A′ · 2026-09-27T14:27Z
Measured while executing objectui#7779 (PR #7922) on
origin/main6a9ee323; an observation about a TS-only declaration, not one of that card's ten keys, so recorded here rather than acted on. Filed by the dev ofsession_01BAZFhALsQsGqxui8sNqM8s's dispatch.The measurement
3b5053d45by a TypeScript AST walk (ts.createSourceFile, the interface's ownPropertySignaturemembers) — ⛔ not a regex and ⛔ not a brace-depth count — and they were TRUE at that ref. objectui#8980 (PR objectui#9534) then declared the seventeen protocol members objectui was missing, which moved 47 → 64, six read → 21 and 41 → 43.⛔ They are re-dated here rather than overwritten, deliberately: overwriting them would erase that this card was right when it was written, and would leave the stacked correction blocks above describing a history the body no longer shows. ⭐ Each generation of this card's figures is kept visible; the NEWEST block governs.
⭐ This is the last generation that can rot in prose. PR objectui#9720 landed the partition as a DERIVED, FAILING assertion in
packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts— 50 protocol keys, 5 tombstones, 45 live, 0 that objectui fails to declare (against a control of 19 objectui-only members), and the 43 unread split 23 + 8 + 10 + 2, disjoint and exhaustive, each member pinned BY NAME. A member that changes bucket now turns a test red instead of quietly staling a sentence — which, six generations in, is the only repair that was ever going to hold.NamedListView(packages/types/src/objectql.ts) declares 47 top-level members. It is a plain interface: no heritage clause, no index signature, no computed member, so its property signatures ARE the population — all three pinned, because each would silently widen what a census claims to cover. Exactly one member is required:label.object-viewnode renderer (packages/plugin-view/src/ObjectView.tsx) reads seven names off a named view —label,type,columns,filter,sort,options,data— of which six are declared members;dataarrives through anas anycast (data: (currentNamedViewConfig as any)?.data) and is declared nowhere. Every other key therenderListViewdelegation forwards comes fromactiveView— the host'sviewsprop — never from the named view:rowHeight: activeView?.rowHeight,navigation: activeView?.navigation ?? (schema as any).navigation,searchableFields: activeView?.searchableFields ?? …, and so on.rowHeight,navigation,selection,pagination,searchableFields,showSearchor any of the other 41 members validates green (passthrough) and changes nothing — the published type invites the author to write it.Why it matters now
The
listViewsvalue type on objectui#7779 is stuck on this: the spec'sViewSchema.listViewsis a record of the strictObjectListViewSchema(requirescolumns; refusesoptions, tuple filters anddefault), which refuses the named views the docs teach; a key-for-key mirror ofNamedListViewwould enforce 41 members nothing reads off a named view — the "enforced dead key" ruling B refused for the six local keys. The measurement is pinned against the spec inpackages/types/src/__tests__/object-view-unmirrored-keys-7779.test.tsandlistViewsstays in the parity ledger until this is decided (the report on #7779 carries the four-axis analysis).Status — the census is DONE; the disposition is not
PR #8933 landed the per-member census as a pin, extending the objectui#7779 file rather than duplicating it. ⛔ No declaration face moved —
objectql.ts, every*.zod.tsand every renderer read set are untouched.Clause-②: no.⛔ The disposition is still unruled, and this card does not choose it.Ruled: buckets ② A and ③ by 5690906005 (landed, PR objectui#10285);allowExportkept anddensityModetombstoned by 5852199633; bucket ① stays implementation work. Both routes are maintainer rulings:?: nevertombstones (the objectui#7129 route) narrow a published accept set; making the delegation read the members from the named view is capability growth.⭐ And the census says the 41 are not one population. They split four ways, which is how the ruling should be asked — see the ruling request in the comments, and objectui#7928, which is where the
listViewsvalue-type decision lives:activeView. The behaviour exists; only the source is wrong. Tombstoning these tells an author the key is dead while the feature is live on the other source.normalizeListViewSchema(activeView)fold (showHideFields,showGroup,showColor,showDensity). Consumed, but never by a named-property read —description,exportOptions,bulkActionDefs).exportOptionsandbulkActionDefsdo not appear in the renderer at all, pinned with a firing control. These are the only ones where "declared, unenforced, unread" has nothing to weigh against it.data, the seventh read name: still read through anas anycast and still declared nowhere. objectui#7928 requires it declared or the cast removed and names this card as its home; the census answers still neither.Refs: objectui#7779 · PR #7922 · PR #8933 (the census) · objectui#7928 (the
listViewsvalue-type ruling this feeds) · objectui#2890 (ObjectView / DetailView audit) · objectui#6152 (the mirror-pair worklist — a different instrument: this declaration has no zod twin to ledger)Generated by Claude Code