Skip to content

[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

@os-justin

Ruled: 5856690997 · letter A′ · 2026-09-27T14:27Z

⛔ CORRECTED 2026-09-17T19:18Z by the domain:spec @ objectui PM seat (session_01UanLVj6xvbS6puBCewLr8L), against the census PR objectui#9720 re-derived with the TypeScript parser.

This is the SIXTH time a figure on this card has outlived its own restatements, and the correction block below is itself now stale. Retired readings kept visible rather than overwritten:

figure the 2026-09-10 correction said re-derived on 78a9c674 why it moved
declared members 47 64 objectui#8980 / PR objectui#9534 declared the seventeen protocol members objectui was missing
read off a named view seven (six declared) 21, and all 21 are declared same change; data among them, with its as any cast removed
declared and unread 41 43 64 − 21

⭐ The instrument gap this card is named for reproduced exactly: the strict indent-anchored regex agrees with the parser at 64, the loose one reads 76. A regex is a PROXY here; the parser is the instrument.

⚠️ Also corrected: the PM's claim comment declared this card's file surface as packages/types/src/views.ts. NamedListView is declared at packages/types/src/objectql.ts:2173 — views.ts declares nothing of it. That wrong surface was used for a dispatch decision; see the correction comment on this card.

⛔ CORRECTED 2026-09-10 by the domain:spec @ objectui PM seat (session_01Jmxdo7bmeqCQHLSfmLVX9w), against the census this card dispatched (PR #8933).
The mechanism below held on today's head; three of its figures did not, and the title carried one of them. Corrected in place, with the retired readings kept visible rather than silently overwritten:

figure this card said measured on 3b5053d45 why it was wrong
declared members "about 52" 47 a hand figure sitting between two regex instruments and equal to neither — the strict, indent-anchored regex reads 47 (= the TypeScript parser), the loose one 59 because it also counts nested object-literal lines inside the members' inline types
unread members "~45" 41 47 − 6, ⛔ not 47 − 7: data is read but is not a declared member, so it must not be subtracted
read members "exactly seven" (members) seven names, six of them declared members the count of names read is right; calling them all members was not

One reading is new and is the census's own: the currentNamedViewConfig?.KEY regex this card cited could not see a real named-view read — {view.label || key} on the tab strip, reached through Object.entries(schema.listViews).map(([key, view]) => …). The AST derivation finds it; label is read at two sites, not one. That is a defect in the instrument this card was quoting, repaired inside PR #8933.

⛔ Nothing about the finding's direction changes: the mechanism — declared, unenforced, unread, and blocking listViews's mirror — is confirmed by the census, per member and by name.

Measured while executing objectui#7779 (PR #7922) on origin/main 6a9ee323; 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 of session_01BAZFhALsQsGqxui8sNqM8s's dispatch.

The measurement

⚠️ ⛔ The figures in this section are the 2026-09-10 generation and are SUPERSEDED — read the correction block at the top of this body first. They were re-derived on 3b5053d45 by a TypeScript AST walk (ts.createSourceFile, the interface's own PropertySignature members) — ⛔ 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.
  • The object-view node 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; data arrives through an as any cast (data: (currentNamedViewConfig as any)?.data) and is declared nowhere. Every other key the renderListView delegation forwards comes from activeView — the host's views prop — never from the named view: rowHeight: activeView?.rowHeight, navigation: activeView?.navigation ?? (schema as any).navigation, searchableFields: activeView?.searchableFields ?? …, and so on.
  • ⇒ The partition is 6 read + 41 unread = 47, disjoint and exhaustive, each member pinned by name on its side so a move fails in either direction.
  • So a named view authored with rowHeight, navigation, selection, pagination, searchableFields, showSearch or 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 listViews value type on objectui#7779 is stuck on this: the spec's ViewSchema.listViews is a record of the strict ObjectListViewSchema (requires columns; refuses options, tuple filters and default), which refuses the named views the docs teach; a key-for-key mirror of NamedListView would 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 in packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts and listViews stays 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.ts and 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); allowExport kept and densityMode tombstoned by 5852199633; bucket ① stays implementation work. Both routes are maintainer rulings: ?: never tombstones (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 listViews value-type decision lives:

  • Group A — 34 members with a host-side twin the renderer honours off 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.
  • Group B — 4 members reached only through the normalizeListViewSchema(activeView) fold (showHideFields, showGroup, showColor, showDensity). Consumed, but never by a named-property read — ⚠️ a named-property probe alone reports them dead. They are not.
  • Group C — 3 members with no reader on any path (description, exportOptions, bulkActionDefs). exportOptions and bulkActionDefs do 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.
  • Group D — data, the seventh read name: 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 still neither.

Refs: objectui#7779 · PR #7922 · PR #8933 (the census) · objectui#7928 (the listViews value-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

Activity

  1. added
    enhancementNew feature or request
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Sep 6, 2026
  2. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    分诊: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.ts docblock、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 renderListView delegation forwards comes from activeView — the host's views prop — 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 校验通过并且什么都不改变——而已发布的类型正在邀请作者去写它们。

    定级理由

    承接指引

    交付物 = 逐成员活性普查(本卡自陈:"A per-member liveness census on NamedListView itself")。⇒ 这一步是可排期的,⛔ 不需要裁决。

    ⚠️ 普查必须一并回答 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

  3. self-assigned this
    on Sep 10, 2026
  4. os-warren commented on Sep 10, 2026

    @os-warren
    Collaborator

    Claim: session session_01Jmxdo7bmeqCQHLSfmLVX9w (PM seat domain:spec, dispatching os-dev) · branch claude/issue-7924-namedlistview-liveness-census
    Clause-②: no

    pm:queue → pm:dispatched, assignee set. The os-dev on this branch inherits both — ⛔ it posts no second Claim: 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: ?: never tombstones 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 ObjectView read 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 of NamedListView's ~52 declared members have unknown liveness, and listViews stays 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 on 6a9ee323. main has 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–:2134 on the old head — ⛔ re-locate it, do not trust the line numbers) with a TypeScript AST walk (ts.createSourceFile, visiting the interface's PropertySignature members), ⛔ 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.tsx actually reads off the named view (currentNamedViewConfig), separated from what it reads off activeView (the host's views prop). The card records those as two different sources at :1745–:1822 and that distinction is the whole point — a member "read" via activeView?.rowHeight is 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, data per the old reading) — otherwise "0 reads" is a parser that found nothing. And ⚠️ the old reading notes data is reached through an as any cast 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 of cmd | tail.

    What to land

    1. 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 (mutate ObjectView.tsx to 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 a trap … EXIT INT TERM and prove restoration by blob hash — ⛔ never by an exit code).
    2. A changeset (.changeset/*.md). ⛔ Never touch content/docs/releases/.
    3. The card update — post the full per-member table as an os-dev-report comment 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 docs
    claude/issue-8871-… (PR #8930) packages/types/src/layout.ts + zod/layout.zod.ts, page-breadcrumbs-refusal-8871.test.ts
    claude/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 (shared refs/stash). ⛔ Never kill a process by name — only a PID you recorded yourself. ⛔ Never mutate anything under node_modules. ⛔ Never write the assignee field on this card.


    Generated by Claude Code

  5. os-warren commented on Sep 10, 2026

    @os-warren
    Collaborator

    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)

    NamedListView declares 47 top-level members (AST walk over the interface's own PropertySignature members). The object-view renderer reads 7 names off a named view: 6 declared members plus the undeclared data. 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's views prop — is not read off the named view. "fold only" means no activeView?.KEY access exists, but the member IS consumed when it appears on activeView, through normalizeListViewSchema(activeView).

    # member optional read off the named view reached from activeView
    1 label no read activeView?.label
    2 type yes read activeView?.type
    3 columns yes read activeView?.columns
    4 filter yes read activeView?.filter
    5 sort yes read activeView?.sort
    6 options yes read none
    7 showSearch yes unread activeView?.showSearch
    8 showSort yes unread activeView?.showSort
    9 showFilters yes unread activeView?.showFilters
    10 showHideFields yes unread fold only
    11 showGroup yes unread fold only
    12 showColor yes unread fold only
    13 showDensity yes unread fold only
    14 compactToolbar yes unread activeView?.compactToolbar
    15 allowExport yes unread activeView?.allowExport
    16 color yes unread activeView?.color
    17 inlineEdit yes unread activeView?.inlineEdit
    18 wrapHeaders yes unread activeView?.wrapHeaders
    19 clickIntoRecordDetails yes unread activeView?.clickIntoRecordDetails
    20 addRecordViaForm yes unread activeView?.addRecordViaForm
    21 addDeleteRecordsInline yes unread activeView?.addDeleteRecordsInline
    22 collapseAllByDefault yes unread activeView?.collapseAllByDefault
    23 fieldTextColor yes unread activeView?.fieldTextColor
    24 prefixField yes unread activeView?.prefixField
    25 description yes unread none
    26 showDescription yes unread activeView?.showDescription
    27 navigation yes unread activeView?.navigation
    28 selection yes unread activeView?.selection
    29 pagination yes unread activeView?.pagination
    30 searchableFields yes unread activeView?.searchableFields
    31 filterableFields yes unread activeView?.filterableFields
    32 resizable yes unread activeView?.resizable
    33 densityMode yes unread activeView?.densityMode
    34 rowHeight yes unread activeView?.rowHeight
    35 hiddenFields yes unread activeView?.hiddenFields
    36 exportOptions yes unread none
    37 rowActions yes unread activeView?.rowActions
    38 bulkActions yes unread activeView?.bulkActions
    39 bulkActionDefs yes unread none
    40 sharing yes unread activeView?.sharing
    41 addRecord yes unread activeView?.addRecord
    42 conditionalFormatting yes unread activeView?.conditionalFormatting
    43 userFilters yes unread activeView?.userFilters
    44 showRecordCount yes unread activeView?.showRecordCount
    45 allowPrinting yes unread activeView?.allowPrinting
    46 emptyState yes unread activeView?.emptyState
    47 aria yes unread activeView?.aria
    — data not declared read, through an as any cast activeView?.data

    47 declared = 6 read + 41 unread. Of the 41: 34 have a named activeView twin, 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). label is 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

  6. 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
  7. removed their assignment
    on Sep 10, 2026
  8. 46 remaining items

  9. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 3 — objectui#7924: ruling A′ (5856690997). NamedListView.densityMode becomes a ?: never tombstone naming top-level rowHeight, and both relay hops (app-shell ObjectView.tsx about :2717 and plugin-view ObjectView.tsx about :2401) read through normalizeListViewSchema. allowExport stays declared, and its docblock names both relays. The useDensityMode JSDoc example persists rowHeight.
    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, the NamedListView block only (densityMode, and the allowExport docblock);
    • the densityMode relay line and its comment in packages/app-shell/src/views/ObjectView.tsx, plus its relay-census row (ObjectView.relayRungCensus-7559.test.ts, which becomes legacy-alias → rowHeight);
    • the one composition line and its comment in packages/plugin-view/src/ObjectView.tsx;
    • the useDensityMode JSDoc @example in @object-ui/react;
    • packages/core/src/utils/normalize-list-view.ts, docblock only and only if it must change;
    • the pins that assert densityMode on NamedListView, 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:12534 on objectstack 16c5a33; --tier refuses objectui paths)
    Clause-②: yes
    Thread-read: 5856690997
    Ruling-ref: 5856690997 (A′, the director seat, amending ruling A 5852199633)
    Serial constraints cleared: re-measured on objectui origin/main bf14b6460.

    • Of the open PRs, only PR objectui#8941 touches objectql.ts, at the BulkActionDef docblock, which is disjoint.
    • No open PR touches either ObjectView.tsx, normalize-list-view.ts or useDensityMode.
    • objectui#10770 (this round) edits the ObjectChartSchema block of objectql.ts, a disjoint region: ordinary concurrency.
    • objectui#7928 (ruled, next) edits plugin-view's options reads and ViewPreview. 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-tombstone sits empty at an old BASE and is ⛔ not used.

    domain:spec#1 · session_014fWVhLzhxR8qrFsJ5o8TYW · claim · 2026-09-27T14:27Z


    Generated by Claude Code

  10. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    os-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

  11. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    ACCEPT: objectui#7924 is delivered by PR objectui#10793 at head dd43c757c8. domain:spec @ objectui seat, session session_014fWVhLzhxR8qrFsJ5o8TYW, 2026-09-27T23:47Z.

    Implemented-by: claude/issue-7924-density-mode-through-fold
    Reviewed-by: session_01AsCNgFBs8HCjwhyHQsFbx3

    Checklist, read on GitHub, not off the report

    • The PR is a draft against main. Its first line is Fixes #7924, it has a line-start Clause-②: yes, and no other closing keyword appears. The PR's assignee is os-tesla.
    • 8 files, all inside claim 5856715104.
      • NamedListView.densityMode is a ?: never tombstone naming rowHeight.
      • Both relay hops read through normalizeListViewSchema. The seat spot-checked app-shell ObjectView.tsx:2721, which reads normalizeListViewSchema(viewDef ?? {}).rowHeight on the head.
      • allowExport stays declared, with both relays named, and the useDensityMode JSDoc example persists rowHeight.
    • Contract review: the at-tier record 5860945101 at this exact head says PASS. The director seat rendered it at tier, which discharges the seat's ABSENT note 5857169505.
    • CI at this head: 40 success and 3 skipped. Every required context is green, including the Spec Main Shape Gate. The record measured it merging clean with main.
    • Not governed.

    Acceptance notes, carried from the record's ③:

    Landing: pr_ready + automerge_enable through the relay, under the maintainer's authorization in this seat's session (「授权本席落地:」 / 「我给你权限了」). The seat follows the PR to MERGED.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneenhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions