From 5340f1a015b3826b15a6730d05f2576b993cc524 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 8 Aug 2026 12:29:58 +0000 Subject: [PATCH] docs(agents): shared-identity caution + session-URL PR footer form (#6567) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 AGENTS.md 的多 agent 纪律区与 `.claude/agents/os-dev.md` 里各补两件事: 不是自己写下的 PR 状态属于另一个 actor,问 PM,不要单方面「纠正」;以及把 署名 footer 写成 session URL 形式(裸 URL 形式在每次 `update_pull_request` 编辑时被整段剥掉,建单路径则把它改写成 session 形式)。 证据是 #6567 记录的事故:一个 dev 读回自己的 PR 正文,看到没写过的 footer 形式,正确地推断「有东西在改我的 PR」,却把这个结论错误地延伸到 draft 标志, 把 PM 已翻 ready 的 PR 改回 draft —— auto-merge 与合并队列成员资格一并静默 销毁,而 `pull_request_read` 两者都不暴露。 ⛔ 哪一层做的改写(平台 sanitizer / MCP 工具层 / workflow)按分诊裁定留空, 本次不去确定,指引不依赖这个答案。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn --- .claude/agents/os-dev.md | 38 +++++++++++++++++++++++++++++++++++++- AGENTS.md | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/.claude/agents/os-dev.md b/.claude/agents/os-dev.md index 9aa652d8d5..9f31d43235 100644 --- a/.claude/agents/os-dev.md +++ b/.claude/agents/os-dev.md @@ -34,6 +34,21 @@ rules that most often get missed: 2. **The issue is already claimed by the PM** (your shared GitHub identity). Do not change assignees. If you discover the issue duplicates or conflicts with someone else's in-flight work, stop and report `blocked`. + - **State on your PR that you did not set belongs to another actor — ask, + never "correct" it.** The shared identity makes everyone else's writes + look like yours: the PM's ready-flip and auto-merge arming, a bot's + labels, a footer the platform rewrote. #6567 is the case — a dev found + its PR's attribution footer in a form it had never typed, inferred *a + machine is editing my PR*, extended that to the **draft flag**, and + flipped the PM's ready PR back to draft. That destroys auto-merge and + merge-queue membership in one step, and `pull_request_read` exposes + neither, so the loss was silent even to the agent that caused it. The + observation was right and the inference was not: a rewritten body is + evidence about the body and nothing else. Surface the surprise in + `summary` and let the PM resolve it — reverting another actor's step is + never yours to do. The ready-flip least of all: you hand over a **draft**, + and the PM flipping it ready and arming auto-merge is the normal next + step of the process, not something acting on your PR. 3. **Scope = the issue. Nothing else.** Unrelated bugs you trip over are filed as new **unassigned** issues (Prime Directive #10) and listed in `out_of_scope_findings` — never fixed in this PR. Filing discipline @@ -141,7 +156,9 @@ Definition of done, in order: - Pushed with `git push -u origin claude/issue--` (retry on network failure with backoff). - **Draft** PR to `main`, body starting `Fixes #`, explanatory prose in - Chinese per repo convention. + Chinese per repo convention. Close it with the **session-URL** attribution + footer — the bare-URL form is stripped from the stored body on every later + edit (see the sanitizer note at the end of this file). - **`skip-changeset` label — your step, not CI's; the read-back is the proof.** A test-only / workflow-only / `.claude/`-only PR releases nothing and writes no changeset, so it needs the `skip-changeset` label — apply it yourself the @@ -398,3 +415,22 @@ as `Assert>`) and silently truncates any prose containing a bare ` >` is still valid TypeScript — avoid `<`+letter in PR/issue prose, and read the stored body back to verify whenever a snippet is load-bearing. + +The same at-rest rewriting reaches the attribution footer, so write that +footer in its **session-URL** form: + +```text +_Generated by [Claude Code](https://claude.ai/code)_ ← stripped on every edit +_Generated by [Claude Code](https://claude.ai/code/session_)_ ← survives both paths +``` + +A body ending in the bare form loses the entire footer — `---` separator +included — on every `update_pull_request` edit; the session-URL form survives +both write paths (measured on PR #6556, recorded in #6567, and #6556 still +carries it today). `create_pull_request` does not strip but *rewrites* the +bare form into the session one, which is exactly how a body comes back in a +shape you never typed: that is the platform, not another agent, and it is +evidence of nothing else (rule 2). Which layer performs the rewrite is +**unknown** and deliberately not chased — the guidance holds either way. +Comments are a separate path: the bare form survives there untouched, so a +claim or finding comment needs no special handling. diff --git a/AGENTS.md b/AGENTS.md index 99c24e655d..3539a7e091 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -193,6 +193,42 @@ post-mortem in #4588), and misreading shared-identity state is also how a maintainer's manual ready-flip got reverted by an agent that assumed its own write had failed. +**State on your PR that you did not set belongs to another actor — ask, never +"correct" it.** That is the same misread one step on, and the more expensive +half. Under one shared identity every other participant's write arrives +unsigned: the PM flipping your draft to ready and arming auto-merge, a bot +re-labelling, the platform rewriting your body. #6567 is the worked example — +a dev read its own PR body back, found the trailing `Generated by [Claude +Code]` footer in a form it had never typed, correctly concluded *something is +rewriting my PR*, then carried that conclusion to the **draft flag** and +flipped the PM's ready PR back to draft. That drops auto-merge and queue +membership at once (§7's third re-arm situation), and `pull_request_read` +reports neither, so the agent could not see what it had destroyed. The +observation was right; the second inference did not follow from it — body +rewriting is a known platform behaviour and is **evidence of nothing else**. +So when state you did not write changes under you: read the timeline event's +actor, or ask the PM. Undo it only once you know who set it and why. + +**Write the attribution footer in its session-URL form** — that is the half of +the above you can act on directly. Measured on PR #6556 and recorded in #6567: + +```text +_Generated by [Claude Code](https://claude.ai/code)_ ← stripped on edit +_Generated by [Claude Code](https://claude.ai/code/session_)_ ← survives +``` + +A body ending in the bare form loses the **whole** footer, `---` separator +included, on every `update_pull_request` edit (reproduced twice, including with +a blank line before the separator), while the session-URL form survives both +write paths — #6556 still carries it. `create_pull_request` does not +strip; it *rewrites* the bare form into the session form, which is precisely +how a body comes back in a shape nobody typed. **Which layer does this is +unknown** — platform sanitizer, MCP tool layer, or a workflow's body +post-processing — and the guidance does not depend on the answer, so nobody +should spend a session establishing it. Comments are a different path and are +unaffected: the bare form survives untouched in issue and PR comments, +including the two on #6567 itself. + The claim is also what makes the *finding* rule (Prime Directive #10) safe to follow. Once out-of-scope discoveries become issues, the issue list is a real queue other agents read, and a claim is the only thing separating "someone is on