Repository navigation
docs(agents): state the attribution-footer append as conditional on absence - #10212
Conversation
…bsence Clause 3 of the GitHub body-mutation list declared the footer append unconditional on PATCH and scoped it to PATCH. Both halves are measured false: the platform appends exactly one bare footer when the sent body does not already end with one, appends nothing when it does, and the same rule holds on comment POST and issue-body PATCH, not only PATCH. The old wording pushed the dangerous direction of the error: a seat that believes the append is unconditional strips its own footer to avoid a duplicate and then ships a body with none. Two sub-clauses carry what the rewrite must not overclaim: which half of the rule was measured on which channel (and the one cell still empty), and that a zero byte delta is not byte identity on the comment endpoint, whose separator normalisation is a net-zero rewrite. The predicate for what the platform recognises as an existing footer stays declared open. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
|
changeset-claim-re-read
|
Contract reviewServed-tier: ① Derived judgmentsRendered in-seat by the skills seat 1 session at the served tier the maintainer named on this date (the fable tier word; ruling record objectstack#7623 comment 5771798588). The contract is the card's measured claim: the attribution-footer append is conditional on absence, not unconditional and not
② Semver levelNone — a governed instruction file; no package source, no changeset owed ( ③ Boundary flags
Implemented-by: VERDICT: PASS 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>
…0213) Fixes objectstack-ai#9992 ## 维护者速读(草稿) **改了什么** `AGENTS.md` 只改一行(第 326 行,「多 agent 协作纪律」节里那条「CI 全绿即自行合并」)。原来它只留了**一条**例外(diff 命中受管面);现在留**两条**,新增的第二条是:**由一次派发跑出来的 PR**(卡上挂着 `Claim:` 评论、由被派发的 `os-dev` 一类 agent 实施的那种)**不论受管面判定如何都停在 draft**,等 CI 收敛、翻 ready、入队、事后重挂 auto-merge 全是派发席的动作。同时把「没有派发、卡上没有 `Claim:` 的自发改动仍按本条自行入队」写明,免得这条例外被读成把自主改动也一并冻住。净增 0 行,文件仍是 589 行。 **为什么改** 同一个 agent 在两份规程里收到相反的指令:objectui 的 `AGENTS.md` 让绿了的非受管 PR 作者自己落地,而每个 os-dev 运行都读的派发契约(兄弟仓 `.claude/agents/os-dev.md`)说它的终态交付物就是「draft PR + 报告」、等 CI 不是它的事。受管面守卫解不开这个结:它答 NOT GOVERNED 只解除**批准**要求,而派发契约管的是**谁在动手**,不是**改了哪些文件**。这不是假想 —— objectui#9475 → PR objectstack-ai#9962 上一个被派发的 dev 已经在这里停住并把它当治理问题上报。派发循环的冲突规则是「两条细则冲突 ⇒ 按更严的一条行动并立卡;⛔ 不当场改文本了结」,更严的一条是派发契约(停在 draft),所以移动的是 `AGENTS.md` 这句自行合并。 **风险与代价(含回滚)** 代价:被派发的 PR 从此多等一个人,落地延迟由派发席的巡检节奏决定。风险面很小 —— 这条规则没有任何机械执行者(受管面守卫不读 `Claim:`),它的全部效力在于读到的人照做;所以写错的后果是「有人照旧自己合并」,不是构建红。回滚就是 `git revert` 这一个 commit,文件回到原来的一行,不触及任何脚本、工作流或标签。另一个方向的代价请一并权衡:如果维护者更想要**方案 2**(改派发契约,让 dev 在非受管 diff 上自行落地),那就得把 PM 循环的复核步骤一起重写,因为复核人现在就是落地人 —— 本 PR ⛔ 没有走这条路,原卡把它记为「会删掉循环赖以存在的复核步骤」。 **席位意见** —— 技能席 1(`session_01Wnstp2kTth7sGXfr8fXypc`)已按 GitHub 与拉取的 head `46680d1` 复核:只改 :326 一行,方向按卡引的冲突规则「按更严的一条」取 os-dev 契约(dev 停在 draft、派发席落地),例外 ② 以「谁在动手」为键、不被受管面判定解除,无派发的自发改动仍按原句自行入队;跨仓契约按内容锚引用而非 `path:line`(本仓禁令,引用门禁读 0 新增)。objectui 自带的五个门禁本席复跑均 exit 0,`check-governed-queue-guard --test AGENTS.md` 读 GOVERNED。复核记录 PASS 5773828944(席内 fable)。与 PR objectstack-ai#10212 同文件不同区域,后落者合并。建议合并;「受管面」一节尚未回指例外 ②,留给下一次动该节的 PR。 **你要做的** 读上面「两条条款」一节的两段原文,确认第二条例外的措辞是你要的治理语义;认可就合并这个 PR(本仓 `AGENTS.md` 是受管面,本 PR 停在 draft,由你落地)。如果你更倾向方案 2,请直接说,这个 PR 撤掉、改去兄弟仓动派发契约。 ## 两条条款,原文与出处 ### A —— objectui `AGENTS.md` 第 326 行(改动前,`0cf2d66`) > - **CI 全绿即自行合并,不必等维护者确认**(授权语义没变,变的只是动作;⛔ **例外:diff 命中受管面的 PR 不适用本条** —— 见下方「受管面」,那类 PR 停在 draft 等一条获授权的批准,再由认领席落地)—— 修改完成后**只提交你任务改动的文件**(逐路径 `git add PATH`,绝不 `git add -A` 扫入无关 diff),开 **draft** PR;等远端 CI 全绿后: 紧随其后的代码块(第 328–332 行)就是那两行入队命令:`gh pr ready` 与 `gh pr merge --squash --auto --delete-branch`。⚠️ 行号漂移,已复核:原卡把这条记在 `:330`(树在 `c255b38`,当时 `AGENTS.md` 593 行)。在本 PR 的基点 `0cf2d66` 上文件是 **589 行**,同一条款在 **第 326 行**。原卡的 `:330` 在今天的树上指向的是那两行入队命令里的 `gh pr merge` 那一行。 ### B —— objectstack `.claude/agents/os-dev.md`(只读,⛔ 本 PR 不改) 在 `372931e` 上,`os-dev.md` 共 403 行。 第 16 行: > 交付物两件:该 issue 实现完毕并推成 draft PR,加下方的 JSON 报告。 第 311 行: > 报告在本地验证走完时交付,CI 收敛等待归 PM 不归你:⛔ 不为等 CI 结论推迟报告。 第 312 行(同族,一并引以说明「等 CI」到底归谁): > 门禁状态如实记录,`in_progress` 是诚实值;PR 开出后 ⛔ 永不 sleep、定时等待或空转轮询 CI。⚠️ 行号漂移,已复核:原卡把第二条记在 `:310`。在 `372931e` 上 `:310` 是那条 objectui `skip-changeset` 的规则,被引的句子在 **第 311 行**,差一行。 ### 它们为什么真的相撞 A 只挖了一个洞:`diff 命中受管面`。一个被派发的 os-dev 在**非受管**文件面上开的 PR 因此整个落在 A 的适用范围里 —— A 让它在 CI 绿后翻 ready 并挂 auto-merge,而 B 告诉同一个 agent 等 CI 根本不是它的活、它的终态交付物是 draft PR 加一份报告。受管面守卫解不开:`node scripts/check-governed-queue-guard.mjs --test` 答 NOT GOVERNED 解除的是 A 自己那条例外要的**批准**,解除不了 B,因为 B 问的是**谁在动手**,A 问的是**改了哪些文件**。 ## 改了哪一行 - 文件:`AGENTS.md`;改动行:**第 326 行**(改动前后都是这一行);净增 **0** 行(589 → 589);`git diff --numstat` 读数 `1 1 AGENTS.md`。 - 改动前(原文见上面 A 节的引文)。 - 改动后: > - **CI 全绿即自行合并,不必等维护者确认**(授权语义没变,变的只是动作;⛔ **两条例外,命中任一就只走到 draft 为止 —— 下面那两行入队命令都不归你跑**:① **diff 命中受管面的 PR** —— 见下方「受管面」,那类 PR 停在 draft 等一条获授权的批准,再由认领席落地;② **一次派发跑出来的 PR** —— 卡上挂着 `Claim:` 评论、由被派发的 `os-dev` 一类 agent 实施的那种,**不论受管面判定如何**都停在 draft:被派发 dev 的终态交付物就是「一个 draft PR + 一份报告」,等 CI 收敛、翻 ready、入队、以及事后重挂 auto-merge 全都是**派发席**的动作 —— 因为 PM loop 的复核步骤要求落地的是**第二个 actor**;兄弟仓的派发契约 `../objectstack/.claude/agents/os-dev.md` 也正是这么写的,逐字照录:「交付物两件:该 issue 实现完毕并推成 draft PR,加下方的 JSON 报告。」「报告在本地验证走完时交付,CI 收敛等待归 PM 不归你」。⚠️ ② **不是** ① 的推论,也不被受管面判定解除:`node scripts/check-governed-queue-guard.mjs --test` 答 NOT GOVERNED 只解除 ① 要的那条**批准** —— ① 问的是**改了哪些文件**,② 问的是**谁在动手**。没有派发、卡上没有 `Claim:` 的自发改动不在 ② 内,仍按本条自行入队)—— 修改完成后**只提交你任务改动的文件**(逐路径 `git add PATH`,绝不 `git add -A` 扫入无关 diff),开 **draft** PR;等远端 CI 全绿后: 两处刻意的写法,请一并复核: 1. **引兄弟仓那份契约用的是内容锚,不是行号。** 本仓对跨文件 `路径:行号` 引用的约定(`AGENTS.md` 的「相信任何『不存在』」一族与 `scripts/check-new-cross-file-line-citations.mjs`)把跨文件行址列为禁写形态,所以落进 `AGENTS.md` 的是两句原话,行号只写在本 PR 正文里(正文不在那道门的受检总体里)。门禁读数:本分支新增跨文件行址 **0** 条。 2. **例外 ② 的判据是「谁在动手」,不是路径。** 措辞刻意不去引用任何脚本的判定,因为没有脚本读得出 `Claim:` —— 这条的执行力就在于读到的人照做,和本仓受管面那节自己承认的情形同类。 ## 门禁(本地,全部在最终 commit `46680d1` 上重跑,退出码在任何管道之前捕获) | 门禁 | 命令 | 退出码 | 判定行 | | --- | --- | --- | --- | | Control Bytes | `node scripts/check-control-bytes.mjs` | 0 | `check-control-bytes: OK (scanned 8216 tracked text file(s); skipped 85 binary).` | | Changeset Declaration | `node scripts/check-changeset-presence.mjs` | 0 | `No source or published contract of a released package changed in this range, so no changeset is owed.` | | Cross-file line citations | `node scripts/check-new-cross-file-line-citations.mjs` | 0 | `VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0`(6/6 synthetic controls PASS) | | Doc links | `node scripts/check-doc-links.mjs` | 0 | `Links are valid across 17 scan roots.` | | Shell escape residue | `node scripts/check-shell-escape-residue.mjs` | 0 | `check-shell-escape-residue: OK (5/5 root(s) resolved -- AGENTS.md: 1 file(s), 15 fence(s); ...)` | | Governed Surface Queue Guard | `node scripts/check-governed-queue-guard.mjs --test AGENTS.md` | 3 | `⛔ GOVERNED — 1 of 1 path(s) are on a governed surface` —— 这是**期望值**,不是失败:它就是本 PR 停在 draft、由维护者落地的依据。 | **本地收窄,已申报并测量。** 这是一个只改 markdown 的 diff。`ci.yml` 的 `Decide whether this change needs a full run` 步骤把 `:(exclude,glob)**/*.md` 列在排除集里(`lint.yml` 有一个孪生步骤),所以在这样的 PR 上两条工作流都会启动、报告、并跳过每一个昂贵步骤;`pnpm lint` / `pnpm type-check` / `pnpm test` 的受检总体里没有任何一个被本 diff 触动的文件(改动的是 `AGENTS.md`,不是任何包的源码,changeset presence 门禁独立测到「0 个已发布包的源码或已发布契约发生变化」)。上表是本地欠的那一半,全农场归 CI。 **changeset:零写,按门禁读数而非标签。** `check-changeset-presence.mjs` 自己印的判定是 nothing owed,所以既不加空 frontmatter changeset、也不碰 `skip-changeset` 标签(本仓该标签零读者)。本仓路径标签归 `labeler.yml`,本 PR ⛔ 未写任何标签。 ## 并发与后来的落地人 本分支从 `0cf2d66`(= 认领时的 `origin/main`)切出,开 PR 前重新 fetch,`origin/main` 未动,无需 merge。触到的行只有 **`AGENTS.md:326`** 这一行。objectui#9563 在同一批认领里改的是 `AGENTS.md` 的 GitHub 正文改写条款(`:470`–`:480` 一带),**区域不同**;两者落地顺序任意,后落地的那个合一次即可。认领时读过的 5 个 open PR 的文件清单里没有一个触到 `AGENTS.md`。 ## Acceptance notes - 本仓 `AGENTS.md` 里**不存在**「人类贡献者自己的 PR」这一单独情形 —— 全文的收件人是 agent(`grep -n '人类贡献者|human contributor|非 agent'` 零命中)。所以派发词里「若 AGENTS.md 也处理人类贡献者的 PR 就保留该情形」这一条**不适用**;取而代之,被保留并写明的是「没有派发、没有 `Claim:` 的自发改动」这一残余情形。 - 第 335 行那条「auto-merge 会在合并冲突和 draft 窗口里被静默丢弃 —— 事后必须复查并重挂」**未改**,也不需要改:它的收件人是**挂上 auto-merge 的那个人**,在例外 ② 之下那就是派发席,而新措辞已经把「事后重挂 auto-merge」明确划给派发席。 - 第 494 行那节「受管面」**未改**(认领申报的文件面只含这条落地流程条款)。若维护者希望那一节也回指例外 ②,那是另一个 PR 的事;⛔ 本 PR 不扩面。 --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #9563
维护者速读(草稿)
改了什么 ——
AGENTS.md里「GitHub 会改写你写进 issue/PR 正文的字节」那份清单的第 ③ 条,从「PATCH无条件追加第二个 footer」改写成实测到的规则:缺了才补、只补一个 bare footer、评论POST/ 评论PATCH/ issue 正文PATCH三条通道同一条规则。原来的「恰好 58 字节、只加不减」那半句证据原样保留。只动这一条,①②④⑤⑥ 一个字没动。净 +2 行(1 行改写 + 2 条子条款),全仓没有第二处引用这句话。为什么改 —— 这句话被实测证伪了,而且错在更危险的那个方向。它同时错两处:条件性说反了(实测是缺了才补,不是无条件),范围也窄了(不是
PATCH独有)。信它的席位为了躲开「第二个 footer」,会先把自己的 footer 剥掉再发,于是发出去、也存下来一条一个 footer 都没有的正文 —— 这正是卡面点名的危害。写给 agent 读的规程说错一句,下一轮每个席位都照着错的做,所以这类缺陷按产品缺陷处理。风险与代价(含回滚) —— 纯文档改动,零运行时影响,没有任何门禁读
AGENTS.md的正文(它的读者是 agent,不是机器)。代价是这三行比原来的一行长:新文本额外写明了「哪一半在哪条通道上测过」和「仍空的那一格」。这是有意付的代价 —— 上一版正是因为把一次局部读数写成了普遍规则才出错,不标注测量边界就等于邀请下一次同样的过度概括。回滚 =git revert这一个 commit,或直接关掉本 PR(尚未落地,零成本)。席位意见 —— 技能席 1(
session_01Wnstp2kTth7sGXfr8fXypc)已按 GitHub 与拉取的 head074b86d复核:只改第 ③ 条并加两条子项,①②④⑤⑥ 与两条建议行逐字节未动;改写后的规则与卡上四次写入、附录六次预登记写入、本席本班三次 PR 正文 PATCH(无 footer 发出各 +58)以及 dev 本轮两次预登记读数(已带 footer 发出 ⇒ 不追加)全部一致;子项如实标出未测的那一格与「+0 ≠ 逐字节相同」。objectui 自带的五个门禁本席在独立 worktree 复跑均 exit 0,check-governed-queue-guard --test AGENTS.md读 GOVERNED(Tier H 依据)。复核记录 PASS 5773828366(席内 fable)。dev 的两个开放问题本席已定:POST /pulls那条读数留在报告里不写进条文;空格子只在席位本就欠一次正文 PATCH 时顺便填、先登记预期。建议合并;姊妹卡 #9908 的 POST 一半由同一句解答,落地后按前提证伪关卡。你要做的 —— 读一遍新的 ③ 和它下面那两条子条款,确认措辞;⚠️ 姊妹卡 objectui#9908 是同一条 ③ 的
AGENTS.md是受管面,本 PR 停在 draft 等你合并,席位 ⛔ 不翻 ready、⛔ 不入队、⛔ 不自己批准(seat-sidecheck-governed-queue-guard.mjs --test AGENTS.md退出 3 = GOVERNED,已实跑确认)。POST那一半:本 PR 的新句子已经把POST的行为写进正文了,所以 #9908 的前提由这同一段文字回答 —— 本 PR ⛔ 不认领、⛔ 不关闭它,那张卡另行处置。改动
一个文件,一处 hunk:
AGENTS.md,「GitHub 会改写你写进 issue/PR 正文的字节」小节的第 ③ 条(该小节六条清单的第三条)。+3 −1。改之前(逐字)
改之后 —— 主条款把规则改成「缺了才补 / 已有则不补」并点名三条通道,保留 +58 与「只加不减」的证据,末尾加上 ⛔ 永不剥自己 footer 的禁令;两条新子条款分别承担两件不能省的事:① 哪一半在哪条通道上被测过、以及仍空的那一格(issue / PR 正文
PATCH送一条已带 footer 的正文);②+0不等于逐字节相同(评论POST会改写分隔符:净零字节、53 个字符位不同),以及「平台认得出一个 footer」这个判据本身没被测过。逐字全文见本 PR 的 diff,报告 JSON 里也带一份。证据(全部来自卡面与其 addendum,本 PR 不新增合成探针)
POSTPATCHPATCHPOST---之前插入一个换行⇒ 「无条件」被行 2 与 addendum 那一行直接证伪;「⚠️ 仍空的那一格已在新文本里具名:没有人在受控条件下对 issue / PR 正文
PATCH独有」被三条POST读数证伪。PATCH送过一条已以 footer 结尾的正文。门禁(markdown-only diff;退出码在任何管道之前捕获)
node scripts/check-control-bytes.mjscheck-control-bytes: OK (scanned 8216 tracked text file(s); skipped 85 binary).node scripts/check-changeset-presence.mjsNo source or published contract of a released package changed in this range, so no changeset is owed.node scripts/check-new-cross-file-line-citations.mjsVERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0node scripts/check-doc-links.mjsLinks are valid across 17 scan roots.node scripts/check-governed-queue-guard.mjs --self-testOK check-governed-queue-guard self-test: 185 cases passnode scripts/check-governed-queue-guard.mjs --test AGENTS.mdGOVERNED — 1 of 1 path(s) are on a governed surface—— 这是预期答案,不是红:它就是本 PR 停 draft 的依据changeset:零。判据不是标签,是门禁自己:
check-changeset-presence.mjs印出「1 file(s) changed, 0 of them published source of a package the release covers」后退出 0。本仓AGENTS.md的规则也是这么写的 —— 只有发版包的已发布可执行源码或八个发布契约字段动了才欠 changeset。⇒ ⛔ 不加空 frontmatter changeset,那会是一次对着空气的声明;本仓skip-changeset标签零读者,⛔ 也不打。不适用的:
lint.yml对**/*.md走paths-ignore,markdown-only 的 diff 在它那里整个跳过(job 内还有一个同表的开关);本仓scripts/pm/下只有board-snapshot.mjs与check-half-states.mjs,没有dispatch-gates.mjs,所以门禁族是从本仓package.json与.github/workflows/手工推导的,推导过程见上表与报告。并发
分支切自
origin/main0cf2d66,收尾前重新git fetch origin main:仍是0cf2d66,且git merge-base --is-ancestor origin/main HEAD退出 0 ⇒ 无需合并,本分支已经在最新的main之上。AGENTS.md里那份六条改写清单的第 ③ 条。objectui#9992 改的是同一文件的 landing-flow 条款 —— 不同区段,两处 hunk 不重叠,后落地的一方合一次即可。预登记的读数(发送之前写下,⛔ 不为测量而多写一笔)
本 PR 之后只剩一笔写入:objectui#9563 上那条
os-dev-report评论,走POST /repos/objectstack-ai/objectui/issues/9563/comments,发出的正文以 bare footer 结尾。同一笔登记也覆盖创建本 PR 的
POST /repos/objectstack-ai/objectui/pulls:发出的正文以 session-URL 形式的 footer 结尾,按 ⑤ 预期原样保留或被归一化,按同一把尺回读。两个读数都进报告的h2_reading,实际观察到什么就写什么。Acceptance notes(⛔ 不在本 PR 修,也未立卡)
grep全文footer:只在 ② ③ ④ ⑤ 与两条建议行上命中,不存在这样的指令。剥 footer 的危害是读者自己推出来的,不是被明文教的 ⇒ 无依赖条款需要连带修,建议行未动。PATCH把 session-URL 形式的 footer 降级为 bare(一个 footer);而卡面转述的二手读数说 session-URL 形式被当成没有 footer、于是触发追加(两个 footer)。两者可能是同一现象的两种读法(只看尾部就会把「追加了一个 bare」读成「降级成了 bare」)。⛔ 本席没有自己的读数,⛔ 不在本卡权限内改 ②;新 ③ 的子条款已把这个判据标成未测,危害因此被围住。要坐实它,需要一次受控写入:发一条以 session-URL footer 结尾的正文,按首个不同字节的下标回读。pnpm install未在本 worktree 跑(已申报的收窄):适用的六条门禁全是零依赖的 Node 脚本,已从裸 worktree 实跑出真实判定;为一个单文件 markdown diff 装整个 monorepo 会挤占并行席位共用的容器。Generated by Claude Code