Repository navigation
Commit 0111212
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
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
55 | | - | |
| 55 | + | |
| 56 | + | |
56 | 57 | | |
57 | | - | |
58 | | - | |
| 58 | + | |
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| |||
275 | 275 | | |
276 | 276 | | |
277 | 277 | | |
| 278 | + | |
278 | 279 | | |
279 | 280 | | |
280 | 281 | | |
| |||
399 | 400 | | |
400 | 401 | | |
401 | 402 | | |
402 | | - | |
403 | 403 | | |
0 commit comments