Skip to content

Commit 0111212

Browse files
os-justinclaude
andauthored
docs(agents): the PR body is the dev's one body write — a later correction is the seat's PATCH, and the Clause-② line rides the create (#18659)
Fixes #18443 Clause-②: no Governed surface (`.claude/**`): draft, `skip-changeset`, the maintainer's approval lands it. File surface: `.claude/agents/os-dev.md` only. Head `1344eb515`, base `d6137fd6c`. Line-neutral: **403 / 403**. ## The gap, re-measured on `origin/main` `d6137fd6c` - L52 and L55 read exactly as the card quotes them (117 B and 118 B): the four-write budget, and `预算外零写,⛔ 不 PATCH 正文`. - How the body line is demanded today: `Check Changeset` (`pr-automation.yml` `changeset-check`) runs `scripts/check-changeset-no-major.mjs --base MERGE_BASE`, which reads the declaration from `github.event.pull_request.body` through `readClause2Line` (imported from `scripts/pm/check-clause2-carriers.mjs`; line-initial key, `yes` / `no` only). The red is CONDITIONAL, not unconditional as the card states: a missing line fails only as `not-measured-material` (exit 1) — a `patch` changeset on a package the diff grew, none of them raised to `minor`+; with no candidate offender it is `not-measured-moot` (exit 0), and the whole job is exempt under `skip-changeset`. The remedy when it does fail is still exactly one act, a body write, and the job is `edited`-subscribed so that write re-fires it without a push. - Whether the seat's committed orders now supply the line: **no.** On `d6137fd6c` the pm-dispatch template names the claim comment as the only carrier (`SKILL.md` :473 `Branch:`、`Clause-②:` 各占一行且行首, :476, :809 template), no committed text tells anyone to put it in the PR body, and `os-dev.md` did not contain the token `Clause` at all (grep: 0). The seat's own closure lives in its dispatch prompts (this card's order says put `Clause-②: no` in the PR body AT CREATION), not in the tree. So on `origin/main` the gap was still the whole class, not only the residual. - The residual class is measurable: three PR-body-scoped gates, all `edited`-subscribed, each cleared only by a body write — `Check Changeset` (the `Clause-②:` line), `Duplicate Fix Guard` (a `Fixes` line naming a card another open PR claims), `Part-of Closing-Keyword Guard` (a closing keyword beside `Part of`). Plus the two non-gate cases the card names: a seat's mid-task correction and a sanitizer-truncated body (platform-readings: the PR-body write side eats a trailing rule line plus footer while the call reports success; bare REST `PATCH /pulls` appends a bare footer beside an existing session-URL one, +58 B). - Provenance holds: #18203's report comment `5696114759` lists write (4) `PATCH /repos/.../pulls/18433` with the words the card quotes. ## Shape chosen: B, with the line carried at creation The card offers (A) an allowance for a declaration-repair `PATCH` (budget five, or the repair write uncounted) and (B) the remedy moved off the dev onto the seat. This PR proposes **B plus one prevention line**: the dev's PR body is written once, in the `POST /pulls` already in budget, and that one write carries the `Clause-②:` line copied from the claim comment; any correction the body needs afterwards is named in the report and written by the seat. The budget stays four; the dev never `PATCH`es a body. It is a variant of B, not a third shape — the rules decision (who writes the correction) is B's. Four-axis reasons (the framework as the dispatch pasted it): - **实际业务需求** — measured, not read-as-useful: the only body author at creation is the dev (`POST /pulls`), and the gate reads the body, so carrying the line at creation closes the case #18203 actually hit at zero extra writes. The residual (three gates + two non-gate cases) is a post-creation PR-state correction, and post-creation PR-state writes are already the seat's: the contract-review carrier moved to the seat on 2026-09-15 (`41ff021ca`), the ready flip and auto-merge were never the dev's. Under A the dev would pay a write per correction; under B the seat pays only in the residual class, in the review it already performs on the PR before hanging the four-piece. - **项目长远合理性** — A makes the budget a judgement (`repair` is a word every out-of-budget write will be argued under, round after round); B keeps it mechanical: four named endpoints, and a `PATCH` in `api_writes` is a refusal the seat can read without interpreting. B also keeps the write-identity design of #18072 intact (REST-only dev writes, a ledger the seat verifies) instead of carving an exception into it. - **防 AI 写错** — B gives the dev one branch fewer, not one more: write the body right once (a fixed line copied verbatim from the claim, so the two carriers agree by construction), never rewrite it. A would hand the dev the PR-body `PATCH` traps the platform-readings measure (the appended footer, the eaten rule line, the ⛔ never re-send a body carrying an appended footer rule) on the rare path — precisely where an AI is least rehearsed. Contract-first: fix at the producer (the body as created), not by tolerating a wrong body downstream. - **创业阶段不扩散需求** — B adds no capability, script or gate and widens nothing: it reallocates one existing seat write (`PATCH .../issues/{n}` body, ✓ in `rest-channel.md`) and deletes two lines of duplicated rule text. A widens the dev's write surface for a case prevention removes. No staged transition: the old sentence is gone, the new one is in force on landing. Cost stated plainly: under B a correction in the residual class still costs a seat round (dev reports, seat writes) — the same cost as today, minus the conflict the dev had to flag. 「flag it rather than bury it」 is kept as an explicit rule for every other out-of-budget write (the rewritten L57). ## The diff — before / after with byte counts Rule 3 block (indented list under 六条基本规则 3): - L55 before (118 B): ` - 三类发现附查重词进报告交席位代立,dev 不 \`POST /issues\`;预算外零写,⛔ 不 \`PATCH\` 正文。` - L55 after (117 B): ` - 三类发现附查重词进报告交席位代立,dev 不 \`POST /issues\`、⛔ 不静默弃报;预算外零写。` - L56 new (117 B): ` - PR 正文 dev 只写一次,在开 PR 那一笔,⛔ 不 \`PATCH\`;事后要改的报告点名改法,席位代写。` - L58 (was L57) before (118 B): ` - 报告记 \`api_writes\`(次数 + 端点清单)与 \`mcp_calls\`(MCP GitHub 调用计数),席位对照预算核验。` - L58 after (119 B): ` - 报告记 \`api_writes\`(次数 + 端点清单)与 \`mcp_calls\`;越界真写了的照列注明缘由,⛔ 不漏记。` - old L58 deleted (112 B): ` - 立不成 ⇒ 发现连同缘由写进报告交 PM 代立;⛔ 不查重硬立与静默弃报同为禁形。` - L52 unchanged (117 B): the four-write budget stands as written. Definition of done block: - L279 new (113 B), directly under the `Draft PR 指向 main` line: `- 正文行首照抄认领的 \`Clause-②:\` 行:\`Check Changeset\` 读正文不读卡,开 PR 那一笔就带上。` 字节与 sanitizer 纪律 block: - old L402 deleted (102 B): `- 评论通路 MCP 与 REST 同判:整块原样存活,缺规则线则不认、再落整块留两个。` Whole file: 36842 B → 36858 B (+16 B), 403 → 403 lines, widest line 120 B before and after, zero lines over the ratchet's 120-byte cap. Why the two deletions are content deletions, not re-wrap: old L58's conditional (`立不成 ⇒ …交 PM 代立`) has been dead since the dev stopped filing (L55: `dev 不 POST /issues`, findings go to the seat unconditionally), and `不查重硬立` guards an act the dev no longer performs; its one live clause — never drop a finding silently — is folded into L55 as `⛔ 不静默弃报`. Old L402 restated AGENTS.md's issue-comment footer paragraph (「a bare footer UNDER the rule line is stored byte-identical, one footer; … NO rule line above it … leaving two」) which this file already routes the reader to twice (the `归属条款是 AGENTS.md 的 GitHub mutates body BYTES 条` line and `完整读数住 AGENTS.md 同条`), and AGENTS.md binds the dev by the file's own opening. The `MCP GitHub 调用计数` parenthetical dropped from L58 is restated by the report template's own `mcp_calls` field description eight lines below the JSON fence. Rider not taken: the pending `tail --pid` rider (the line names no pid source) sits in the 干净收尾 block, not in either block this PR touches, so it does not fit line-neutrally here and is left for the next os-dev.md PR. ## Gates Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the worktree at `1344eb515` (change set: 1 path vs merge base `d6137fd6c`; the tool flagged STALE TREE — `origin/main` moved one commit to `62d830e54`, `scripts/check-plugin-teardown-shape.mjs`, which is not among the 18 families and does not touch this file). Every exit code captured redirect-then-`$?`: - `node scripts/check-closing-keyword-parity.mjs` :: exit 0 - `node scripts/check-closing-keyword-parity.mjs --self-test` :: exit 0 - `node scripts/check-comment-mask-corpus.mjs` :: exit 0 - `node scripts/pm/check-governed-queue-guard.mjs --self-test` :: exit 0 - `node scripts/pm/check-harness-current.mjs --self-test` :: exit 0 - `pnpm --filter @objectstack/lint run check:doc-formula-expressions` :: first run exit 3 PREREQUISITE NOT MET (no `dist/` for formula + lint), prerequisite built under `os-verify-lock.sh` (VERDICT command-exit 0, held 1s, waited 0s, turbo cache hit), re-run :: exit 0 (`22 record-scoped formula example(s) across 438 files / 1378 TS blocks judged clean`) - `pnpm check:agent-model-declared` :: exit 0 - `pnpm check:agent-test-spelling` :: exit 0 - `pnpm check:commit-card-trailers` :: exit 0 - `pnpm check:doc-authoring` :: exit 0 - `pnpm check:driver-memory-census` :: exit 0 - `pnpm check:nul-bytes` :: exit 0 - `pnpm check:pm-governed-merges` :: exit 0 - `pnpm check:pm-skill-id-lint` :: exit 0 - `pnpm check:pm-skill-ratchet` :: exit 0 — `.claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0)`, same reading on the unedited tree - `pnpm check:refd-timer-probe` :: exit 0 - `pnpm check:skill-frame-sync` :: exit 0 - `pnpm check:watch-hint-literal` :: exit 0 - extra, the roster the derivation marks ⛔ under `.claude`: `pnpm check:pm-settings-deny-roster` :: exit 0 - `node scripts/pm/check-governed-merges.mjs --test .claude/agents/os-dev.md` :: exit 3 GOVERNED (`.claude/** ×1 — the agent instruction tree`), recorded as the expected answer, not a failure `--ran` reconciliation: `18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3)`. Pin checks: no script or doc outside this file pins any string rewritten or deleted here (grep over `scripts/`, `.claude/`, `AGENTS.md`, `docs/`: zero hits for each). `skip-changeset`: `.claude/**` is on the fast lane (nothing published moves); `check-governed-merges --test` answered GOVERNED as above. ## 维护者速读(草稿) **改了什么** — 只动 `.claude/agents/os-dev.md`,行数不变(403 / 403)。dev 的 GitHub 写预算仍是四笔;新增两条规则:① PR 正文 dev 只在开 PR 那一笔写,⛔ 不 `PATCH`,事后要改的写进报告、由席位代写;② 开 PR 时把认领评论里的 `Clause-②:` 行照抄进正文行首,让 `Check Changeset` 一开就读得到。另把「越界真写了的照列注明缘由,⛔ 不漏记」写成明文。两行新增由两行删除付账:一条自 dev 不再立卡起就死掉的「立不成 ⇒ …」句(仅存的一句「不静默弃报」并入原句),一条与 AGENTS.md 逐字重复、本文件已两次指回 AGENTS.md 的评论页脚读数。 **为什么改** — 卡面测到的矛盾:`Check Changeset` 判红时唯一的补救是改 PR 正文,而 L55 明禁 dev `PATCH` 正文;dev 只能报冲突、烧一轮往返。复测发现根因更靠前:树里没有任何一处告诉 dev 把 `Clause-②:` 行放进 PR 正文(派发模板只写认领评论),所以线是在建 PR 时就漏掉的。把它放回建 PR 那一笔,常规情形零额外写;剩下的残余类(三个读正文的门禁、席位中途改令、被截正文)本来就是建 PR 之后的 PR 状态修正,归已经持有这类写(contract-review 标签、ready 翻转、auto-merge)的席位。没选「预算抬到五」:那会把预算从机械清单变成每轮都要争辩的「算不算修复」。 **风险与代价(含回滚)** — 代价:残余类里的一次正文修正仍要一轮席位动作(dev 报、席位写),与今天相同,只是少了 dev 报冲突那一步。风险:席位写正文要走 REST `PATCH .../issues/{n}`,平台对 PR 正文的页脚/横线变异读数已在 platform-readings,席位照读回即可;dev 侧少一个分支,不多。两处删除若被判为内容损失,可单独恢复(各一行、字节已列)。回滚:revert 本 PR 即回到 L52/L55 原文,无其它文件牵连、无脚本、无 changeset。 **席位意见** — (留席位定稿) **你要做的** — 批准或退回本 PR:批准即接受「正文修正归席位、dev 零 `PATCH`」这一取舍;退回并注明「抬预算」即改走卡面的 A 形。 ## Acceptance notes - noted, not filed: the committed pm-dispatch template still names the claim comment as the only `Clause-②:` carrier; after this PR the dev copies that line into the body, so the template needs no change to close the case. 承接者:the `domain:skills` seat, on the next pm-dispatch SKILL.md PR, if it wants the body carriage stated on both sides. - noted, not filed: the card's sentence 「`Check Changeset` reds a PR whose body carries no line-start `Clause-②:` declaration」 is conditional in the gate (`not-measured-material` only); the remedy statement stands regardless. 承接者:无 — recorded here and in the report. - noted, not filed: the `tail --pid` rider is left in place (different block, see above). 承接者:the next os-dev.md PR. - The dispatch-gates STALE TREE flag: `origin/main` advanced by `62d830e54` during this round; re-derivation after a fetch names the same 18 families (the changed file is not one of them). 承接者:无. --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ Co-authored-by: os-justin <noreply@anthropic.com>
1 parent 9cbd227 commit 0111212

1 file changed

Lines changed: 4 additions & 4 deletions

File tree

‎.claude/agents/os-dev.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ model: opus
5252
- 写预算四笔:`git push`、一次 `POST /pulls`(draft)、`POST /issues/{n}/labels`、`os-dev-report` 评论。
5353
- ⛔ 不用 MCP GitHub 写工具;令牌按会话定:installation ⇒ `claude[bot]`,user-to-server ⇒ 用户。
5454
- 卡与线程只走 payload 档(公开仓单卡网页内嵌 JSON,拼写住 platform-readings)或单卡 REST 读。
55-
- 三类发现附查重词进报告交席位代立,dev 不 `POST /issues`;预算外零写,⛔ 不 `PATCH` 正文。
55+
- 三类发现附查重词进报告交席位代立,dev 不 `POST /issues`、⛔ 不静默弃报;预算外零写。
56+
- PR 正文 dev 只写一次,在开 PR 那一笔,⛔ 不 `PATCH`;事后要改的报告点名改法,席位代写。
5657
- 通道对照表见 `.claude/skills/pm-dispatch/references/rest-channel.md`,其 ✓ 按座位实测。
57-
- 报告记 `api_writes`(次数 + 端点清单)与 `mcp_calls`(MCP GitHub 调用计数),席位对照预算核验。
58-
- 立不成 ⇒ 发现连同缘由写进报告交 PM 代立;⛔ 不查重硬立与静默弃报同为禁形。
58+
- 报告记 `api_writes`(次数 + 端点清单)与 `mcp_calls`;越界真写了的照列注明缘由,⛔ 不漏记。
5959
- PM 的去重读数随派发词下发,当既有事实用,只复核其后增量,⛔ 不重跑。
6060
- 归挂不散落:落在已排队 issue 完成范围内的发现,立成它的 sub-issue(自动进派发池)。
6161
- 只是依赖它的,独立立单带一行 `Blocked-by:`;立在修复落地的仓,带回链。
@@ -275,6 +275,7 @@ model: opus
275275
- 分区 pin `test/vitest-tiers-partition.test.ts` 在某个测试文件两层皆无或皆有时变红。
276276
- 用 `git push -u origin claude/issue-<n>-<slug>` 推上去,网络失败退避重试;pre-push 拒卡片 trailer。
277277
- Draft PR 指向 `main`,正文首行 `Fixes #<n>`;合并不应关卡时用 `Part of #<n>`,并说明留下哪一半。
278+
- 正文行首照抄认领的 `Clause-②:` 行:`Check Changeset` 读正文不读卡,开 PR 那一笔就带上。
278279
- ⛔ 永不 `Fixes` 一张还在决策箱的卡:合并会静默关掉它,而收件箱过滤只读 open。
279280
- ⛔ 不写否定式的关单句,它照样关掉点名的卡:解析器无视否定,只匹配关键词 + `#<n>`。
280281
- 关键词是 `fix/fixes/fixed/close/closes/closed` 与 `resolve/resolves/resolved`;让它们远离其它卡号。
@@ -399,5 +400,4 @@ _Generated by [Claude Code](https://claude.ai/code/session_<id>)_ ← session-
399400
```
400401

401402
- 页脚形态随通道(MCP / REST)与动作(建 / 改)变,⛔ 不由一条推其余;发你要存的,写后必回读。
402-
- 评论通路 MCP 与 REST 同判:整块原样存活,缺规则线则不认、再落整块留两个。
403403
- 耐久归属写进正文散文或评论,⛔ 不循环重贴页脚;完整读数住 AGENTS.md 同条。

0 commit comments

Comments
 (0)