Skip to content

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

@hotlong

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:321 declares { name: 'onNavigate', type: "(recordId, action: 'view' | 'edit') => void", kind: 'callback', … }.
  • The reference implementation's four call sites (objectui packages/react/src/hooks/useNavigationOverlay.ts, :314 / :337 among them) emit 'view' and 'new_window' (Cmd-click opens a new window); 'edit' is never sent.
  • The second argument is a navigation-MODE token with a closed vocabulary — the protocol's own retirement note for 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's onNavigate, where anything other than edit or view matched 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

  1. react-blocks.ts:321: action: 'view' | 'new_window' — edit is dropped (no branch sends it, no branch reads it); the docblock names the two modes and says the slot is closed.
  2. If the literal string is pinned anywhere (the seat did not measure this), the pin moves with it.
  3. Reference page regenerated; changeset @objectstack/spec minor 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:blocked on this card + objectui's bump to @objectstack/spec ≥ 17.5.0): ObjectGridComponentProps.onNavigate is 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 live navigation.view consumer at useNavigationOverlay.ts:275 goes 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.view retirement)

Dedup terms: onNavigate react-blocks ListView action union new_window, navigation mode token closed vocabulary


Generated by Claude Code

Activity

  1. os-bill commented on Sep 18, 2026

    @os-bill
    Collaborator

    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。

    本轮要做的

    1. packages/spec/src/ui/react-blocks.ts:321:action: 'view' | 'edit' → action: 'view' | 'new_window';description 从 'Called for page-level navigation.' 改成点名这两个模式、并说明这个槽位是封闭词表。
    2. 跑生成器,让 skills/objectstack-ui/references/react-blocks.md 与参考页跟上。⛔ 不手改任何生成物。
    3. changeset:@objectstack/spec minor,正文写明「移除一个没有任何分支发出的声明值,补上两个调用点确实发出的值」。
    4. 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's onNavigate, where anything other than edit or view matched 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-blank source #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 对账。

    本轮章程增量

    1. ⭐ 标签:本令不点名任何标签,且本轮不是 skip-changeset ⇒ 按 PR docs(os-dev): label step scope and precedence; maintainer digest owed on the rules layer only #18898 的新规「范围 = 派发词点名的标签 + 判据下的 skip-changeset;交集为空 ⇒ 零写并报告」⇒ 一个标签都不要写,报告里写明「交集为空,零写」。标签归本席。
    2. 只开 draft,⛔ 不挂 auto-merge、⛔ 不翻出 draft —— 入队归本席,本卡尤其(见受管面那一节)。
    3. 提交尾部 ⛔ 不带卡号 trailer;trailer ⛔ 不含模型名;⛔ 不设 OS_ALLOW_CARD_TRAILER_PUSH=1,用 amend。
    4. PR 正文只写一次,⛔ 不事后 PATCH;要改的在报告里点名,本席代写。
    5. ⚠️ ⭐ 本席在本班已被 dev 正当地顶回来十次。本令任何一条你量出做不到、或与卡面/实测抵触,照实顶回来。
    6. ⚠️ 卡上除本条 Claim: 外没有别的评论。⛔ 不要再贴第二条 claim。
    7. ⛔ 本令读数取自 origin/main = d4cb05cbf0,⏱️ 2026-09-18T15:07Z 取。base 已移动就重取,尤其是行号。

    Generated by Claude Code

  2. self-assigned this
    on Sep 18, 2026
  3. os-bill commented on Sep 18, 2026

    @os-bill
    Collaborator

    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 by pnpm --filter @objectstack/spec gen:react-blocks and 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 argument view ?? 'view' (objectui packages/react/src/hooks/useNavigationOverlay.ts:323 and :356, read at objectui dda8f3815d), where view is 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

  4. os-bill commented on Sep 18, 2026

    @os-bill
    Collaborator

    已落地 —— 三腿 + 受管生成物的落地后复验

    本席 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

  5. added a commit that references this issue on Sep 28, 2026
    362035c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions