Repository navigation
docs(agents,settings): port the write-identity locks — deny MCP content writes, §9 states ruling C (objectui#9418) - #9448
Conversation
…i#9418) Write-identity lock 1, ported from objectstack `origin/main` (`7ef05f997`). Every content write from a seat or a dev agent now has to leave through the REST proxy, where it is authored by the app identity `claude[bot]` instead of the claude.ai account's linked GitHub user. The failure mode this closes is not hypothetical and not about tidiness: a suspended user account hides everything it authored. When that happened on this fleet the losses were a triage seat's round comments, cards that went 404 to every reader, and a shift closed out over it — work that existed, was correct, and simply stopped being visible. An app identity cannot be suspended the way a user account was, so work authored by it does not vanish with one account's status. The list is copied verbatim by a script that parses both JSON documents and moves the array object across — never retyped. A hand-copied deny list that is one entry short is a hole that nothing reports: the missing tool keeps working, under the wrong identity, and no gate and no reader can tell. The four state-shaped tools stay allowed on purpose (`update_pull_request`, `enable_pr_auto_merge`, `actions_run_trigger`, `resolve_review_thread`): they author no content, so denying them buys no identity benefit, and the landing path needs them. Nothing else crosses over — objectstack's own hooks and allow list stay there. This repo's allow list and hooks are byte-identical to `origin/main`; the diff is purely additive. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
…, then the seat lands (objectui#9418) Write-identity lock 5. §9's governed-surface paragraph said a governed PR stops at draft and waits for a HUMAN MERGE, and that the human's merge action is itself the review record — so there was no approval to wait for, and the paragraph said so in as many words. Ruling C moved the landing action without moving the restraint: a governed hit still stops at draft, but what releases it is an authorized APPROVED review (`GOVERNED_APPROVERS` — os-zhuang / hotlong — latest-decisive, not dismissed), after which the CLAIMING SEAT lands it through the merge queue. The restraint is unchanged in substance and the fifth prohibition is unchanged in text: a seat never leaves that approval itself, under any account. The hard-rule line is replaced equal-line, and states the rule in objectstack `lanes/ui.md`'s own words rather than a paraphrase of them. That is not style: this paragraph is the operating procedure every later dispatch reads, and two wordings of one rule drift apart silently — the repo has already paid for that once, with the `skills/**` clause that was written backwards here while the guard read it the other way. Four other lines named the landing action as a human merge — the exception clause in the "CI 全绿即自行合并" bullet, the subsection heading, the `skills/**` bullet, and the mixed-diff bullet. Each gets the minimal word change, because a paragraph that states the new rule once and the old rule four more times is worse than either rule alone. The line describing what `scripts/check-governed-queue-guard.mjs` PRINTS is deliberately left alone: it describes the script, not the rule, and the script still prints the draft-and-human-merge remedy. That divergence is real and is called out in the pull request rather than quietly papered over here. Net 0 lines; no other line in the file moves. The changeset declares no release with an empty frontmatter — neither path is released package source. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
Contract reviewHead: ① derived judgments (seat-measured on the fetched head, ⛔ not taken from the report):
② semver: unchanged — nothing released moved; the empty-frontmatter changeset declares no release ( ③ boundary flags for the approver: (a) line 519 「CI 全绿、已 review 都不构成例外。」 is unchanged — 「已 review」 now sits next to the one thing that IS the exception under ruling C (an authorized approval); the sentence after it keeps its subject green CI, so it reads coherent; the approver rules whether to touch it (⛔ not widened here). (b) Implemented-by: Verdict: PASS — awaiting an authorized approval; ⛔ never ready / enqueue / auto-merge before it; the seat lands after. 维护者速读(终稿)objectui 补上 objectstack 昨天落地的两道锁的本仓半边:① Generated by Claude Code |
|
Ruling C landing step — skills seat (session Generated by Claude Code |
…ude/settings.json (objectui#9586) (objectstack-ai#10214) Fixes objectstack-ai#9586 ⛔ **GOVERNED — this pull request is a DRAFT and stays one.** The changed instruction-tree path is governed surface in this repository. Asked of the guard itself, in this worktree: ``` $ node scripts/check-governed-queue-guard.mjs --test .claude/settings.json .changeset/issue-9586-enqueue-class-deny-mirror.md ⛔ GOVERNED — 1 of 2 path(s) are on a governed surface: .claude/** x1 — the agent instruction tree (skills, hooks, settings) - .claude/settings.json One governed path governs the WHOLE pull request — proportion is not a question. ⛔ Do not flip it ready, enqueue it, or arm auto-merge. Park it as a DRAFT and leave the merge to the maintainer; a human merge IS the review record for a governed surface. The merge-queue run of "Governed Surface Queue Guard" refuses this diff unless an APPROVED review by an authorized approver (GOVERNED_APPROVERS: os-zhuang, hotlong) is on the pull request — on whichever commit it was left (maintainer ruling 2026-09-04). (exit 3) ``` ⇒ ⛔ never flipped ready, ⛔ never enqueued, ⛔ never armed with auto-merge, ⛔ never approved by a seat under any account. Under ruling C the claiming seat lands it only once an authorized APPROVED review exists. ## 维护者速读(草稿) **改了什么** 一个文件、三行:`.claude/settings.json` 的 `permissions.deny` 从 14 条加到 17 条,新增点名三个 MCP GitHub 工具 —— `update_pull_request`、`enable_pr_auto_merge`、`disable_pr_auto_merge`。改完之后,本仓的 deny 数组与 objectstack 的**逐元素相同、顺序也相同**。`allow` 与 `hooks` 两块字节未动。另附一个空 frontmatter 的 changeset(声明「不发版」,本仓惯例)。 **为什么改** 这是 objectstack 已裁决条款的跨仓镜像,不是本仓的新主张。objectstack#18282 裁决 A(批次 objectstack-ai#135 追认)把「声明即强制」扩到**入队类**:席位在两个板上真正用的入队通路只有 REST 的 `PUT .../pulls/{n}/ccr/auto_merge`(它就在同一个文件的 `allow` 里),所以把 MCP 的那两个入队工具关掉,**不损失任何能力**,只是把一条没人走的旁路封死。 第三条 `update_pull_request` 是本仓自己的事故教训:objectui#6183 里,一次只传 `reviewers` 的 MCP 调用**顺带**把 PR 的 `draft: false` 设了,于是一个受管草稿进了合并队列、零人工批准就落了地。本仓当时只镜像了 14 条内容写入类,这一条(objectstack 在 PR #18276 落地)和入队那两条(PR #18317)都没跟上 —— 本次一并补齐。 **风险与代价(含回滚)** - 代价:任何还想用 MCP 去翻 ready、挂/摘 auto-merge 的流程会被运行时拒绝。这是有意的。`gh pr ready`、`gh pr merge --auto` 与 REST 的 ccr 路径都不受影响。 -⚠️ 已知代价,需要你知道:AGENTS.md 里有两处**配方文本**仍然点名这两个工具(自行合并那条的「MCP 等价物」一行,以及受管面段「发现自己已经挂上了怎么办」那条补救 —— 后者写的是「只调 `disable_pr_auto_merge` …… 两个都要做」)。本卡的文件面只有 `.claude/settings.json`,且 AGENTS.md 当前被两个在途 PR 持有(objectstack-ai#10212 / objectstack-ai#10213),所以本 PR ⛔ 不碰它,改由席位另立卡。方向是安全的一侧:配方失效会**响亮地被拒**,不会静默做错事。 - 回滚:`git revert` 单个 commit 即可,无数据迁移、无构建产物、无已发布包字节变化。 **席位意见** (留空,交席位定稿) **你要做的** 确认「入队类 MCP 工具一并关掉」这个方向是你要的(objectstack 那边已经这么定了),然后在本 PR 上留一条 APPROVED review。批准之后由认领席翻 ready、入队落地,你不需要自己合并。上面那条⚠️ 只需要你知道,不需要你在这张卡里处理。 ## 前提核验(动手前,在 `origin/main` `0cf2d6644` 上实测) | # | 前提 | 读数 | 结论 | |:--|:--|:--|:--| | P1 | 卡片说本仓 deny 有 14 条 `mcp__github__*`,且三条都不在 | 实测 deny 总数 14,全部是 `mcp__github__*`;三条各命中 0 次,正控 `issue_write` 命中 1 次 | 成立 | | P2 | 本仓有没有 deny 名册门禁(objectstack 的 `scripts/pm/check-settings-deny-roster.mjs`) | 不存在;`scripts/pm/` 全部内容是 `board-snapshot.mjs` 与 `check-half-states.mjs`;全仓 grep `settings.json`,`scripts/` 与 `.github/` 内零个读取者 | 无 ⇒ ⛔ 不移植该门禁 | | P3 | 本仓有没有通道对照表(objectstack 的 `references/rest-channel.md`) | `.claude/` 下不存在任何 `references` 目录;`.claude/` 全部文件共 14 个(4 个 hook + 4 个 selftest + `launch.json` + `settings.json` + 2 个 skill 的 4 个 md) | 无 ⇒ 那一行事实无处可加 | | P4 | `.claude/settings.json` 是不是被 pin 的上游移植文件(改它会红 `check:upstream-port-parity`) | 不是。`scripts/upstream-port-pin.json` 13 条,`.claude/` 下只 pin 了 8 个 hook/selftest 文件 | 可以改;该门禁仍已跑,绿 | | P5 | 并发:有没有别的在途 PR 碰这个文件 | 7 个 open PR 的文件清单逐一读过,零个碰 `.claude/settings.json`;`objectstack-ai#10212` / `objectstack-ai#10213` 只有 `AGENTS.md`(hunk 分别在 468 与 323 行区) | 无冲突 | ## 执行判据(卡片的那一条) 推送之后,`GET /repos/objectstack-ai/objectui/contents/.claude/settings.json?ref=claude/issue-9586-settings-deny-auto-merge-mirror` 解码后的 `permissions.deny`(HTTP 200,blob sha `30c3367037de7ba000673e4da8caa2913a9c704c`,size 2255): ``` 1 mcp__github__issue_write 2 mcp__github__create_pull_request 3 mcp__github__update_pull_request ← 新增(objectstack PR #18276 的镜像) 4 mcp__github__add_issue_comment 5 mcp__github__add_comment_to_pending_review 6 mcp__github__add_reply_to_pull_request_comment 7 mcp__github__pull_request_review_write 8 mcp__github__push_files 9 mcp__github__create_or_update_file 10 mcp__github__delete_file 11 mcp__github__create_branch 12 mcp__github__sub_issue_write 13 mcp__github__merge_pull_request 14 mcp__github__create_repository 15 mcp__github__fork_repository 16 mcp__github__enable_pr_auto_merge ← 新增(本卡的判据) 17 mcp__github__disable_pr_auto_merge ← 新增(本卡的判据) ``` 两条判据条目都在 ⇒ 判据满足。 ## 改动的形状,实测而非声称 ⛔ 没有手打重排。两处锚点文本替换,锚点各自唯一,其余字节不动: ``` deny base: 14 branch: 17 objectstack: 17 branch deny === objectstack deny (order included): true allow identical to origin/main: true sha 46560076ce2be885 = 46560076ce2be885 hooks identical to origin/main: true sha 10e6c3a3ae991607 = 10e6c3a3ae991607 top-level keys / permissions keys: 与 origin/main 相同,无增删 added: ["mcp__github__update_pull_request","mcp__github__enable_pr_auto_merge","mcp__github__disable_pr_auto_merge"] removed: [] duplicate entries: 0 git diff --numstat: 4 1 .claude/settings.json ``` 顺序惯例是**按类分组、不是字母序**(实测:objectstack 把 `update_pull_request` 紧挨 `create_pull_request`、把入队对追加在末尾),本 PR 照同样的位置插入,结果两仓数组完全一致。 两个读者都能解析:`node JSON.parse OK` · `python json.load OK`。`settings.json` 里 wire 的 4 个 hook 命令全部落在磁盘上(4/4 OK)—— 这个文件正是点名它们的那一个。 ## 门禁 门禁族由本仓自己推导:objectui 没有 `scripts/pm/dispatch-gates.mjs`(不存在),所以逐个读 `.github/workflows/*.yml` 的触发条件,取「会判到这两个路径」的全集。 | 门禁 | exit | 判定行 | |:--|:--|:--| | JSON 有效性 | 0 | `node JSON.parse OK` · `python json.load OK` | | `check-control-bytes`(commit 后重跑) | 0 | `✅ check-control-bytes: OK (scanned 8217 tracked text file(s); skipped 85 binary).` | | `check-governed-queue-guard --self-test` | 0 | `OK check-governed-queue-guard self-test: 185 cases pass` | | `check-governed-queue-guard --test` | 3 | 本文顶部那段(受管,exit 3 是本 PR 的健康读数) | | `check-changeset-presence` | 0 | `✅ No source or published contract of a released package changed in this range, so no changeset is owed.` | | `check-changeset-no-major` | 0 | `✅ No changeset declares a major bump.` | | `check-changeset-overwrite` | 0 | `✅ No pre-existing changeset was modified or deleted.` | | `check-lint-coverage` | 0 | `✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).` | | `check-entry-guard --self-test` / 全跑 | 0 / 0 | `63 cases pass` · `108 scripts/ file(s) — no entry guard outside the baseline` | | `check-upstream-port-parity --self-test` / 全跑 | 0 / 0 | `66 cases pass` · `13 ported file(s) match objectstack-ai/objectstack modulo their declared divergences` | | `check-bash32-floor --self-test` / 全跑 | 0 / 0 | `160 cases pass` · `13 tracked shell file(s) … name no bash 4+ construct` | | `check-skills-paths` | 0 | `✅ check-skills-paths: OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined).` | | `check-new-cross-file-line-citations` | 0 | `VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0` | 退出码一律在任何管道之前捕获(先重定向到文件,再读 `$?`)。 **不欠的两族,连同不欠的理由:** `hook-selftests.yml` 的 paths 是 `.claude/hooks/**` 与它自己 —— 本 diff 不碰 hook 文件(hook 命令仍被 `settings.json` 点名,所以单独验了 4/4 落盘)。`check-shell-escape-residue` 的 `SCAN_ROOTS` 是 `AGENTS.md`、`CLAUDE.md`、`skills`、`.claude/skills`、`content/docs` 五条,`.changeset/**` 不在其中 ⇒ 新增的 changeset 不在它的总体里。 **lint 与 type-check:一次申报过的收窄,不是一次漏跑。** 三条读数里的两条来自仪器,第二条被替换,如实写明: 1. **总体读自 eslint 自己的配置**:`eslint.config.js` 里每一个 `files` 块都限定在 TS/TSX(`**/*.{ts,tsx}`、`scripts/**/*.{ts,tsx}`、`**/*.test.{ts,tsx}`、`**/__tests__/**/*.{ts,tsx}`、`**/examples/**/*.{ts,tsx}`、`**/*.tsx` 及三个具名单文件),没有 JSON 块,也没有 markdown 块。 2. **文件数**:⚠️ 本次**不是**读自 `--format json`,而是读自 diff 的扩展名普查 —— 本分支 `git diff --name-only` 两个文件,`.json` 与 `.md` 各一,`.ts/.tsx/.js/.mjs/.cjs` 命中 **0** ⇒ 落在 eslint 总体里的文件数必然为 0。这个容器里 objectui 没有 `node_modules`(共享检出与本 worktree 均无),而为一个 JSON + markdown 的 diff 做一次全仓 `pnpm install` 是这台共享机器上最贵的动作、且买不到任何判定。⇒ 这一格的仪器读数记 NOT MEASURED,理由如上;推翻它只需要一个 `.ts` 文件,而本 diff 没有。 3. **对未触碰文件的不变性**:type-aware linting 全仓未启用(`eslint.config.js` 里没有任何块设 `parserOptions.project` 或 `projectService`)⇒ 本 diff 移不动任何它没触碰的文件的判定。同一读数覆盖 `type-check`:tsc 的输入字节未变。 仓级 `pnpm lint` / `pnpm type-check` 农场是 CI 的运行。⚠️ 并且它**会**在本 PR 上跑满:`lint.yml` 的相关性判断只排除 `**/*.md`、`content/**`、`docs/**`、`.changeset/**`,而 `.claude/settings.json` 不在排除列里 ⇒ `should_run=true`,安装之后的那半边门禁由 CI 如常判定。 上述读数取自本分支最终 commit `45f22049`。 ## Acceptance notes 顺路看到、⛔ 本 PR 一律没动的: - **AGENTS.md 有两处配方点名了本 PR 刚关掉的工具。** 一处在「CI 全绿即自行合并」那条的代码块里(`# MCP 等价物:pull request update(draft: false) + enable_pr_auto_merge`),一处在受管面段的补救条(「只调 `disable_pr_auto_merge` 会摘掉 auto-merge 但不取消队列成员资格,两个都要做」)。本 PR 落地之后,照这两处文本行事的席位会撞上运行时拒绝。objectstack 那边的对应修复是改它的通道对照表那一行(「auto-merge 备用 MCP 未拒」→「auto-merge MCP 锁 1 同拒」);本仓的等价文本恰好落在 AGENTS.md 里,而 AGENTS.md **不在本卡的文件面内**,且当前被 objectstack-ai#10212 / objectstack-ai#10213 两个在途 PR 持有。⇒ 交席位另立卡,本 PR 不碰。方向安全:失效的配方会被响亮拒绝,不会静默做错。 - **`.changeset/issue-9418-write-identity-locks-port.md` 里有一句已经过时的话。** 那份(空 frontmatter、永不消费的)历史声明写着四个「状态形」工具 —— 含 `update_pull_request` 与 `enable_pr_auto_merge` —— 「stay allowed」。本 PR 之后其中两个不再 allowed。它是一份历史记录而非规程,读者为零,⛔ 不改、也不建议为它单独立卡;记在这里只是为了下一个读到那句话的人不被它误导。 - **`check-governed-queue-guard.mjs` 的拒绝文案仍写着「a human merge IS the review record」**,而 AGENTS.md 已按裁决 C 改成「获授权批准后由认领席落地」。这条在 objectui PR objectstack-ai#9448 的 Acceptance notes 里已经报过一次,至今仍在;本 PR 只是再次撞见,不重复立卡。 `Clause-②: no` 本次实现所在的会话,写成散文以便在正文被改写后仍可追溯:`session_01Wnstp2kTth7sGXfr8fXypc`。 ## 席位意见(`domain:skills` seat 1,session `session_01Wnstp2kTth7sGXfr8fXypc`) **建议:批准并由席位落地。** 一个文件三行,把 objectui 的 `permissions.deny` 补成与 objectstack 逐元素、同顺序相同(席位在复核工作树上解析两边数组比对为真);`allow` / `hooks` 未动;三条都在卡的裁决范围内(objectstack#18282 裁决 A、批次 objectstack-ai#135 追认;`update_pull_request` 是卡正文点名的「本仓一并缺失」项)。不损失能力:两个板上席位真正用的入队通路只有 REST `PUT …/ccr/auto_merge`,就在同一文件的 `allow` 里。卡留给取卡者的两个可选项 —— 移植 deny 计数门禁、频道表加一行 —— 本树都不存在对应物,均未做(新增门禁默认否)。 **你需要知道的一件事(不阻塞本 PR):** 落地后 objectui `AGENTS.md` :331 与 :531 两处配方文字仍写着 `enable_pr_auto_merge` / `disable_pr_auto_merge` 作为 MCP 等价物,届时会被响亮拒绝而非静默执行错;两个在途 AGENTS.md PR(objectstack-ai#10212 :468、objectstack-ai#10213 :323)不在那两段,不能顺带。席位身份过不了立卡门,已记到席位帖的维护者事项里,由你决定是否立卡。 复核记录:PR 评论 5778587633(PASS,在席按服务档渲染);ACCEPT 在卡 objectui#9586。CI 读数:42 latest-per-name check runs — 39 success, 3 skipped, 0 in progress, 0 other。 --- _Generated by [Claude Code](https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc)_ --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #9418
⛔ GOVERNED — this pull request is a DRAFT and stays one. Both changed paths are governed surface in this repository. Asked of the guard itself, in this worktree:
⇒ ⛔ never flipped ready, ⛔ never enqueued, ⛔ never armed with auto-merge, ⛔ never approved by a seat under any account. Under ruling C — which is the rule this pull request writes into §9 — the claiming seat lands it only once an authorized APPROVED review exists (
GOVERNED_APPROVERS: os-zhuang / hotlong).维护者速读(草稿)
改了什么
两件,都是把 objectstack 已经落地的规则搬到本仓:
.claude/settings.json新增一个permissions.deny,点名 14 个会写内容的 MCP GitHub 工具。AGENTS.md§9 受管面段:落地动作由「停在 draft,等人类合并」改为「停在 draft,等一条获授权的批准,然后由认领席落地」(裁决 C)。为什么改
第 1 条防的是一次已经发生的事故,不是假想。MCP 写入署名的是 claude.ai 账号绑定的那个 GitHub 用户;而一个被封的用户账号,会把它写过的一切一起藏起来 —— 评论、卡片,对所有读者 404。改走 REST 代理、署名
claude[bot](应用身份)之后,工作不再随某一个人的账号状态消失。这是对整类失败的结构性修复,不是配置整洁。第 2 条是让本仓的规程和已生效的裁决一致。原文写的是「人类的那次合并动作本身就是审核记录 —— 不需要额外的逐 PR 批准点击,也别去等一个不存在的 approval」;而现在恰恰有一条批准要等,批准之后落地是席位的事。规程与裁决不一致时,照规程做的席位会做错事 —— 而这一段正是后续每一次 dispatch 都要读的那段。
风险与代价(含回滚)
update_pull_request、enable_pr_auto_merge、actions_run_trigger、resolve_review_thread)保持放行,落地路径因此不受影响。git revert任意一个都不影响另一个。没有数据迁移,没有构建产物,没有已发布包的字节变化。席位意见
(留空,交席位定稿)
你要做的
读一下 §9 那一段的新措辞是不是你要的意思(尤其是「获批后认领席落地」这半句),然后在本 PR 上留一条 APPROVED review。批准之后由认领席翻 ready、入队落地,你不需要自己合并。
Premise readings
All four checked against the tree before anything was written; all four hold.
.claude/settings.jsonhasallow+ hooks but nopermissions.denypermissionskeys onorigin/mainefc1c9c=['allow']only; the port script refuses outright if adenykey is already thereorigin/mainbegan 「⛔ 绝不gh pr ready」 and ended 「这类 PR 停在 draft,等人类合并……也别去等一个不存在的 approval。」origin/maincarries the landed deny listgit log origin/main --oneline -20 | grep -c '(#18072)'= 1, at7ef05f997— so the final spelling exists and the card'sBlocked-by:is clearedAGENTS.mdin a different sectionAGENTS.mdhunk is@@ -107,6 +107,13 @@(§5 Coding Standards, adding a cite-by-content commandment), +7 −0. This branch's hunks are at 318 / 483 / 502 / 508 / 517, all in §9. Zero overlap; that section is untouched hereItem 1 — the deny list, verified
⛔ Never retyped. A script parses both JSON documents and moves the array object across; it refuses rather than writing a partial list. A round-trip check ran first, so the rewrite provably alters nothing but the inserted key:
The two
denyarrays are identical (14 = 14):The file parses, on both readers that matter:
allowandhooksare byte-identical toorigin/main:The diff is purely additive:
1 file changed, 16 insertions(+), with the only touched pre-existing byte being the]that becomes],whereallowcloses. Nothing else from objectstack's file came across — its hooks and its allow list stayed there.Fence 2 held — the four state-shaped tools are absent, with a positive control so the zeros are a reading rather than a hopeful grep:
Consistency edits within §9
Net 0 lines in
AGENTS.md—git diff --numstatreports5 5 AGENTS.md, and every hunk is@@ -N +N @@, i.e. one line replaced by one line at 318, 483, 502, 508 and 517. No other line in the file moves.人类合并appeared exactly 6 times in §9 before this change. Five are below. The sixth, on line 522, is deliberately left alone: it describes whatscripts/check-governed-queue-guard.mjsprints, not what the rule is — and the script does still print that remedy, as the--testoutput quoted at the top of this body shows.1. Line 508 — the hard rule (the equal-line replacement). Stated in objectstack
lanes/ui.md:25's own words; ⛔ not paraphrased.before
after
The bolded clause is
lanes/ui.md:25 verbatim. The four concrete prohibitions are kept and re-anchored to "before that approval", because they are the operational half a reader acts on; what leaves is the two now-false sentences — that a human's merge is the review record, and that there is no approval to wait for.2. Line 318 — the exception clause inside the 「CI 全绿即自行合并」 bullet. Only the parenthesised clause moves; the rest of that long bullet is untouched.
3. Line 483 — the subsection heading.
### ⛔ 受管面(governed surface):agent 起草,人类合并### ⛔ 受管面(governed surface):agent 起草,获授权批准后由认领席落地Checked before changing it: nothing in the tree links to this heading by anchor — the only occurrence of the heading text anywhere is the heading itself.
4. Line 502 — the
skills/**bullet.5. Line 517 — the mixed-diff bullet.
Untouched on purpose: the fifth prohibition (line 510) and the two verbatim maintainer quotes (512, 522). Verified mechanically —
第五条禁令andAn agent seat never submitsappear in the diff only as context lines, never on a+or-line.Gates
Derived from this repo's own
package.jsonscripts and the.github/workflows/*.ymlthat name.claude/orAGENTS.md, then widened by asking whichscripts/*.mjsactually read the changed paths — which is howcheck:shell-escape-residueandcheck:doc-linksgot onto the list; neither was named up front, and both readAGENTS.mddirectly.check:governed-queue-guardOK check-governed-queue-guard self-test: 185 cases passcheck:control-bytes✅ check-control-bytes: OK (scanned 7617 tracked text file(s); skipped 85 binary).check:skills-paths✅ check-skills-paths: OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined).check:shell-escape-residue✅ check-shell-escape-residue: OK (5/5 root(s) resolved -- AGENTS.md: 1 file(s), 15 fence(s); …)check:doc-linksLinks are valid across 17 scan roots.check:new-line-citationsVERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0check:changeset-claimscheck-changeset-presence✅ No source or published contract of a released package changed in this range, so no changeset is owed.130 passed·120 passed·50 passed·38 passed— 338 cases, 0 failedHook wiring was checked as well, since
settings.jsonis what names the hooks: all 4 commands it wires resolve to files on disk.lint and type-check — a declared, proven narrowing rather than a repo-wide run. Three readings, taken from the instruments rather than asserted:
filesglobs are**/*.{ts,tsx},scripts/**/*.{ts,tsx},**/examples/**/*.{ts,tsx},**/*.test.{ts,tsx},**/__tests__/**/*.{ts,tsx},packages/cli/src/**/*.{ts,tsx},packages/create-plugin/src/**/*.{ts,tsx},**/*.tsx,packages/types/src/objectql.ts,packages/core/src/registry/Registry.ts. There is no markdown or JSON block.--format json: eslint run over the three changed files reports 3 files, 0 errors.parserOptions.projectorprojectService— so this diff cannot move the verdict on any file it does not touch. Independently:git diff --name-onlyover this branch returns 0 files with a.ts/.tsx/.js/.mjs/.cjsextension. The same reading coverstype-check: tsc's inputs are unchanged, byte for byte.All readings above were taken on
660ab2c, the final commit of this branch.The repo-wide
pnpm lintandpnpm type-checkfarms remain CI's run, not this branch's.Acceptance notes
Observations from the surrounding text, ⛔ none of them acted on here — this is a governed rules-layer paragraph and a seat should not widen the edit on its own judgement.
scripts/check-governed-queue-guard.mjsstill prints the old landing shape. Its refusal text readsPark it as a DRAFT and leave the merge to the maintainer; a human merge IS the review record for a governed surface— visible verbatim in the--testoutput at the top of this body. After this pull request,AGENTS.mdstates ruling C while the script a seat is told to consult still narrates draft-and-human-merge. The card scopes to.claude/settings.json+AGENTS.md, so the script is out of scope here; it is reported to the PM to file. Worth noting the divergence is one-directional and harmless in the safe direction — the script under-permits rather than over-permits.check-changeset-presencesays on its own terms that no changeset is owed (nothing released moved). The empty-frontmatter declaration is written anyway, because this repo's rule asks for a declaration once rather than for a release, and because PR docs(agents): cite by content, not by line address (#8875 clause 1) #9301 — the nearest precedent, anAGENTS.md-only change — carries exactly that shape. ⛔ Noskip-changesetlabel is applied: in this repo that label is read by nothing and exempts nothing.AGENTS.md, verified above. Separately noted, not acted on: its hunk adds a Coding Standard numbered#10, and a commandment#10has since landed onorigin/mainfrom another card, so that branch has two#10s to reconcile when it mergesmain. That is that seat's PR to sort out; recorded here only because reading its diff was a required step and the collision was visible while doing so.Clause-②: noSession, as prose so it survives a body rewrite: this branch was produced in
session_01DAcomhvR9kKizeYgg89Vo8.Generated by Claude Code