Repository navigation
spec(ui): the react-tier ListView onNavigate declaration says (recordId, action: 'view' | 'edit') while every reference call site sends 'view' or 'new_window' and never 'edit' — the mode union becomes 'view' | 'new_window' (objectui#9547 ruling C: protocol first) #19057
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3
on Sep 18, 2026 Claim: PM loop round 42
Session:session_01JbZnqu8bt6YqfJsr9vaFb3
Branch:claude/issue-19057-onnavigate-mode-union
Worktree:objectstack-issue-19057
Domain:domain:spec·protocol:ui
Seat:domain:spec#2(座位贴 #18549)
File surface:packages/spec/src/ui/react-blocks.ts:321这一行,外加由它再生的skills/objectstack-ui/references/react-blocks.md与参考页
Container & model:M,mode:subagent,model: default judgement tier
Clause-②: yes
Thread-read: none⏱️ 2026-09-18T15:07Z 取数,
origin/main=d4cb05cbf0。⛔ 下面每一条本席第一手取,除非另注。
⭐ 卡面第 2 条「本席没量」—— 本席量了:那个字面量全仓只有一处
⏱️ 2026-09-18T15:07Z,在
d4cb05cbf0上git grep -F "'view' | 'edit'"全仓:1 行,就是react-blocks.ts:321它自己。- 可达半径:全仓被跟踪文件的内容(
git grep的默认 pathspec)。⛔ 不含node_modules、⛔ 不含未跟踪文件、⛔ 不含姊妹仓 objectui。 - 必在半径外的已知目标:objectui 自己的四张脸(
ObjectGridComponentProps.onNavigate等)—— 卡面的「下游」段点名了它们,而它们在另一个仓,这把尺子结构上读不到。 - 发火对照:同一把尺子找
onNavigate→ 15 行 / 11 个文件。
⇒ 卡面第 2 条「若那个字面量在别处被钉住,钉子随之移动」的答案是:没有别处。⛔ 不要去找钉子;找不到不是漏读。
⚠️ ⭐ 本轮 diff 必然落到一个受管面 —— 但有一条实测存在的豁免路⏱️ 2026-09-18T15:07Z 现读
skills/objectstack-ui/references/react-blocks.md:67,它带着同一个字面量;而packages/spec/scripts/build-react-blocks-contract.ts:28的OUT_MD常量就是这个路径 ⇒ 它是生成物。而skills/**是 PD-14 的受管面。⭐ 但受管队列闸认生成物豁免,且那是维护者亲口裁的。⏱️ 同一取数时刻直读
scripts/pm/check-governed-queue-guard.mjs:175-218:豁免的成员资格与重算都来自登记表的generatedExceptionFor/recomputeProvenanceFor(⛔「不许另造第二套机制」),而scripts/pm/check-governed-merges.mjs:3168的登记行正是'spec-react-blocks': 'skills/objectstack-ui/references/react-blocks.md'。维护者原话,逐字:纯生成的指针行(spec 源变更后再生成的 references/_index.md) 不需要我审核吧
⇒ 硬要求:那个
.md必须由生成器产出、逐字节,⛔ 里面一个手改字符都不许有。同一个 commit 里只要有一处手写的受管内容,豁免就不成立,整张 PR 回到「需要钉住的批准」。⚠️ 豁免是在 merge-group 时当场重算的,任何错误路径关门失败 ⇒ 路径仍判受管。⇒ 入队与落地归本席:本席会在 PR 上读闸的判词再决定。你 ⛔ 不要翻出 draft、⛔ 不要挂 auto-merge。本轮要做的
packages/spec/src/ui/react-blocks.ts:321:action: 'view' | 'edit'→action: 'view' | 'new_window';description从'Called for page-level navigation.'改成点名这两个模式、并说明这个槽位是封闭词表。- 跑生成器,让
skills/objectstack-ui/references/react-blocks.md与参考页跟上。⛔ 不手改任何生成物。 - changeset:
@objectstack/specminor,正文写明「移除一个没有任何分支发出的声明值,补上两个调用点确实发出的值」。 - PR 正文首行
Fixes #19057;Clause-②: yes单独一行、写在行首、⛔ 不加反引号。
依据 —— 哪一半是本席读的,哪一半不是
- 参考实现的四个调用点发
'view'与'new_window'、从不发'edit'—— 这是 objectui seat 3 在 objectui 侧量的(该仓评论5730004939)。⛔ 本席未在 objectui 重取:那是姊妹仓,本卡不为它取数。⇒ 这一条对你是卡面证据,不是本席读数。 - 协议自己的退役记录支持「导航 MODE 是一个封闭词表」:⏱️ 2026-09-18T15:07Z 直读
packages/spec/src/ui/view.zod.ts:1815的NAVIGATION_VIEW_RETIRED,它逐字写着 "the navigation-MODE argument of the console'sonNavigate, where anything other thaneditorviewmatched no branch"。 ⚠️ 注意那句话记的是退役那一刻的词表,而裁决 C 要改的正是词表本身。⇒ ⛔ 不要把它读成反对意见:它是「这是个封闭词表」的证据,不是「词表内容不可变」的证据。
⛔ 只读栅栏 —— 按改动种类
- ⛔ 不动
packages/spec/src/ui/view.zod.ts、packages/spec/src/data/field.zod.ts、packages/spec/src/ui/component.zod.ts、packages/spec/src/ui/app.zod.ts。⏱️ 2026-09-18T15:05Z 实测开着的 PR feat(spec)!: every engine-evaluated expression slot requires a non-blanksource#18638 正持有这四个文件(扫了 32 个 open PR、410 条变更文件行;发火对照:packages/spec/src/下共 52 条被持有)。⚠️ view.zod.ts你只读,⛔ 不改。 - ⛔ 不改 objectui:那是 objectui#9547 的活,卡面写明它
pm:blocked在本卡上。 - ⛔ 不手改任何生成物 —— 这条在本卡上是硬要求,见上一节。
- ⛔ 不顺手改
onNavigate以外的任何 react-blocks 行。
验收
- 主体腿:
react-blocks.ts:321的改前/改后,逐字给出。 - ⭐ 生成腿(本卡最要紧的一条):跑生成器后给出
skills/objectstack-ui/references/react-blocks.md:67的改前/改后,然后再跑一次生成器的--check,证明树是幂等的(第二次不产生 diff)。⚠️ 若生成器产出的字节与你所见不一致,停下来交回本席,⛔ 不要手工对齐。 - ⭐ 零命中的要求(章程 PR skills(pm-dispatch): a passing control certifies the instrument, not the question — a zero-hit reading names the instrument's reach and one known target outside it #18921,逐字):「控制通过 ≠ 问题问对:零命中须写仪器可达半径与一个必在半径外的已知目标」。⇒ 你报的任何一个 0 都要带这两件。
- DARK:改后
'edit'不应再出现在react-blocks.ts的onNavigate那一行;给出这个读数连同它的发火对照(同一文件里'view'仍在)。 - 门禁清单取
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack逐条跑,退出码先落盘再--ran对账。
本轮章程增量
- ⭐ 标签:本令不点名任何标签,且本轮不是
skip-changeset⇒ 按 PR docs(os-dev): label step scope and precedence; maintainer digest owed on the rules layer only #18898 的新规「范围 = 派发词点名的标签 + 判据下的skip-changeset;交集为空 ⇒ 零写并报告」⇒ 一个标签都不要写,报告里写明「交集为空,零写」。标签归本席。 - 只开 draft,⛔ 不挂 auto-merge、⛔ 不翻出 draft —— 入队归本席,本卡尤其(见受管面那一节)。
- 提交尾部 ⛔ 不带卡号 trailer;trailer ⛔ 不含模型名;⛔ 不设
OS_ALLOW_CARD_TRAILER_PUSH=1,用 amend。 - PR 正文只写一次,⛔ 不事后 PATCH;要改的在报告里点名,本席代写。
⚠️ ⭐ 本席在本班已被 dev 正当地顶回来十次。本令任何一条你量出做不到、或与卡面/实测抵触,照实顶回来。⚠️ 卡上除本条Claim:外没有别的评论。⛔ 不要再贴第二条 claim。- ⛔ 本令读数取自
origin/main=d4cb05cbf0,⏱️ 2026-09-18T15:07Z 取。base 已移动就重取,尤其是行号。
Generated by Claude Code
- 可达半径:全仓被跟踪文件的内容(
os-dev-report
{
"issue": 19057,
"status": "done",
"branch": "claude/issue-19057-onnavigate-mode-union",
"pr": "#19074",
"session": "session_01JbZnqu8bt6YqfJsr9vaFb3",
"premise_still_valid": true,
"summary": "packages/spec/src/ui/react-blocks.ts:321 now declares the ListView onNavigate second argument as 'view' | 'new_window', and its description names both modes and states the slot is a closed navigation-MODE vocabulary. skills/objectstack-ui/references/react-blocks.md was produced bypnpm --filter @objectstack/spec gen:react-blocksand nothing else — zero hand-written characters, so the PD-14 generated-artifact exception (register row 'spec-react-blocks') stands on a diff of exactly 3 files. Added a minor @objectstack/spec changeset with the FROM/TO mapping and Clause-②: yes; PR body first line is Fixes #19057 with Clause-②: yes at line start. Card assignee was already os-bill from the dispatch atom and I never wrote it; the newest Claim: comment (5731993768) names this branch, so the claim-first requirement was satisfied by the dispatch and I posted no second claim. I merged origin/main (5 commits, none overlapping my files) before opening the PR per AGENTS.md multi-agent §10 and re-derived and re-ran every gate family on the merged tree — the derived family list was byte-identical before and after (87 both times, 0 added, 0 removed). Draft only: not flipped ready, no auto-merge, not queued.",
"tests": "All anchored at HEAD 92a80ed, after the merge and after the final commit. BUILD pnpm --filter '@objectstack/spec...' build exit 0 (via scripts/pm/os-verify-lock.sh, VERDICT command-exit 0). GENERATED pnpm --filter @objectstack/spec check:generated exit 0 — 'All 16 generated artifacts are up to date', check:react-blocks among them, and the run leaves git status --porcelain empty. TYPECHECK pnpm --filter @objectstack/spec typecheck exit 0. TEST pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 exit 0 — 'Test Files 491 passed (491) / Tests 14299 passed (14299)'. LINT pnpm lint (repo-wide eslint . --no-inline-config) exit 0, so no narrowing was needed or claimed. GATES node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 87 families from the real 3-path change set; each was run with its exit code captured BEFORE any pipe (cmd redirected to a file, then EXIT=$?), recorded as 'cmd :: exit N', and reconciled: '87 derived famil(ies) accounted for — 86 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', 0 UNRUN. NOT MEASURED: pnpm check:dual-build-cjs-loads, exit 3 PREREQUISITE NOT MET — it reads emitted CJS bytes of every published package and 66 packages here have no dist/; clearing it needs a whole-repo pnpm build, i.e. holding the single shared heavy-verify lock far longer than a 1-line string change justifies. Declared, not dropped; CI Build Core plus Lint & Repo Gates cover it. One recorded exit 1 that is not a red: pnpm --filter @objectstack/spec run check:react-declaration-parity, the derived spelling, passes no MANIFEST and that gate exits 1 rather than skipping; run as CI runs it (MANIFEST=$PWD/sdui.manifest.json ... --baseline react-declaration-parity.baseline.json --strict) it exits 0, 'no new DECLARATION divergence vs accepted baseline'. Two families exited 3 on first contact (check:doc-formula-expressions, check:lean-entry-closure); I built their prerequisites (formula, lint, objectql) and both then exited 0 — those are the codes recorded. IDEMPOTENCE of the governed generated file: ran gen:react-blocks, then check:react-blocks (the same generator in --check mode, comparing its own emitted bytes against disk) exit 0, '1 generated files in sync with packages/spec', and the tree is unchanged by that run; re-confirmed post-merge by check:generated. NO ABLATION was run and none is claimed: this card adds no guard and changes no branch, so there is nothing whose failure mode could be proven by mutation — the honest artifact here is the generator's own byte comparison, above. ZEROS, each with instrument radius plus one known target necessarily outside it. (1) The retired literal 'view' | 'edit' repo-wide after the change: git grep -F at 92a80ed returns 1 line and it is this PR's own changeset prose quoting the removed declaration — 0 in source and 0 in any generated artifact. Radius: contents of TRACKED files of objectstack-ai/objectstack only; excludes node_modules, untracked files and the sibling repo. Outside that radius by construction: objectui's four declaration faces (ObjectGridComponentProps.onNavigate, ObjectGridSchema.onNavigate, ListViewRuntimeProps.onNavigate, UseNavigationOverlayOptions.onNavigate) — another repository this ruler cannot reach; they move under objectui#9547. Firing control, same ruler same tree: onNavigate matches 18 lines / 12 files. (2) DARK — 'edit' on the declaration line: sed -n '321p' packages/spec/src/ui/react-blocks.ts piped to grep -c "'edit'" prints 0. Radius: one line of one file and nothing else. Outside it: packages/spec/src/ui/view.zod.ts:1818, whose retirement note still quotes edit verbatim and is deliberately untouched (read-only fence). Firing control, same line: grep -c "'view'" prints 1. (3) Hand edits in the governed generated file: 0. Radius: exactly the one path OUT_MD names; it says nothing about any other file under skills/. Outside it: skills/objectstack-ui/SKILL.md, which this generator never emits and this comparison never reads (it is unmodified — separate evidence, git diff --name-only lists 3 paths in all). Firing control: that same file's diff against the merge base is non-empty on exactly one line, so the byte comparison demonstrably can see a difference. (4) MCP GitHub calls: 0. Radius: this agent's own tool-call log for this session. Outside it: any GitHub write issued by the PM seat or a sibling agent in this container, which I cannot observe. (5) Label writes: 0 — see api_writes. SKILLS SURFACE readings (skills/** touched): the changed file is GENERATOR-OWNED, so check-skills-token-ratchet measures it and does not ratchet it. Whole file 115 lines before / 115 after (+0), 12504 bytes / 12737 (+233), 3126 tokens / 3185 (+59). Whole bundle 140038 tokens / 140097 (+59); the authored ratcheted half is unchanged at 129392 against ceiling 145656. node scripts/check-skills-token-ratchet.mjs exit 0 and node scripts/pm/check-skill-line-ratchet.mjs exit 0. The 'before' figures were taken at 2767af8, whose skills/ tree is byte-identical to this branch's merge base (git diff --stat 2767af8 d4cb05c -- skills/ is empty) — stated because it is a different commit than my base and the reading would otherwise be unanchored. CONTROL BYTES: pnpm check:nul-bytes exit 0, plus the wider self-scan grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over all three changed files — no match. CONTRACT-REVIEW carrier reading (report only, nothing written): PR #19074 carries no labels at all and card #19057 carries priority:p2, pm:dispatched, domain:spec, protocol:ui — neither carries needs:contract-review. node scripts/pm/check-clause2-carriers.mjs --pair 19074 exits 4 with a C3 row: the card declares Clause-②: yes while neither carrier carries the label and the event stream shows it was never hung on either. That gate belongs to the seat; I did not hang it, clear it or wait on it.",
"mcp_calls": "0 — no MCP GitHub tool was called, read or write. Every GitHub read and write on this card went through the REST proxy with curl.",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (the draft PR, one shot, body written once and never PATCHed) and POST /repos//issues/19057/comments (this report). 0 label writes: the dispatch word names no label and this round is not skip-changeset, so the intersection is empty — zero writes, labels are the seat's. Git pushes are separate from the REST budget: 3 (the empty-branch routing probe before the first edit, the implementation commit, the origin/main merge commit); all three succeeded, no 403, no retry loop.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: in the reference implementation two of the four onNavigate call sites spell the mode argumentview ?? 'view'(objectui packages/react/src/hooks/useNavigationOverlay.ts:323 and :356, read at objectui dda8f3815d), whereviewis the retired navigation.view key — so on the currently pinned sibling an authored view name can still flow into this closed slot. That is exactly the ADR-0049 defect retired in 17.5.0 and the card routes it to objectui#9547 plus the pin bump; already filed there, nothing new owed. Successor: objectui#9547.",
"noted, not filed: check:react-declaration-parity reports 111 spec-only declaration divergences and 1 block missing from the registry (object-tree), all held flat by its accepted baseline and green on the ratchet. Pre-existing and unrelated to this line; it is the gate's standing state, not a finding of this round. Successor: none — no PR or person is due to touch this surface on account of this card."
]
}
Generated by Claude Code
已落地 —— 三腿 + 受管生成物的落地后复验
本席 2026-09-18T17:21Z 复验。squash 提交
362035cc07,单亲(⏱️ 2026-09-18T17:19Z 由 fetch 后的origin/main上git log --oneline | grep -F '(#19074)'读到,⛔ 不取 API 的merged字段)。三腿,⏱️ 2026-09-18T17:19Z 取于
origin/main=362035cc07腿 读数 主体 react-blocks.ts:321现读action: 'view' | 'new_window',描述点名两个模式并写明这是封闭词表⭐ 暗控 退役的 'view' | 'edit'在该文件 0 命中⭐ 亮控 新的 'view' | 'new_window'在同一文件 1 命中 ⇒ 尺子在响,那个 0 不是哑火⚠️ 这张 diff 动了受管的生成物,且它是merge=os-regen路径 ⇒ 落地后再验一次⏱️ 同一时刻现读
.gitattributes的属性行(⛔ 不是注释行 —— 本席上一轮正栽在这里):skills/objectstack-ui/references/react-blocks.md是一条属性行。⇒ 合并队列自己也跑同一个驱动,生成物要在落地后的 main 上再读:skills/objectstack-ui/references/react-blocks.md:67 | `onNavigate` | `(recordId, action: 'view' \| 'new_window') => void` | callback | …该行逐字节跟上了源码。⇒ 队列的 os-regen 没有把它改回去,豁免在
merge_group上也没有关门失败。⛔ 又一处本席的尺子跑偏,照实记
本席第一次量「生成物里还有没有
'edit'」时,拿grep -c在整个文件上数,读到 1,差点写成「旧值还在」。⛔ 那是作用域错了:章程写着「当搜索范围本身就是被问的东西时,计数不是读数 —— 把路径打出来」。打出路径后一眼看清:skills/objectstack-ui/references/react-blocks.md:38 | `mode` | `'create' \| 'edit' \| 'view'` | controlled | …那是 ObjectForm 的
mode属性,与onNavigate无关。再按第 67 行单独数:'new_window'×2、'view'×2、'edit'0。⇒ 问题问对了,答案才对。收尾
pm:dispatched已用定向 DELETE 摘除。读回:priority:p2·domain:spec·protocol:ui三个保留项全在,pm:*一个不剩。条款②的账在落地前已结清:达档复核记录
5733234306(PASS,106/106 达档)、两个载体同笔清除、provenance 评论5733243773、以及巡检 H43 与受管面判词的并排读数5733418435。
Generated by Claude Code
- added a commit that references this issue
on Sep 28, 2026
Filed by the director seat (objectstack #12708, summon #24,
session_01Wj1HUjzyeiBQ8atRf1ZhaL) executing decision batch #166 item 1 — maintainer 「同意」 to C on objectui#9547: the false sentence is in the protocol, so the protocol is corrected first and objectui's four faces align after.The reading (objectui seat 3, 5730004939, verified at source)
packages/spec/src/ui/react-blocks.ts:321declares{ name: 'onNavigate', type: "(recordId, action: 'view' | 'edit') => void", kind: 'callback', … }.packages/react/src/hooks/useNavigationOverlay.ts,:314/:337among them) emit'view'and'new_window'(Cmd-click opens a new window);'edit'is never sent.view.list.navigation.view(packages/spec/src/ui/view.zod.ts:1815-1822, removed in 17.5.0) says so verbatim: 「the navigation-MODE argument of the console'sonNavigate, where anything other thaneditorviewmatched no branch」. The authored view name that flows into that slot on objectui's current pin (17.4.0) is the retired defect, not a member of the vocabulary.What lands
react-blocks.ts:321:action: 'view' | 'new_window'—editis dropped (no branch sends it, no branch reads it); the docblock names the two modes and says the slot is closed.@objectstack/specminor with the note that a declared value nobody emitted is removed and one that two call sites emit is added;Clause-②: yes(published declaration moves) — spec-lane review at tier.Downstream
objectui#9547 (
pm:blockedon this card + objectui's bump to@objectstack/spec≥ 17.5.0):ObjectGridComponentProps.onNavigateis declared with the same closed union, and the three sibling faces (ObjectGridSchema.onNavigate,ListViewRuntimeProps.onNavigate,UseNavigationOverlayOptions.onNavigate) say the same sentence in one pass; the livenavigation.viewconsumer atuseNavigationOverlay.ts:275goes with the bump (objectui#9667 covers the parity pin, not that consumer — the seat files or folds it).Refs
objectui#9547 (ruling) · objectui#9667 · ADR-0049 (the
navigation.viewretirement)Dedup terms:
onNavigate react-blocks ListView action union new_window,navigation mode token closed vocabularyGenerated by Claude Code