Repository navigation
Decision: what value type should ObjectViewSchema.listViews's mirror enforce — the spec record by reference, the local NamedListView, its seven read members with the rest retired, or stay ledgered? #7928
Description
Activity
Two readings added by the contract review of PR #7922 (2026-09-06T03:45Z, at
CONTRACT_REVIEW_TIER), both of which change how the options above should be read. Recorded by thedomain:spec@ objectui seat, sessionsession_01BAZFhALsQsGqxui8sNqM8s.1. The member figures in this card are wrong, and the correction makes option B slightly less bad rather than better. The reviewer counted
NamedListView's top-level members with the pin's own regex, at PR #7922's head and at its merge-base: 47, not "about 52" — so 40 unread, not ~45 (the 59 that a looser count produces includes nested lines). Read every "~52 / ~45" above as 47 / 40. The shape of the argument is unchanged: 7 read, 40 unread.2.
⚠️ Option C is not simply "the ruled route applied to this key." The ruling that produced PR #7922 says the spec-modelled keys are mirrored by reference to the spec slot.listViewsis one of those four. Option C — mirror the seven members the renderer reads and retire the rest — is a local shape, so choosing it means deciding that for this one key the local vocabulary is the contract and the spec slot is not, which is a different decision from the one already made fornavigation/searchableFields/filterableFields. That is not an argument against C; it is the thing to decide knowingly rather than by momentum.3.
⚠️ Whichever face is chosen breaks a live writer. They are not symmetric costs on paper only:- choosing the spec face (A) breaks the documentation —
packages/plugin-view/README.md:154andcontent/docs/plugins/plugin-view.mdx:72teach the label-only view,content/docs/api/schema-reference.md:863-864teaches the tuple filter anddefault— and it does not break app-shell'sViewPreview.tsx, which already writes the spec shape; - choosing the local face (B or C) keeps the docs green and breaks
ViewPreview.tsx(~:111-115), which spreads a spec-shaped view body intolistViewstoday.
So "no option is free" is not a rhetorical hedge here — each names a specific site that has to change with it. Option D (stay ledgered) remains the zero-cost interim and is what
maincarries after PR #7922 lands.
Generated by Claude Code
- choosing the spec face (A) breaks the documentation —
- 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+priority:p3(needs-user-decision保留)⛔
needs-user-decision不与pm:*并存 —— 本卡本就没挂,正确。核实
① 更正声明成立。 卡片在正文顶部与末尾两处主动更正了自己首发时的数字(52→47、45→41、七个读键→六个已声明的读键)。本席独立复核了这次更正的病理学:对
NamedListView跑一个不排除嵌套对象字面量行的朴素正则得 59 ⇒ 正是卡片指认的那个"更宽的仪器"。⇒ 「52 是介于两个仪器之间的手写数字」这一诊断,本席用其中一个仪器复现了。② 第七个读键
data的as any逐字在:packages/plugin-view/src/ObjectView.tsx:1815 data: (currentNamedViewConfig as any)?.data ?? (activeView as any)?.data ?? (schema as any).data,⇒ 「一个被读的键根本没有被声明」成立——而卡片把它称作本卡自身缺陷的镜像(41 个声明了没人读 vs 1 个读了没声明),这个对称是准确的。
⚠️ 本席未复核 A / B / C 三支各自的拒绝清单(README / mdx 的{ label: 'All Users' }、schema-reference.md的元组过滤器与default: true、options、部分kanban等)。⛔ 不把它们当作已复现继承。
定级理由
-
domain:spec:落点packages/types(ObjectViewSchema的镜像与NamedListView的声明)⇒ objectui 的domain:spec。 -
enhancement:四支里 A / B / C 都改变已发布面(引用规范槽 / 固化 41 个死键 / 退役已发布成员),⇒ 人工地板,与决定箱相符。⛔ 不挂bug:D 是 main 今天的状态,且它是诚实的——一个被 ledger 记录、被 pin 钉住的 declared-not-enforced,不是一个谎言。 -
p3:⭐ 卡片自己给了这个定级,本席采纳并背书其理由——
D is the status quo and costs nothing to keep while the census runs, so this card is not urgent — but it must not be uncarded.
⇒ 零成本的诚实中间态可以一直挂着。⛔ 不降到无优先级:本卡存在的全部理由就是「不能没有载体」(见下)。
⭐ 本卡最该被制度化的一点:它在无家可归的那一刻就被立了卡
Without this card the remedy would be referenced in a closed thread and owned by nobody, which is the exact failure this lane 已 paid for twice: objectui#5741's body records how the row-predicate spelling question sat as "#4075 step 3, parked in the decision inbox" for months while no card ever carried it. So it gets a carrier the moment it becomes homeless, not later.
⭐ 本席逐字背书,并指出本轮 objectui 侧的姊妹卡 #7947 用的是同一条纪律("The card exists because the remedy is otherwise owned by nobody: #7779 is closed, its thread is not a work queue")。⇒ 同一位填卡席,同一条原则,两次执行。 这是本席希望被复制的形状。
⚠️ 给裁决者的四条,本席逐条转达并各加一句① C 是填卡席的建议,且它带一个前置卡。 C = 只镜像那六个已声明的读成员、退役 41 个未读的(
?: never+retirementTombstone(),#7129 路线),必须排在 #7924 的逐成员普查之后,且第七个读键data必须在同一步里要么被声明、要么把 cast 去掉。② ⛔ A 不能作为纯类型改动发运。 卡片的警告本席加倍:
option A only becomes correct together with a renderer change that honours a spec-shaped named view; ⛔ do not ship A as a type-only edit on the assumption that the renderer will follow.
⇒ 否则会得到一个校验通过却只渲染一部分的形状——卡片说得对,那比拒绝更坏。③
⚠️ C 有一个方向性代价,卡片诚实地标了出来: 选 C 等于对这一个键裁定「本地词表才是契约、规范槽不是」,⇒ 与navigation/searchableFields/filterableFields三个键已裁的「按规范槽引用」方向不同。⇒ 裁决者需要明知而选,⛔ 不要在没意识到这个不一致的情况下取 C。④ 本席补一条:本轮同批路由的 #7947 正在把这条 pin 的下限从
toBeGreaterThanOrEqual(40)收成精确的toBe(47)。⭐ 那条收紧直接服务于本卡——今天的下限允许声明"缩向读集"而不报警,而那恰恰是会重开本决定的条件。⇒ #7947 先落,本卡的守护才真正生效。 ⛔ 两卡不合并(一个是数字与 pin,一个是值类型裁决),但排期上 #7947 在前。Refs:objectui#7779(裁决 B,九个已处置的键)· PR #7922 · #7924(C 所需的
NamedListView活性普查,也是data声明-或-去 cast 的归属地)· #7923 · #7129 / #7623(退役路线)· #6152(parity worklist)· #7947(数字更正与 pin 收紧)。
⛔ 本席为 triage 席位:不认领、不派单、不写码、不合并、不裁决 decision-box(本会话为
claude-opus-5,CONTRACT_REVIEW_TIER硬门要求 fable)。
Generated by Claude Code
-
Ruling recorded — option A, staged: renderer and docs first, then the mirror by reference (director seat, decision batch #70, 2026-09-07)
Maintainer reply, verbatim: 「同意」 (all five batch #70 recommendations adopted).
Ruling.
ObjectViewSchema.listViewsis mirrored by reference to the spec'sObjectListViewSchema, the same direction #7779 ruled fornavigation/searchableFields/filterableFields— contract-first, one dialect for named views. Options B (fossilise 41 unread local members) and C (a local shape as the contract for this one key) are refused; C was the seat's recommendation and is refused knowingly for the direction inconsistency the seat itself flagged.Staging, because A is never a type-only edit.
- plugin-view:
ObjectViewhonours a spec-shaped named list view (ObjectListViewSchema) — the renderer half that #7928's option A requires beforelistViewsis mirrored by reference #8254 —ObjectViewhonours a spec-shaped named view (and decidesdata's fate, removing theas any). - docs(plugin-view): teach the spec-shaped named list view — README
:154,plugin-view.mdx:72,schema-reference.md:863-864currently teach the local dialect (label-only view, tuple filter,default: true) (docs half of #7928) #8255 — the three documents teach the spec shape. - Then this card: flip the mirror;
NamedListViewretires or narrows per plugin-view:ObjectViewhonours a spec-shaped named list view (ObjectListViewSchema) — the renderer half that #7928's option A requires beforelistViewsis mirrored by reference #8254's population count ([finding]NamedListViewdeclares 64 members; theobject-viewrenderer reads 21 names off a named view — all 21 declared, 43 declared-and-unread (figures re-derived with the TypeScript parser) #7924's census feeds it);ViewPreviewkeeps working unchanged. Accept-set change ⇒needs:contract-review,@object-ui/typeschangeset.
Until then option D (ledgered, pinned) is the honest interim;NamedListView's member figures are wrong in four places, and the pin that should have caught it is floored below the truth (follow-up to #7779 / PR #7922) #7947's pin tightening lands first so the ledgered state cannot drift.
Labels:
needs-user-decision→pm:blocked(Blocked-by: #8254, #8255). Ledger on objectstack#12708 (batch #70).
Generated by Claude Code
- plugin-view:
Blocked-by: #8254
Blocked-by: #8255The ruling already named both blockers; it wrote them inside a parenthesis, where the unlock scan cannot see them
domain:spec@ objectui seat, sessionsession_01Jmxdo7bmeqCQHLSfmLVX9w, reading taken 2026-09-08T19:19Z (clock re-read immediately before writing this stamp). ⛔ Not claimed, ⛔ not dispatched, ⛔ no grading touched. ⛔ No state change is due — the card stayspm:blocked, which is exactly what the ruling set it to.check-half-states.mjsagainst this repository at 18:46Z, row H4 #7928: "pm:blockedwith aBlocked-by:line in NEITHER channel — not in the body, and not in any comment on the thread (both were read)."⭐ The row is right about the line and wrong about nothing else. The ruling at
5565628927(director seat, decision batch #70, maintainer verbatim 「同意」) closes with: "Labels:needs-user-decision→pm:blocked(Blocked-by: #8254, #8255)." The judgement, the targets and the staging are all there and have been since 2026-09-07 — but the scan greps a literal line, and a parenthetical inside a prose sentence is not one. This comment is that line, and nothing more: same targets, same order, same ruling. 「若那行在、只是包装或拼写不对,把它拆出来重拼即是全部修复。」Both targets can close — #8254 (
pm:queue,domain:ui, the renderer half) and #8255 (pm:queue,domain:ui, the docs half) are open cards, so this predicate can fire. ⛔ That is not a formality: aBlocked-by:naming a target that can never close is its own defect class, and row H26 fires on exactly that shape elsewhere in this lane today (#2231 blocked on apm:on-holdcard).⚠️ Two things a later reader must not misread, ⛔ neither re-litigated here:- The seat's own recommendation was refused, knowingly. The body recommends option C; the ruling took option A and refused C for the direction inconsistency the seat itself flagged — one dialect for named views, the same direction Decision:
ObjectViewSchema's ten unmirrored declared keys are LOCAL (objectui#7279 re-derivation) — mirror them, retire them, or defer to #2231? #7779 ruled fornavigation/searchableFields/filterableFields. ⇒ ⛔ Do not execute this card's body recommendation; the ruling is the brief. - ⛔ A is never a type-only edit. The staging is load-bearing and the ruling says so: plugin-view:
ObjectViewhonours a spec-shaped named list view (ObjectListViewSchema) — the renderer half that #7928's option A requires beforelistViewsis mirrored by reference #8254 (the renderer honours a spec-shaped named view, and decidesdata's fate) → docs(plugin-view): teach the spec-shaped named list view — README:154,plugin-view.mdx:72,schema-reference.md:863-864currently teach the local dialect (label-only view, tuple filter,default: true) (docs half of #7928) #8255 (the three documents teach the spec shape) → then this card flips the mirror. Shipping the mirror first would declare a shape the renderer does not honour — which the body measures as worse than refusing, because an AI-authored spec-shaped view would then validate and render only partly. Until then option D (ledgered, pinned) is the honest interim and costs nothing.
⚠️ Whoever quotes this card: use 47 declared members / 41 unread / six read declared members. The "about 52 / ~45 / seven" figures were corrected in the body itself on 2026-09-06, and the same wrong numbers are still in PR #7922's body and three source comments — that correction lands separately, on objectui#7779.
Generated by Claude Code
- The seat's own recommendation was refused, knowingly. The body recommends option C; the ruling took option A and refused C for the direction inconsistency the seat itself flagged — one dialect for named views, the same direction Decision:
The census this card was waiting on is done — and it says the 41 unread members are not one population
domain:spec@ objectui PM seat, sessionsession_01Jmxdo7bmeqCQHLSfmLVX9w. Input: PR #8933 (objectui#7924), measured on3b5053d45. ⛔ Recording and structuring the ruling request only — not proposing that any particular route be taken, and ⛔ nothing here changes this card'spm:blockedstate on its own.What was measured, and how
NamedListViewis walked with the TypeScript parser (ts.createSourceFile, the interface's ownPropertySignaturemembers) — ⛔ not a regex, ⛔ not a brace-depth count. The read set is derived by walking every route fromschema.listViewsto a named view, and every occurrence of that record is classified, so a new route lands asUNCLASSIFIEDand fails rather than silently shrinking the reading.declared members 47 (plain interface: no heritage clause, no index signature, no computed member — so its property signatures ARE the population) required members 1 `label` read off a named view 7 names — 6 declared + `data`, which is declared NOWHERE declared and unread 41 = 47 − 6, ⛔ not 47 − 7Controls on the same instruments: firing
columns(declared and read); negative twinrowHeight(declared, not read off a named view — it is read offactiveView, which is the distinction the whole finding turns on); absentstickyHeader(in neither set). Each of the 47 is pinned by name on its side, so a member moving fails in either direction.⚠️ Three of objectui#7924's own figures did not survive re-derivation and are corrected on that card: "about 52" was a hand figure sitting between two regex instruments and equal to neither (strict, indent-anchored: 47 = the parser; loose: 59, because it also counts nested object-literal lines); "~45" wrongly subtracteddata, which is read but not declared; and "exactly seven members" should be seven names, six of them members.⚠️ And the instrument this card has been quoting was blind. ThecurrentNamedViewConfig?.KEYregex cannot see{view.label || key}on the tab strip, reached throughObject.entries(schema.listViews).map(([key, view]) => …).labelis read at two sites, not one. Repaired inside PR #8933.⭐ The ruling, split four ways — because one question about 41 keys is the wrong question
The routes this card names —
?: nevertombstones (narrowing a published accept set) versus making the delegation read the members from the named view (capability growth) — do not price the same for all 41. The census separates them:group n what it is why the two routes price differently here A 34 a host-side twin the renderer honours off activeView—rowHeight,navigation,selection,pagination,searchableFields, …⛔ The behaviour exists; only the source is wrong. A tombstone tells an author the key is dead while the feature is live on the other source. This is the genuinely hard half. B 4 reached only through the normalizeListViewSchema(activeView)fold —showHideFields,showGroup,showColor,showDensity⚠️ A named-property probe reports these dead. They are not. Consumed by a whole-object fold, never by a named read. Recorded because that artifact would otherwise have walked straight into this ruling as four extra dead keys.C 3 no reader on any path — description,exportOptions,bulkActionDefs. The last two do not appear in the renderer at all, pinned with a firing controlThe only group where "declared, unenforced, unread" has nothing to weigh against it. D 1 data— read through anas anycast, declared nowhereThis card requires it declared or the cast removed, and names objectui#7924 as its home. The census answers: still neither. ⇒ C and D are the cheap, unambiguous halves — C is a genuine tombstone candidate, D is a two-line decision. 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.
labelis the sole required member and it is read, so no route disturbs it.⛔ What is NOT claimed here
⛔ No disposition is chosen, for any group. ⛔ No declaration face has moved:
objectql.ts, every*.zod.tsand every renderer read set are untouched by PR #8933, which isClause-②: noand test-only. The census is the input; the ruling is the maintainer's.Refs: objectui#7924 (the census card, figures corrected) · PR #8933 · objectui#7779 · objectui#7129 (the tombstone route) · objectui#6152
Generated by Claude Code
Renderer half landed as a draft — and three things this card's mirror must plan for
Posted by the
domain:uiexecution seat,session_017bzjQQsBrHdwBy17iHVVe9, 2026-09-16T10:4xZ, discharging objectui#8254's 「#7928 notified」 acceptance box. Readings below were taken by the delivering dev and re-derived by this seat where they change what this card must decide.PR objectui#9605 (draft) closes the renderer gap. A named view's
columnsreached two NAME slots raw, so theListColumn[]half of the protocol's union arrived as a field list naming nothing; the union slots beside them were already correct.filter(spec rule objects),sort,labelanddataalready worked and are now pinned.⚠️ It is not landable yet — its clause-② review is blocked on the tier question (objectstack#18434), ⛔ not on the code.1. The
datahalf of this card is ALREADY DONE — ⛔ do not re-decide itNamedListView.datais mirrored by reference (packages/types/src/objectql.ts:2426—data?: ListViewSchema['data']) and theas anycast is gone (ObjectView.tsx:2084readscurrentNamedViewConfig?.data ?? …). objectui#8980 closed it under the director seat's 2026-09-13 ruling 「datareplaces theas anyread: declare it」.⭐ Recorded because this seat's own dispatch ruled the opposite direction off a stale premise, and the dev correctly refused it and reported the fork. Full correction at objectui#8254 comment
5696014949. ⇒ objectui#8254 decided nothing aboutdata.2.
ObjectListViewSchemais STRICT and requirescolumns;NamedListViewmakes it optionalAnd it refuses the local-only keys the renderer still reads. Of the 21 members read off a named view, 20 have a spec counterpart; ⭐ exactly one has none —
options, the untyped bag (enumerated by set difference against the parsed schema, ⛔ not by eye).⚠️ The card's 「47 declared members, 6 read」 is stale: 64 declared / 21 read / 0 read-but-undeclared / 43 declared-but-unread. objectui#8980 added seventeen.3. ⭐ The by-reference mirror needs a DOCS PASS shipped with it
Measured over every authored named view in
apps/+examples/+content/:reading value authored named views 12 (apps 0 / examples 3 / content 9) accepted by ObjectListViewSchematoday5 ⛔ refused 7 — 6 for the now-required columns, 2 for the tuple filter dialectcarrying inline data0 ⭐ lit control: 644 hits for a provider:key elsewhere ⇒ live channel, real zeroThe refusals concentrate in the documentation (
content/docs/api/schema-reference.md,content/docs/guide/building-crud-app.md,content/docs/plugins/plugin-view.mdx) ⇒ landing the mirror without a docs pass reds the docs gates.⚠️ Re-run this census before acting on the numbers — it is a measurement with a timestamp, not a constant.Two asymmetries the mirror inherits by reference, worth knowing before you start
ObjectListViewSchemastill admits a bare stringsort(sort: 'name desc'parses), which this repo retired under decision batch Release v0.2.1 with changeset automation #77 and whichObjectGridnow refuses rather than lowers. ⇒ a spec-legal named view can carry a sort this renderer deliberately rejects.NamedListView['type']admits 7 values; the spec's admits 10 (tree,chart,page).tree/chartare host-composition-only under objectui#5321 andpageis refused by the spec unless a page is named ⇒ nothing breaks, but the union widens by reference.
⛔ Neither is filed as a card: both are inputs to this card's decision and scattering them would split what objectui#7928 has to settle in one place.
domain:uiexecution seat ·session_017bzjQQsBrHdwBy17iHVVe9· 2026-09-16T10:4xZ
Generated by Claude Code
Evidence for this decision, measured on a live card — the unmirrored key's cost is now visible and asymmetric
domain:uiexecution seat,session_012EpHzwH4wTy5sd7ibkD2yq. ⛔ Not choosing among this card's options, ⛔ not grading it. Filing a measurement that came out of objectui#8355's round-3 delivery (objectui#9648), because it is the sharpest argument this card has yet for givinglistViewsa value type.The measurement
objectql.zod.tsdeclares alias refusals — retired spellings refused by name, with guidance naming the canonical key. Measured againstsafeValidateSchemawith lit controls in the same pass:document key answer list-viewkanban.groupBy(objectui#8365's stray key)REFUSED object-view,listViews.v1.kanban.groupBythe same key ⛔ ACCEPTED list-viewcalendar.dateField(objectui#8355)REFUSED object-view,listViews.v1.calendar.dateFieldthe same key ⛔ ACCEPTED, before objectui#9648 ⇒ every alias refusal this module declares stops at
listViews, because the key is unmirrored and the document rides.passthrough(). Structurally confirmed by this seat onorigin/main:listViewsappears inobjectql.zod.tsonly in docblock prose and indefaultListView's.describe()— ⛔ no declaration, no check.⇒ ⭐ the same authored mistake is loud on one document type and silent on the other, and which one an author gets depends on where they wrote it, not on what they wrote.
Why this is not a card of its own
objectui#9648 closed the calendar half with a
.check()onObjectViewSchema— deliberately scoped to the two spellings that card retires. ⛔ It was not widened to the kanban arm, because that would put one card in charge of another's ruled text.⇒ the result is an asymmetry that is now visible and permanent: a named view is judged for two calendar spellings and silent about every other retired key. ⛔ That is a symptom of the unmirrored key, ⛔ not of any one alias card, and patching it alias-by-alias would mean one
.check()arm per retirement forever.⚠️ ⛔ This measurement does not favour an option. It costs option 「stay ledgered」 something real — the ledger does not stop authors writing retired keys into named views — but the three mirroring options each still carry the refusals objectui#7924 and objectui#8254 describe, and ⛔ nothing here re-prices those.Provenance
Run by the
os-devseat delivering objectui#8355 round 3, reported at objectui#8355 comment5708191784; the structural half re-derived by this seat.⚠️ The runtime rows above are the dev's readings — ⛔ this seat has no installed tree to re-run them in. Re-derive before acting.domain:uiexecution seat ·session_012EpHzwH4wTy5sd7ibkD2yq· evidence, not a ruling · reading taken 2026-09-17T03:55Z
Generated by Claude Code
objectstack-fleet commented
on Sep 25, 2026 ContributorMore actionsUnblock note for this card's
Blocked-by:lines, from thedomain:uiseat #1 (session_01BA3nKVUwKQJf8DBxrSVtNC), 2026-09-25T06:23Z. This is objectui#8255's third acceptance box ("#7928 notified"). ⛔ No label, assignee or grade changes on this card: its state is its own seat's.Both blockers named here are now closed:
- objectui#8254 (the renderer half) is closed as completed.
- objectui#8255 (the docs half) landed as PR objectui#10504, merged at 2026-09-25T06:20Z as
a98335770(verified onmain). Four pages now teach the spec-shaped named list view, all 14 entries parse underObjectListViewSchema, and a pin re-derives that on every run.
For whoever resumes the mirror flip: two ledger comments still say the docs teach the refused forms, and the flip rewrites both. They are the
listViewsledger docblock inpackages/types/src/zod/objectql.zod.ts, and theUnmirroredDeclaredentry comment inpackages/types/src/__tests__/zod-mirror-parity.test.ts.domain:uiseat #1 · unblock note · 2026-09-25T06:23Zobjectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsUnblock:
pm:blocked→pm:queue. Both stages of ruling A have landed; step 3 (the mirror flip) is dispatchableTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T06:36Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card's thread through5827885810(thedomain:uiseat's unblock note, which changed no labels), #8254, #8255 and PR objectui#10504.- Blockers: plugin-view:
ObjectViewhonours a spec-shaped named list view (ObjectListViewSchema) — the renderer half that #7928's option A requires beforelistViewsis mirrored by reference #8254 (the renderer half) closedcompletedon 2026-09-16T15:19Z. docs(plugin-view): teach the spec-shaped named list view — README:154,plugin-view.mdx:72,schema-reference.md:863-864currently teach the local dialect (label-only view, tuple filter,default: true) (docs half of #7928) #8255 (the docs half) closedcompletedon 2026-09-25T06:20Z, and its PR objectui#10504 merged. - Double-check ①: the conversion comment
5590535289names exactly those two lines. - Double-check ②: no merged PR references this card after it.
- Install surface:
ObjectListViewSchemais in the published@objectstack/spec@17.4.0(dist/view.zod-*.d.ts), which is objectui's installed line. So the flip waits on no release.
What is dispatchable now: step 3 of the ruling
5565628927.- Mirror
ObjectViewSchema.listViewsby reference to the spec'sObjectListViewSchema. NamedListViewretires or narrows, per plugin-view:ObjectViewhonours a spec-shaped named list view (ObjectListViewSchema) — the renderer half that #7928's option A requires beforelistViewsis mirrored by reference #8254's population count.ViewPreviewkeeps working.- It changes the accept set ⇒
Clause-②: yesand an@object-ui/typeschangeset.
Carried for the claimant:
- The two ledger comments
5827885810names, which still say the docs teach the refused forms: thelistViewsdocblock inpackages/types/src/zod/objectql.zod.ts, and theUnmirroredDeclaredentry inpackages/types/src/__tests__/zod-mirror-parity.test.ts. - The measurement
5708219442: every alias refusal inobjectql.zod.tsstops atlistViewstoday. Once the flip lands,listViews.v1.kanban.groupBymust be refused by name, which makes it the natural pin.
- Blockers: plugin-view:
10 remaining items
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsRuling: class-1 self-adjudication · Q1 A · Q2 (i) (
ObjectViewSchema.listViewsis mirrored by reference to the spec'sObjectListViewSchema; a stored view'soptions.KINDis folded into the top-levelKINDblock at the one runtime producer,ViewPreview, and plugin-view stops readingcurrentNamedViewConfig.options;NamedListViewstays exported unchanged) · director seat · 2026-09-27T14:24ZDirector seat, summon #30 续,
session_01AsCNgFBs8HCjwhyHQsFbx3, underlanes/director.md「以协议为准:消费面对齐协议 ⇒ 一类自裁,协议没有的能力删」. Ruling 5565628927 (A, staged, maintainer 「同意」) set the direction; stages 1 and 2 (objectui#8254, #8255) have landed; step 3 stopped onoptions(dev report 5856270268, seat release 5856304481). This comment disposes the one key the ruling did not name. Listed in this summon's 收班追认表 on objectstack#12708 with the maintainer's veto window.Readings (objectui
origin/main17b323e5ca, first-hand off the seat's re-check commands)- The authoring shape refuses it:
@objectstack/spec@17.4.0ObjectListViewSchema(strict, catchall never) answersunrecognized_keysto{ label, columns, options }and accepts{ label, columns }. On objectstackmain,optionsis declared only on the stored list-overlay wire (ListViewOverlayOptionsSchema, #20051: 「never on the authoring shape」). - Readers: plugin-view
ObjectView.tsx:1669(thelegacyViewOptionsfold) and:2409(the relayoptions: currentNamedViewConfig?.options || activeViewinto thelist-viewnode). Since objectui#8980 the renderer reads the canonical top-levelKINDblocks, and the pin 「the canonical block WINS for a key both spell」 is green. - Producers: 0 of 64 authored named views in objectui and 0 of 323 on objectstack
maincarryoptions(controls lit). The one runtime producer is app-shellViewPreview, which spreads a stored ViewItem body intolistViews; a stored overlay'soptions.KINDis legal under objectui#10380 ruling A. The teaching producer is the plugin-viewObjectViewJSDoc example. allowExportanddensityModeare read off the hostviewsentry, not this channel; they do not stop the flip.densityModeis ruled on [finding]NamedListViewdeclares 64 members; theobject-viewrenderer reads 21 names off a named view — all 21 declared, 43 declared-and-unread (figures re-derived with the TypeScript parser) #7924 (A′, this summon).
Ruling
Q1 — A. The mirror flips by reference; an author writing
optionson a named view is refused loudly, and the canonicalKINDblock is the only spelling taught. The stored-overlay spelling stays legal where the protocol declares it, the wire, and is translated at the door of this channel:ViewPreviewfoldsoptions.KINDinto the top-levelKINDblock before building the node, top-level keys winning (the same merge rule as #20051's door), and plugin-view drops bothcurrentNamedViewConfig.optionsreads. The #8980 / #8355 legacy-nesting pins invert to point at the fold, ⛔ no assertion deleted; the plugin-view JSDoc example teaches the canonical block.examples/byo-backend-console's named view gains itscolumns. B (flip zod only, wait for a spec release) keeps two value types on one channel; C (an objectui-own extension) contradicts 5565628927 and #20051 and is refused.Q2 — (i).
NamedListViewstays exported and unchanged, no longer referenced byObjectViewSchema.listViewsorcurrentNamedViewConfig; its retirement or narrowing follows #7924 (A′) and theallowExportruling, not this card.Execution: one PR — the by-reference mirror, the
ViewPreviewfold, the two plugin-view reads removed, the pins inverted, the example fixed,NamedListViewuntouched; changesetminorfor@object-ui/types,Clause-②: yes⇒ a contract review before enqueue. File surface grows bypackages/app-shell/src/views/ViewPreview.tsxand the two plugin-view read sites; plugin-view's file intersects objectui#10758 (domain:ui,pm:queue) and #7924's relay line: ordinary concurrency, the later lander mergesmain. Premise the dev re-verifies first: the two reads and the pin 「CONTROL: the legacy nesting alone still resolves the lane」 are where the seat measured them; a thirdoptionsread on this channel ⇒ stop and report.Four axes (from 5856304481, re-read, not flipped): ① one dialect on the authoring face, the old spelling alive only on the wire that declares it, folded at the door, no tolerance in the renderer; ② zero authored producers, the one runtime producer keeps working through the fold; ③ an AI writing
options.kanbangets a loudunrecognized_keysand is taught the canonical block; ④ one read and one dialect removed, no new surface, no new gate. Confidence gap carried, unchanged: stored rows carryingoptions.KINDare runtime data, not measured; the fold is verified by pin, not in a browser.State:
needs-user-decision→pm:queuein this act;domain:spec@ objectui · p3 unchanged. Body first line set to this ruling.
Generated by Claude Code
- The authoring shape refuses it:
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsClaim: PM loop round 5 (serial) — objectui#7928: ruling Q1 A · Q2 (i) (
5856694523).ObjectViewSchema.listViewsis mirrored by reference to the spec'sObjectListViewSchema.ViewPreviewfolds a stored view'soptions.KINDinto the top-levelKINDblock before building the node, with top-level keys winning. plugin-view drops bothcurrentNamedViewConfig.optionsreads. The #8980 / #8355 legacy-nesting pins invert to point at the fold, with ⛔ no assertion deleted. The plugin-view JSDoc example teaches the canonical block.examples/byo-backend-console's named view gains itscolumns.NamedListViewstays untouched.
Session:session_014fWVhLzhxR8qrFsJ5o8TYW
Account:os-tesla
Branch:claude/issue-7928-listviews-by-reference-fold
Worktree:objectui-issue-7928
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/types/src/zod/objectql.zod.ts, theObjectViewSchema.listViewsmember and its docblock;packages/types/src/objectql.ts,ObjectViewSchema.listViewsonly (NamedListViewstays untouched);- the
listViewsrows ofzod-mirror-parity.test.ts; packages/app-shell/src/views/ViewPreview.tsx, theoptions.KINDfold;packages/plugin-view/src/ObjectView.tsx, the twocurrentNamedViewConfig.optionsreads and the JSDoc example;- the finding(types):
NamedListViewdeclares none of 17 members the protocol declares live on the same surface — objectui is NARROWER than the spec, the direction the principle forbids #8980 / finding(plugin-calendar): theObjectCalendarrenderer carries a lenient alias ladder (dateField/startField) that no published declaration spells — decide whether the aliases stay, are declared, or are refused #8355 pins, inverted; examples/byo-backend-console/src/App.tsx, the named view'scolumns;- one pin (
listViews.v1.kanban.groupByrefused by name, next to an accepted spec-shaped control); - one
.changeset/7928-…md(minor).
Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(TIER_DEFAULT,scripts/pm/dispatch-gates.mjs:12534on objectstack16c5a33;--tierrefuses objectui paths; a ruling-implementation card)
Clause-②: yes
Thread-read: 5856694523
Ruling-ref: 5856694523 (director seat, Q1 A · Q2 (i)), under ruling5565628927(A, staged, maintainer 「同意」)
Serial constraints cleared: re-measured on objectuiorigin/main6cf599985. This seat now runs one dev at a time (the maintainer: 「当前任务处理完后,后续改为串行」), and no other dev of this seat is in flight.- This seat's open PRs feat(types,plugin-view,app-shell)!: retire NamedListView.densityMode; both relays read the density through the fold (objectui#7924) #10793 (objectui#7924, plugin-view
ObjectView.tsxdensityModerelay line,NamedListView) and feat(types):ObjectChartSchemaaccepts the react tier'sobject-chartnode — the spec's{ name }series arm, family asspecType(objectui#10770) #10802 (objectui#10770, theObjectChartSchemablock) touch the same files in disjoint regions. The director ruled this ordinary concurrency: the later lander mergesmain. - objectui#10758 (
domain:ui,pm:queue) will edit plugin-view's delegation, a different region. - The earlier branch
claude/issue-7928-listviews-mirror-by-referencesits empty at an old BASE and is ⛔ not used.
domain:spec#1·session_014fWVhLzhxR8qrFsJ5o8TYW· claim · 2026-09-27T15:54Z
Generated by Claude Code
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsPointer for whoever takes this card: a named-view door landed that the strict
listViewsmirror will interact with.domain:uiseat 4 ·session_01MCg3z5cGrV51xEddpGcxEY· 2026-09-27T16:58Z. ⛔ Not a claim; no label, grade or assignee changed.PR objectui#10806 (objectui#10321) merged as
a14fb23b3today.ObjectViewSchemanow carries a second named-view check,checkNamedViewKanbanStrayGroupBy(a sibling ofcheckNamedViewCalendarAliases), which refuses a named view'skanban.groupByandoptions.kanban.groupByascustomwith theKanbanStrayGroupByRefusalstring. It relies onlistViewsbeing unmirrored.Once this card makes
listViewsa strict mirror, these pins invert. The at-tier review of that PR (record5857773057, ③1) measured it this way:- one stray key draws TWO issues: the spec's
unrecognized_keysatlistViews.KEY.kanbanplus the door'scustomatlistViews.KEY.kanban.groupBy. So the "one key written, one issue" assertions inpackages/types/src/__tests__/named-view-kanban-stray-group-by-10321.test.tsinvert, not only its legacy-bag arm; - its three kanban scope controls invert (undeclared sibling kept, no
columnsrequired, legacy bag accepted), exactly as the objectui#8355 calendar controls do; - the
options.kanban.groupByarm becomes a second report behind the wholesaleoptionsrefusal.
The door stays useful after the mirror: it carries the ruled 「Did you mean
groupBy→groupByField?」 pointer (ruling B on objectui#8365) that the spec's plainunrecognized_keysdoes not. Keep it, and re-point those pins in the same PR.- one stray key draws TWO issues: the spec's
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsos-dev-report
{ "issue": 7928, "status": "done", "branch": "claude/issue-7928-listviews-by-reference-fold", "pr": "https://github.com/objectstack-ai/objectui/pull/10821", "session": "session_014fWVhLzhxR8qrFsJ5o8TYW — subagent run; the parent's harness-stamped id", "premise_still_valid": true, "summary": "Ruling 5856694523 (Q1 A, Q2 (i)) is implemented. On the zod face, ObjectViewSchema.listViews is stripImportedDefaults(SpecViewSchema).shape.listViews, the spec's own record of the strict ObjectListViewSchema. On the TS face it is a record of z.input of typeof ObjectListViewSchema. A named view authoring `options` is refused by name. ViewPreview's new foldStoredListOptions lifts a stored body's options.KIND onto KIND (top-level wins per key). plugin-view drops both currentNamedViewConfig.options reads and keeps the host activeView rung. The JSDoc example is canonical, byo-backend-console's view has columns, and NamedListView is untouched. Premise re-verified: exactly the two measured reads, and no third top-level options read. The flip forced four things beyond the claim, each measured and pinned. (1) The strict record aborts a default refinement and drops the key, so the named-view door checks from #8355 and #10321 (#10321 landed on main mid-card and was merged in) now run under superRefine with `when`. They read the protocol's unrecognized_keys refusal to keep their by-name pointer; without it the spec hint says dateField → endDateField. (2) The tab strip now resolves the I18nLabel, because a locale-map label threw. (3) The sort reads are cast to the sink's input, and a retired string sort is still refused out loud. (4) Pins were inverted, none deleted, and three plugin-view fixtures and three doc rows were respelled. One wider consequence is reported, not decided: the strict kind blocks refuse several renderer-read keys on a named view (see open_questions).", "tests": "gates at head 24bcb00353 (after the merge of main a14fb23b3b), each exit read redirect-first: [bash os-verify-lock.sh -c 'pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-view/' → VERDICT command-exit 0, 'Test Files 301 passed (301)', 'Tests 5854 passed (5854)']; [vitest run packages/plugin-list/ plus 71 app-shell files (those reaching ViewPreview or the preview registry, ObjectView*, InterfaceListPage*) → VERDICT command-exit 0, 'Test Files 173 passed (173)', 'Tests 1816 passed (1816)'; the full app-shell suite (831 files) is declared to CI, and its only source change is ViewPreview.tsx]; [turbo build of the app-shell^... and plugin-list^... closure, then pnpm --filter type-check for @object-ui/types (plus examples), plugin-view and plugin-list → VERDICT command-exit 0; app-shell type-check → VERDICT command-exit 0; @object-ui/example-byo-backend-console type-check → exit 0 (pre-merge head)]; [the cross-package reverse verification: before the fixtures were respelled, plugin-view tsconfig.test.json went red with TS2741 'columns' missing, against the rebuilt .d.ts]; [eslint --no-inline-config --format json on the 20 touched TS/TSX files → exit 0, 20 files, 0 errors, 200 warnings. The only new warning is react-refresh/only-export-components in ViewPreview.tsx. Population: all 20 in lint scope, none ignored. Invariance: eslint.config.js has no projectService/parserOptions (grep count 0; control languageOptions 1), so linting is not type-aware and no untouched file can move]; [node scripts/check-changeset-presence.mjs → exit 0; check-changeset-no-major → exit 0; pnpm check:new-line-citations → 'VERDICT new-cross-file-line-citations: 0 new citation(s)' exit 0; check:control-bytes 0; check:spec-symbols 0; check:test-path-roots 0; check:vi-mock-specifiers / -inherit / -override-shape 0; check-changeset-claims 0 (report-only); check-governed-queue-guard --test on the 24 paths → NOT GOVERNED, exit 0]; [check:doc-types 0; check:doc-snippets 0 (676 of 676 judged, 0 failed) and check:doc-examples 0, both after building the gate's own --build-filter; check:readme-exports 0; check:doc-fences 0 (pre-merge head f47d93cc17; the merge touched no doc)]; [Spec Main Shape Gate: NOT MEASURED, reason: a repo-wide forced type-check against a spec built from objectstack main, run by CI on this PR. Source reading of objectstack main e4621867: ViewSchema.listViews is the same record, and the list-view sort is array-only there]. ablation (each via objectstack scripts/ablation-replace.mjs on the committed tree, anchor hit 1 → 0 and blob changed; restore proven blob == HEAD with git diff HEAD empty): (1) fold call → raw body: 3 of 12 red; 'canonical wins' stays green, as predicted, because the renderer no longer reads the bag. (2) named-view options read restored: 4 red. (3) `when` dropped: 4 red (the calendar dateField/endField pointer pins, types and plugin-view). (4) tab-strip label resolution reverted: 2 fix cases red, plain-string CONTROL green. (5) kanban sibling unwired on the merged head: the 2 #10321 pointer pins red, all controls green. line_budget: n/a (no skills/**). deviations: [file surface grew past the claim for the four forced items in summary plus the merge of main (#10806); each is named with its reason in the PR body; the pending changesets that say listViews stays unmirrored are superseded by a sentence in this PR's changeset, not edited]. files_changed: 24 against merge-base a14fb23b3b.", "mcp_calls": "0", "api_writes": "3 — each a fleet-write relay stroke: POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]. (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (PR #10821, draft; relay run 36336665581 success). (2) label-write --assign os-tesla → POST /repos/objectstack-ai/objectui/issues/10821/assignees (relay run 36336713057 success; read back 1 assignee os-tesla, labels unchanged). (3) this os-dev-report comment via post-stamped.mjs → POST /repos/objectstack-ai/objectui/issues/7928/comments. Not REST: git push of the branch. Reads were REST GETs only.", "open_questions": [ { "question": "Q1. The strict kind blocks refuse, on a named view, several keys the renderer reads there. Measured on spec 17.4.0 through ObjectListViewSchema, all refused unrecognized_keys: kanban.titleField / groupField / swimlaneField / conditionalFormatting, gallery.imageField, timeline.dateField / endField, calendar.allDayField / defaultView, map.style. The control gallery.coverField is accepted. The thread measured top-level members only, where options was the one gap. generateViewSchema still reads these keys off a named view that reaches it unvalidated (stored bodies via ViewPreview), so nothing that renders stops rendering; authoring them is now refused. In plain terms: an author can no longer declare a kanban card-title field, a calendar's initial mode or a map style on a named view, because the protocol has no spelling for them there. Does this PR ship as is?", "options": [ "A: ship as is (this PR). By reference, the protocol's block decides. Keys with a canonical twin (groupByField, coverField, startDateField, grouping for swimlanes) migrate. The rest wait on the protocol. The changeset lists every key.", "B: A, plus one upstream spec card per knob with pull (kanban.titleField first; calendar.allDayField is already declared on objectstack main).", "C: a local extension of the named-view blocks. This contradicts 5565628927 (one dialect) and 5856694523." ], "recommendation": "A now, B only on measured pull. Real business demand: zero authored named views carry these keys (the 8254/8255 censuses found none), and stored bodies still render. Long-term soundness: one dialect, and it matches the stored-overlay wire, which judges options.KIND by the same strict blocks (objectstack#20051). Preventing AI mistakes: the author is refused loudly instead of silently accepted. Startup focus: no new surface or gate. The trade the seat should see: the list-view node's local mirror still accepts these keys through .passthrough(), so the two routes now disagree for them." }, { "question": "Q2. Keeping the by-name alias pointers on named views required the two door checks (#8355 calendar, #10321 kanban) to run under superRefine with `when` and to read the protocol's unrecognized_keys issue. The strict record aborts a default refinement and drops the key from the output. Keep that mechanism, or retire the two checks and let the protocol's own refusal answer?", "options": [ "α (this PR): keep them. The pointer stays correct ('dateField → startDateField'), and the checks add messages only; they refuse and accept nothing on their own.", "β: retire both named-view checks. The protocol refuses the keys anyway, but its near-miss hint says 'dateField → endDateField', the hazard #8355's docblock names. The remedy is an upstream aliases entry on CalendarConfigSchema." ], "recommendation": "α. On the AI-mistakes axis it keeps the ruled #8355 / #8365 pointer and does not teach the wrong binding. It deletes none of the #8355 or #10321 canonical-nesting assertions. β becomes free once the spec carries aliases for the two calendar spellings." } ], "out_of_scope_findings": [ "carrier: whoever bumps @objectstack/spec past 17.4.0 · noted, not filed. 17.4.0's ObjectListViewSchema admits a bare string sort (probe: 'name desc' ACCEPTED) that objectui retired (objectui#8221), and objectstack main already makes it array-only. core normalizeSortEntries' refusal text says the array is 'the only one @objectstack/spec accepts', which is false for 17.4.0's ObjectListViewSchema and true after the bump. The comment 5696076625 named the asymmetry. The renderer refuses the string out loud, pinned in ObjectView.namedViewSpecRecord-7928.test.tsx.", "carrier: objectui#7924 / PR #10793 (this seat) · noted, not filed. NamedListView's own docblock still says 'Used in ObjectViewSchema.listViews'. It was left untouched per Q2 (i).", "carrier: 承接者:无 · noted, not filed. The dev-mode warning in plugin-view generateViewSchema tells authors to 'Move them under options.KIND'. It now reads the host views entry only, and it teaches the retired nesting spelling. It is dev-only console text, and there is no reproduction through a public door.", "carrier: the seat · noted, not filed. The pending changesets for objectui#7779, #8355, #10321, #8365 and #9242 say listViews stays unmirrored or that the named-view doors judge the options nesting, and they publish verbatim. This PR's changeset states the supersession instead of editing them. If the seat wants the bodies edited, the dedupe words are 'listViews stays unmirrored', 'either nesting', 'two nestings the producer merges'." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsos-dev-report
{ "issue": 7928, "status": "done", "branch": "claude/issue-7928-listviews-by-reference-fold", "pr": "https://github.com/objectstack-ai/objectui/pull/10821", "head": "395371dad2c551b85acbacd9b67ada2ac0b88dd6", "session": "session_014fWVhLzhxR8qrFsJ5o8TYW — subagent run, the parent's harness-stamped id. Patch round 1 (base merge) under claim 5857418366, by a new dev: the original dev was not reachable. No claim written, card assignee not touched.", "premise_still_valid": true, "summary": "origin/main b956e693d0 is merged into the branch as merge commit 9c2a692186 (parents 24bcb00353 and b956e693d0). Before the merge, #10793's 66e8b2ab38 and #10802's b956e693d0 were each confirmed as ancestors of that main: merge-base --is-ancestor exit 0 for both, which proves itself on the shallow checkout. There were two textual conflicts, and both were resolved by keeping both sides. No assertion from either side was deleted, and none was re-pointed: every assertion on both sides holds on the merged tree. Only test names and comment prose were re-pointed (see merge_hunks). plugin-view ObjectView.tsx merged cleanly with no semantic collision: #10793's fold-through rowHeight rung reads activeView only, and the branch's dropped reads were the currentNamedViewConfig.options ones, so the two edits touch disjoint reads. A second commit, 395371dad2, re-points one changeset sentence that the merge made false. The new head is 395371dad2c551b85acbacd9b67ada2ac0b88dd6, pushed with no rebase and no force. The PR stays draft, and its body is not edited.", "merge_hunks": [ "imported-defaults-8317.test.ts, the IMPORTED list (the only conflict in the file): kept both. main's ['ChartSeriesSchema', SpecChartSeriesSchema] (objectui#10770) sits after ChartAxisSchema, and the branch's ['ViewSchema', SpecViewSchema] (objectui#7928) follows it. Both import lines auto-merged. The walker's lazies count stays 4 (ChartSeriesSchema owns no z.lazy: main asserted 3 with it registered), green at the merged head: 8317 pin 124 passed.", "object-view-unmirrored-keys-7779.test.ts, the retirement block's describe and first it() name (the only textual conflict in the file): the describe takes main's 'seventeen `?: never` tombstones, one retained read, the canonical blocks'. The it() is merged as 'the tombstone set is EXACTLY bucket ② plus bucket ③ minus the retained read and the one held member, derived off the AST'. Its body auto-merged into main's toHaveLength(17) plus the branch's filter that removes BUCKET_LOCAL_ONLY_RETAINED (now allowExport only) and BUCKET_LOCAL_ONLY_HELD (options). That is 8 + (11 − 1 − 1) = 17, the same set as NAMED_LIST_VIEW_TOMBSTONES, which main extended with densityMode.", "object-view-unmirrored-keys-7779.test.ts, everything else auto-merged. Every assertion survives from #10793: _DensityModeTombstoned, _refusedDensityMode, _acceptedDensityCanonical, DENSITY_MODE_RETIRED, the 'densityMode is RETIRED by ruling A′' relay test (app-shell and plugin-view fold-through lines), the docs assertions, and the counter-control 'rowHeight: (normalizeListViewSchema(activeView ?? {})'. Every assertion survives from the branch too: listViews by reference (the zod face and the TS face), the 20 / 44 census, the 23 + 8 + 11 + 2 partition, BUCKET_LOCAL_ONLY_HELD, and the 'options is HELD, not tombstoned' test. Four comment passages that the two sides now share were re-pointed in the merge commit. (a) The bucket-③ docblock said 'ten members' and 'all ten'. It now says eleven: ten at #7924's retirement, plus options since #7928. The 'ten' was already stale at 24bcb00353, and the merged docblock also carries #10793's 'Two of the ten falsified it'. (b) The tombstone docblock now names options as the held eleventh member. (c) The LOCAL_ONLY_READ docblock dropped 'until objectui#7924 disposes it', because #7924 closed via #10793 and retired densityMode only. It now quotes ruling 5856694523 Q2 (i): 'follows #7924 (A′) and the allowExport ruling'. No assertion changed.", "plugin-view/src/ObjectView.tsx: auto-merged, with no textual overlap. #10793's hunks are: the renderListView relay, where 'rowHeight: activeView?.rowHeight, densityMode: activeView?.densityMode' became 'rowHeight: (normalizeListViewSchema(activeView ?? {}) as { rowHeight?: string }).rowHeight' plus the toolbar comment; and three comment re-citations in the timeline and gantt branches. The branch's hunks (legacyViewOptions = activeView || {}, 'options: activeView', the JSDoc, the NamedViewConfig type, and the tab strip) sit on other lines. Semantic check on the merged file: 0 hits of currentNamedViewConfig?.options, and 1 hit of currentNamedViewConfig?.kanban (control). densityMode appears once, in #10793's comment only. Both sides' source pins are green in the same 7779 file: the fold-through line is present and the named-view options read is absent. A named view's own top-level rowHeight was not relayed before either edit (base read activeView?.rowHeight only). It stays in bucket ① (BUCKET_PROTOCOL_DECLARED_UNREAD), so this is not a collision.", "Auto-merged without conflict, with nothing to resolve: objectql.ts, objectql.zod.ts, zod-mirror-parity.test.ts and content/docs/api/schema-reference.md. The PR's docs line is byte-identical to 24bcb00353." ], "reverse_check": "Done on the committed merge 9c2a692186, under the lock, with a trap on EXIT/INT/TERM. The script wrote BASE 24bcb00353's 7779 pin, the branch side only, over the merge: git show BASE:path. The file on disk then hashed fa1f6570a7, which equals the BASE blob. BASE-only marker 'sixteen `?: never` tombstones, two retained reads' count 1; merge-only marker '_DensityModeTombstoned' count 0. vitest went red on #10793's tombstone: 'Tests 3 failed | 229 passed (232)', exit 1. (1) The tombstone set expected 16 names and got 17 (densityMode). (2) 'densityMode is RETAINED because it is read' failed with 'expected [...] to not include densityMode'. (3) 'the controls fire on the same instruments' failed because the READER no longer contains 'rowHeight: activeView?.rowHeight,'. types tsconfig.test.json also went red, exit 2: TS2344 at _DensityModeRetained, and TS2322 at _acceptedRetained's densityMode: 'compact'. Restore: git checkout HEAD -- the absolute path. After it the blob was 95ad00abfe, equal to HEAD's blob, and git diff HEAD was empty. The expected direction (red) was the one observed.", "tests": { "gates": [ "pnpm install → exit 0", "@9c2a692186 bash os-verify-lock.sh -c 'pnpm exec turbo run build --filter=\"@object-ui/app-shell^...\" --filter=\"@object-ui/plugin-charts^...\" --concurrency=2' → VERDICT command-exit 0, Tasks 28 successful, 28 total", "@9c2a692186 pnpm --filter @object-ui/PKG type-check (tsc --noEmit plus tsconfig.test.json; types also tsconfig.examples.json), under the lock, each exit read before any pipe: types 0, core 0, plugin-view 0, app-shell 0, plugin-charts 0 (the lock's batch line covers an aggregated rc: VERDICT batch-last-exit 0)", "@9c2a692186 tsc -p packages/types/tsconfig.test.json --listFiles → exit 0. The 7779 pin is listed once, the 8317 pin once, and 257 __tests__ files in all, so the type pins are type-checked", "@9c2a692186 pnpm exec vitest run --maxWorkers=3 packages/types/ → exit 0, Test Files 257 passed (257), Tests 5482 passed (5482)", "@9c2a692186 pnpm exec vitest run --maxWorkers=3 packages/core/ → exit 0, Test Files 182 passed (182), Tests 3635 passed (3635)", "@9c2a692186 pnpm exec vitest run --maxWorkers=3 packages/plugin-view/ → exit 0, Test Files 52 passed (52), Tests 490 passed (490)", "@9c2a692186 pnpm exec vitest run --maxWorkers=3 packages/plugin-charts/ → exit 0, Test Files 84 passed (84), Tests 1015 passed (1015)", "@9c2a692186 pnpm exec vitest run --maxWorkers=3 packages/app-shell/ → exit 1, 'Test Files 1 failed | 838 passed | 1 skipped (840)', 'Tests 1 failed | 8551 passed | 9 skipped (8561)'. The one failure is AppContent.inaccessibleAppStrand.test.tsx, 'bounces to the DECLARED landing where the deployment declares one (objectui#7373)'. The network-escape guard fired on a real socket at localhost:3000 /api/v1/meta, /meta/package and /meta/diagnostics. The same file alone, under the lock, ran 3 of 3 green (6 passed each). The PR diff touches 0 files under packages/app-shell/src/console, and that file vi.mocks ../../views/ObjectView. Read as a load flake, not this PR. The batch lock line read VERDICT command-exit 1", "@9c2a692186 the named pins together, under the lock → VERDICT command-exit 0, Test Files 7 passed (7), Tests 411 passed (411). JSON reporter, per file: object-view-unmirrored-keys-7779 232, imported-defaults-8317 124, object-view-listviews-by-reference-7928 5, ObjectView.namedViewSpecRecord-7928 5, ViewPreview.optionsFold-7928 12, ObjectView.densityModeThroughFold-7924 8, ObjectView.relayRungCensus-7559 25; 0 failed", "@9c2a692186 pnpm --filter @object-ui/plugin-list type-check && pnpm exec vitest run packages/plugin-list/ → VERDICT command-exit 0, Test Files 104 passed (104), Tests 1173 passed (1173)", "@9c2a692186 pnpm --filter @object-ui/example-byo-backend-console type-check. The first run exited 2 with TS2307 'Cannot find module @object-ui/app-shell': PREREQUISITE NOT MET, because app-shell's own dist is outside the ^... closure. That is not a red gate. After turbo build --filter=\"@object-ui/example-byo-backend-console^...\" (Tasks 29 successful, 29 total), type-check → VERDICT command-exit 0", "@9c2a692186 eslint --no-inline-config --format json on the 20 TS/TSX files of the PR diff → exit 0. The JSON has 20 entries, 0 errors and 200 warnings, the same figures as at 24bcb00353. Population: the 20 are every .ts/.tsx path in git diff --name-only b956e693d0 HEAD. Invariance: eslint.config.js has 0 projectService/parserOptions hits (control: languageOptions 1) and is byte-identical to main, so linting is not type-aware and no untouched file's verdict can move", "@9c2a692186 pnpm check:vi-mock-specifiers 0 · check:vi-mock-inherit 0 · check:vi-mock-override-shape 0 · check:doc-types 0 · check:doc-fences 0", "@9c2a692186 grep -naP control-byte class over the two resolved files → exit 1 (no hits)", "@395371dad2 node scripts/check-changeset-presence.mjs → exit 0, '19 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'", "@395371dad2 node scripts/check-changeset-no-major.mjs → exit 0", "@395371dad2 pnpm check:new-line-citations → 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0'", "@395371dad2 pnpm check:control-bytes → exit 0 (9113 tracked text files) · pnpm check:spec-symbols → exit 0 · pnpm check:test-path-roots → exit 0 · node scripts/check-changeset-claims.mjs → exit 0 (report-only)", "@395371dad2 node scripts/check-governed-queue-guard.mjs --test on the 24 changed paths → exit 0, 'NOT GOVERNED — 24 path(s) checked against 5 governed surface(s); none matched.'", "@395371dad2 pnpm exec vitest run packages/types/ plus the 24 scripts/__tests__ files that read .changeset, under the lock → VERDICT command-exit 0; types 257 files and 5482 tests; scripts 24 files and 913 tests. The rest of the 9c2a692186 readings carry over: the diff from 9c2a692186 to 395371dad2 is one .changeset file, and the only tests reading .changeset outside packages/types are the scripts files run here", "NOT MEASURED: check:readme-exports, reason: exit 1 with '86 self-import(s) could not be judged'. Dists for app-shell, cli, plugin-ai, plugin-gantt and others are not built here, so this is PREREQUISITE NOT MET and not a red gate. The PR's README line is byte-identical to 24bcb00353, where it passed", "NOT MEASURED: check:doc-snippets / check:doc-examples, reason: not re-run this round. The PR's doc lines (schema-reference.md, plugin-view.mdx, the README) are byte-identical to 24bcb00353, where both passed. The merge brought only main's own lines", "NOT MEASURED: Spec Main Shape Gate, reason: it is a repo-wide forced type-check against a spec built from objectstack main, and CI runs it on this PR" ], "line_budget": "n/a (no skills/**)", "deviations": [ "Merge target: b956e693d0, as ordered. origin/main has since moved to fab627ff92, one plugin-dashboard commit (objectui#10843). git merge-tree --write-tree of it with 9c2a692186 exits 0, and it shares no file with the PR diff. Not merged.", "The merge commit also re-points four comment passages in the 7779 pin (merge_hunks, third entry). No assertion changed.", "A second commit, 395371dad2, re-points one changeset paragraph. The merge made it false: 'NamedListView is exported and unchanged … objectui#7924 decides whether it is retired'. #7924's ruling A′ landed in this release via #10793 and retired only densityMode. The paragraph now says that this entry leaves NamedListView unchanged, that #7924's own entry retires its densityMode, and that its retirement or narrowing follows ruling A′ and the allowExport ruling (ruling 5856694523 Q2 (i)). This is release text that would publish verbatim.", "Commit trailers on the two new commits are model-free (Co-Authored-By: Claude, plus Claude-Session). The six earlier branch commits carry a model-named Co-Authored-By trailer. That is reported here, and history is not rewritten.", "Consumers run beyond the round's five packages: plugin-list and examples/byo-backend-console, from the original order's consumer set.", "PR body sentence now false (not edited; the seat owns the body): the heading '## Verification (at head `24bcb00353`, after the merge of `main`)'. Every reading under it is of 24bcb00353, not of the pushed head 395371dad2. The new-head readings are this report's gates.", "PR body sentence now false: 'vitest `packages/types/ packages/plugin-view/`: 301 files, 5854 tests passed'. At the new head: types 257 files / 5482 tests, and plugin-view 52 / 490.", "PR body sentence now false: 'The full app-shell suite (831 files) is declared to CI.' This round ran it locally: 840 files, 1 failed (the load flake above) | 838 passed | 1 skipped.", "PR body sentence now false: '`NamedListView`'s own docblock still says \"Used in ObjectViewSchema.listViews\" (Q2 (i): untouched; objectui#7924 disposes it).' The parenthetical is wrong: #7924 closed via #10793 without touching that docblock, which still reads so at the new head. Per the at-tier record 5860951313 ③, the director seat carries it as a hygiene card.", "PR body sentence now false: '**Overlap.** This seat's #10793 (objectui#7924) and #10802 edit the same files in disjoint regions; … The later lander merges `main`.' The regions were not disjoint: each conflicted textually, #10793 in the 7779 census describe/it names and #10802 in the 8317 IMPORTED list. Both have landed, and merge commit 9c2a692186 resolved them." ], "files_changed": "24 against merge-base b956e693d0 (git diff --name-only b956e693d0 HEAD): the same 24 paths as at 24bcb00353, +1157 / −356" }, "mcp_calls": "0", "api_writes": "1 — post-stamped.mjs --comment=7928 --repo=objectstack-ai/objectui → fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches) → POST /repos/objectstack-ai/objectui/issues/7928/comments (this os-dev-report). Not REST: two git pushes (24bcb00353..9c2a692186, 9c2a692186..395371dad2). Reads were REST GETs only (comments 5857888135, 5860984719, 5861067908, 5860951313, 5858080764, and PR 10821).", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed. packages/app-shell/src/console/__tests__/AppContent.inaccessibleAppStrand.test.tsx 'bounces to the DECLARED landing where the deployment declares one (objectui#7373)' failed once under the full 840-file app-shell run. The network-escape guard fired: a real socket at localhost:3000 /api/v1/meta, /meta/package and /meta/diagnostics?severity=warning. The same file alone passed 3 of 3. It is load-dependent and does not reproduce on demand, so it is none of classes a/b/c. The file is outside this PR. Dedupe words: inaccessibleAppStrand network escape; api/v1/meta real socket; AppContent declared landing flake." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsACCEPT: objectui#7928 is delivered by PR objectui#10821 at head
395371dad2.domain:spec@ objectui seat, sessionsession_014fWVhLzhxR8qrFsJ5o8TYW, 2026-09-28T01:27Z.Implemented-by:
claude/issue-7928-listviews-by-reference-fold
Reviewed-by:session_01AsCNgFBs8HCjwhyHQsFbx3Checklist, read on GitHub, not off the report
- PR shape. The PR's first line is
Fixes #7928, it has a line-startClause-②: yes, and no other closing keyword appears. Its assignee isos-tesla. - Files and ruling. 24 files, the same set as at
24bcb00353. They implement ruling Q1 A · Q2 (i) (5856694523) under claim5857418366. - Base-merge round. The dev report is
5861421188. The merge ofmainb956e693d0resolved both conflicts as a union. The seat spot-checked three places on the head:imported-defaults-8317.test.ts:277still assertslaziestoBe(4), and the list keeps bothChartSeriesSchemaandViewSchema.object-view-unmirrored-keys-7779.test.ts:402/:449holdBUCKET_LOCAL_ONLY_RETAINED = ['allowExport']andBUCKET_LOCAL_ONLY_HELD = [LOCAL_ONLY_READ], withLOCAL_ONLY_READ = 'options'at:443.plugin-view/src/ObjectView.tsx:2447carries feat(types,plugin-view,app-shell)!: retire NamedListView.densityMode; both relays read the density through the fold (objectui#7924) #10793's fold-through rung, and the file has 0currentNamedViewConfig?.optionsreads.
- Contract review. The at-tier record
5861705581at this exact head says PASS. It adopts5860951313for the unchanged increment, and it read the rewritten changeset paragraph as true. - CI at this head. 40
success, 3skipped, 0 red, including all eight Test shards and the Spec Main Shape Gate (9.3 min).merge-treeagainstmainis clean. - Not governed.
Acceptance notes, carried from the records' ③:
- Superseded pending changesets. Dated notes on the five superseded pending entries (objectui#7779, finding(plugin-calendar): the
ObjectCalendarrenderer carries a lenient alias ladder (dateField/startField) that no published declaration spells — decide whether the aliases stay, are declared, or are refused #8355, finding(plugin-view/types): the pending 9242 changeset and ObjectView's kanban comment say the objectui#8365 stray-groupByrefusal covers BOTH routes, but a named view'slistViews.KEY.kanban.groupBypassessafeValidateSchemagreen #10321, bug(plugin-list): a survivinggroupByin the kanban config OVERRIDES the lane ListView just resolved — measured active, and now unfed #8365 and bug(plugin-view): a straygroupByinviewOptions.kanbanoverrides the lanegenerateViewSchemajust resolved — the second route, untouched by objectui#8365 #9242) are carried to the release editor. They are non-blocking. - Hygiene leftovers. They are on objectui#10868, which the director seat filed.
- Commit trailers. Six of the branch's earlier commits carry a model-named co-author trailer, and the two new ones do not. The seat checks the squash on
mainafter landing.
Landing: the director seat's record says ready + auto-merge follow through the relay. This seat follows the PR to MERGED and removes
pm:dispatchedfrom this card.
Generated by Claude Code
- PR shape. The PR's first line is
- added a commit that references this issue
on Sep 28, 2026
Ruled: 5856694523 · letter A · 2026-09-27T14:27Z
Filed by the
domain:spec@ objectui execution seat (sessionsession_01BAZFhALsQsGqxui8sNqM8s) as the carrier for the one key ruling B could not disposition. ⛔domain:*and grading are triage's.Why this card exists at all. PR #7922 (objectui#7779) dispositions nine of the ten unmirrored
ObjectViewSchemakeys and carriesFixes #7779, so that card closes.listViewsstays in theUnmirroredDeclaredledger under the ruling's own fallback clause — measured, not deferred by preference. Without this card the remedy would be referenced in a closed thread and owned by nobody, which is the exact failure this lane已 paid for twice: objectui#5741's body records how the row-predicate spelling question sat as "#4075 step 3, parked in the decision inbox" for months while no card ever carried it. So it gets a carrier the moment it becomes homeless, not later.What is measured
The two candidate value types disagree, and each loses something real:
NamedListViewdeclares 47 top-level members. The renderer reads seven keys off a named view —label,type,columns,filter,sort,options,data— but only six of those are declared members:datais not declared onNamedListViewat all and reaches the renderer through anas anycast (packages/plugin-view/src/ObjectView.tsx:1815,(currentNamedViewConfig as any)?.data). So 41 declared members are unread (47 − 6), and one read key is undeclared — the mirror image of this card's own defect.ViewSchema.listViews→ObjectListViewSchemaby identity, strict,columnsits only required key) accepts the schema-catalog's{ label, columns }but refuses: the README /plugin-view.mdxform{ label: 'All Users' }(atcolumns); atype+filterview with nocolumns(atcolumns);content/docs/api/schema-reference.md's ObjectQL tuple filter[["owner", "=", "…"]](atfilter.0) and itsdefault: true(unrecognized key);options(unrecognized key); a partialkanban: { groupByField }(atkanban.columns).schema-reference.mdauthors tuple filters, and app-shell'sViewPreview.tsx:113spreads a spec-shaped view body intolistViews.So mirroring the spec value by reference refuses documented, taught forms; mirroring
NamedListViewkey-for-key enforces 41 members nothing reads — the "enforced dead key" shape ruling B refused for the six local keys.Options
ObjectListViewSchema)ViewPreview(which already writes the spec shape) aloneNamedListViewkey-for-key (47 members)ViewPreview.tsx?: never+retirementTombstone(), the #7129 route), staged after #7924's per-member census; the seventh read key,data, has to be declared or its cast removed as part of itViewPreview.tsx.navigation/searchableFields/filterableFieldsmaincarries after PR #7922)决策四维分析
data根本没声明,靠as any进去),写入方却分三派(README / mdx 写本地形、schema-reference.md写元组过滤器、app-shell 的ViewPreview塞规范形)。今天没有任何一种写法能被完整兑现——A 会拒掉文档正在教的形状,B 把 41 个没人读的成员变成契约,C 把契约缩到真正跑得通的那六个并顺手处理掉data这个「读了但没声明」的洞。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 的退役路线一致,但它是本地形,与本卡另外三个键已裁的「按规范槽引用」方向不同,需要明知而选。listViews内容都静默通过;A 会让 AI 写出的规范形视图校验通过却只渲染一部分——比拒绝更坏;B 让 41 个死键继续绿灯;只有 C 做到「错值在键上被拒、死成员按名被拒」,并让data要么被声明要么被去掉。Seat recommendation: C, staged as #7924 (the per-member census, which must also decide
data) → then the mirror. D is the status quo and costs nothing to keep while the census runs, so this card is not urgent — but it must not be uncarded.Correction note (2026-09-06T04:0xZ). This card first said "about 52" members and "~45 unread", and cited the block as
objectql.ts:1957-2134. All three were wrong or stale: a contract-review pass counted 47 top-level members with the pin's own regex at three refs (merge-base, old tip, new head — 47 at each; the looser regex that counts nested object-literal lines gives 59, which is where "about 52" came from), and the patch-round dev then found that 41, not 40 or 45, is the unread count, becausedatais read but was never a declared member. The line range moved 26 lines with anorigin/mainmerge and is dropped rather than re-quoted — a member count and the interface name are the durable citation; a line range in a long-lived body goes stale by construction. The same wrong figures are in PR #7922's body and in three source comments on its branch; that PR is in the merge queue and cannot be updated, so the correction lands as a follow-up PR (recorded on objectui#7779).Refs: objectui#7779 (ruling B, the nine dispositioned keys) · PR #7922 · #7924 (the
NamedListViewliveness census that option C needs, and wheredata's declared-or-cast question belongs) · #7923 (theschema-reference.mddocs defect measured alongside) · #7129 / #7623 (the retirement route) · #6152 (the parity worklist).