From 6d7e34e617060c43959c58a326881f5f4d8db9af Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 08:01:24 +0000 Subject: [PATCH 1/3] docs(pm-dispatch): the pipeline-exit trap where the two write tools are typed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pipeline's status is its last command's, so `post-stamped … | tail -N && label-write …` rewrites a refusal into the tail's 0: the audit comment is refused, the label lands, and the card carries a grade with nothing on it saying why — the half-state the comment-before-label ordering exists to prevent. The trap was written down in one lane's private wake-up text only, which is why it reached no seat before it was typed. It now sits in both places a seat passes through: the write-side block of `references/rest-channel.md`, beside the two tools it names, and the header of `scripts/pm/post-stamped.mjs`, which is the file opened before the idiom is typed. Neither restates the generic mechanism — that reading keeps its single home in `references/platform-readings.md`. Paid in place at the 82-line ceiling by retiring the 入队/落地 verification row: `added_to_merge_queue` is already on this file's own queue-routing block, and `git rev-list --parents` is a spelling this file's 第三桶 block already routes to `platform-readings.md`, where it lives verbatim. Claude-Session: https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt Co-authored-by: Claude --- .../pm-dispatch/references/rest-channel.md | 2 +- scripts/pm/post-stamped.mjs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.claude/skills/pm-dispatch/references/rest-channel.md b/.claude/skills/pm-dispatch/references/rest-channel.md index 82130421bc3..86917f77867 100644 --- a/.claude/skills/pm-dispatch/references/rest-channel.md +++ b/.claude/skills/pm-dispatch/references/rest-channel.md @@ -36,6 +36,7 @@ - ✓ 评论 `POST .../issues/{n}/comments`;改评论 `PATCH .../issues/comments/{id}`。 - ✓ 标签加法 `POST .../issues/{n}/labels`,定向删 `DELETE .../issues/{n}/labels/{name}`;加法优先。 - 标签/assignee 写恒经 `scripts/pm/label-write.mjs`:四步内建、回读、回退整组 PATCH 回传 assignees。 +- ⛔ `post-stamped`/`label-write` 永不接进管道再 `&&`:拒收读成 0;看尾先落文件或 `set -o pipefail`。 - ⛔ 永不 MCP `issue_write`(锁 1 已拒);会话分类器拒改动 ⇒ 无通道,交有通道席位立卡。 - ✓ 建卡带标签 `POST .../issues` · 改正文 `PATCH .../issues/{n}` · 认领 `POST .../issues/{n}/assignees`。 - 请求体走文件(`-d @file`)或引号定界 heredoc(`<<'EOF'`),⛔ 永不内联双引号串。 @@ -49,7 +50,6 @@ - ✓ `POST .../ccr/comments/{id}/resolve` · `/unresolve`;`{id}` 是评审评论 id,⛔ 只在自己 PR 上探。 - ✓ auto-merge 挂载 `PUT .../pulls/{n}/ccr/auto_merge` 带 `{"merge_method":"SQUASH"}`,`DELETE` 卸载。 - ⛔ `PUT .../ccr/auto_merge` 在 draft 上 422 零存储;`DELETE` 无挂载回 422 = 本就没挂,非失败。 -- 入队读 timeline `added_to_merge_queue`,落地读 `git rev-list --parents`;⛔ `auto_merge` 与回显都不作数。 - ⛔ 永不 MCP `update_pull_request`(锁 1 已拒);ready/draft 翻转只走 ccr 路;auto-merge MCP 锁 1 同拒。 - 直合仓 `PUT .../pulls/{n}/merge`;actor 记通道令牌:REST 按会话为 `claude[bot]` 或用户,MCP 恒用户。 diff --git a/scripts/pm/post-stamped.mjs b/scripts/pm/post-stamped.mjs index f5fe528a9f2..9b124d419cf 100644 --- a/scripts/pm/post-stamped.mjs +++ b/scripts/pm/post-stamped.mjs @@ -62,6 +62,24 @@ * `{{NOW}}` is never refused — so mixed alone would not have caught the * recorded failure, whose comments carried no token at all. * + * ## ⛔ Never pipe this tool, then `&&` the write that follows + * + * The exit register above is worth exactly what the caller reads. A pipeline's + * status is its LAST command's, so the habitual seat idiom + * `post-stamped … | tail -3 && label-write …` hands the `&&` tail's 0 and the + * refusal is gone: the audit comment is REFUSED, the label lands anyway, and the + * card is left graded with nothing on it saying why — the half-state the + * comment-before-label ordering exists to prevent, and the direction of it that + * nobody can recover from a later read. Measured on a live card, where the seat + * noticed eight seconds on; a turn that had ended there would have left it. + * + * So read the output without spending the code — redirect first, then capture: + * + * node scripts/pm/post-stamped.mjs … > /tmp/p.log 2>&1; EXIT=$?; tail -3 /tmp/p.log + * + * or arm `set -o pipefail` before the pipeline. The rule is the caller's, not + * this tool's, so it covers every writer beside it — `label-write.mjs` included. + * * ## The quoted route has a DIRECTION, not only a shape (#17763) * * Checking that a `{{WAS:…}}` value is shaped like a stamp leaves the estimate From fda9ece4a6c860be7f198d8d75df94a081ccb5a1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 08:01:37 +0000 Subject: [PATCH 2/3] docs(pm-dispatch): the mandatory write-side header, on the table seats consult MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Content-Type: application/json` is required of every REST write through the egress proxy, and a request without it is refused 415 with not one byte written. The rule existed and was right; it lived only in `references/platform-readings.md`, while the table a seat opens before hand-rolling a curl — this file's write-side block, two of whose lines are about how the request body is spelled — carried no mention and no cross-reference of it. Six independent 415s across four endpoints in one shift are the behavioural evidence that the split placement did not reach the reader; each reads as a concurrent whole-set overwrite of the write, which is a distributed race that was never there. The rule is now stated where it is typed, and the reading it is measured from — the 415 and the discriminator that tells a proxy refusal from a GitHub one — keeps its single home, one hop away, named rather than copied. Paid in place at the 82-line ceiling by retiring the shell-expansion reading beside it: the backtick expansion, the broken-title-intact-body symptom and the quoted-delimiter requirement are all in `platform-readings.md` verbatim, and the prescription they belong to stays on the line above. Claude-Session: https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt Co-authored-by: Claude --- .claude/skills/pm-dispatch/references/rest-channel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/pm-dispatch/references/rest-channel.md b/.claude/skills/pm-dispatch/references/rest-channel.md index 86917f77867..b670a1c8e0f 100644 --- a/.claude/skills/pm-dispatch/references/rest-channel.md +++ b/.claude/skills/pm-dispatch/references/rest-channel.md @@ -40,7 +40,7 @@ - ⛔ 永不 MCP `issue_write`(锁 1 已拒);会话分类器拒改动 ⇒ 无通道,交有通道席位立卡。 - ✓ 建卡带标签 `POST .../issues` · 改正文 `PATCH .../issues/{n}` · 认领 `POST .../issues/{n}/assignees`。 - 请求体走文件(`-d @file`)或引号定界 heredoc(`<<'EOF'`),⛔ 永不内联双引号串。 -- 双引号内 shell 先展开反引号、`$(...)`、`$VAR`,请求尚未成形;只标题坏而正文完好即此形。 +- 每个写请求必带 `Content-Type: application/json`;缺头的 415 与判别式见配额段。 - ✓ 请求复审 `POST .../pulls/{n}/requested_reviewers` · 开 PR `POST .../pulls` 带 `draft=true`。 - ✓ `origin/main` 合进 PR head:`PUT .../pulls/{n}/update-branch`,PM 席位、零文件写、真合并提交。 - `expected_head_sha` 须完整 40 字符 SHA(短 SHA 回 422);base 未动回 422 = 无事可做,不是失败。 From 7e05c91ad9a1f682d630e3ac056fa6da2a31a2d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 08:01:49 +0000 Subject: [PATCH 3/3] docs(pm-dispatch): the direct-merge actor follows the token class, not the session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The row read 「REST 按会话为 `claude[bot]` 或用户」 — the premise the fact table retired: the REST channel's token class follows the Claude Code account, and it flipped inside a single session, with no seat action, twice in two days on two different seats. A seat recording a `merged_by` actor from this row records it on a premise its own read-back can contradict. Re-keyed in place: the actor is the token class, read from each write's own read-back rather than assumed from the session, with the measured rows named instead of copied. `MCP 恒用户` stays — it is this table's fact and has no other home. Claude-Session: https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt Co-authored-by: Claude --- .claude/skills/pm-dispatch/references/rest-channel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/pm-dispatch/references/rest-channel.md b/.claude/skills/pm-dispatch/references/rest-channel.md index b670a1c8e0f..a195fc4a47a 100644 --- a/.claude/skills/pm-dispatch/references/rest-channel.md +++ b/.claude/skills/pm-dispatch/references/rest-channel.md @@ -51,7 +51,7 @@ - ✓ auto-merge 挂载 `PUT .../pulls/{n}/ccr/auto_merge` 带 `{"merge_method":"SQUASH"}`,`DELETE` 卸载。 - ⛔ `PUT .../ccr/auto_merge` 在 draft 上 422 零存储;`DELETE` 无挂载回 422 = 本就没挂,非失败。 - ⛔ 永不 MCP `update_pull_request`(锁 1 已拒);ready/draft 翻转只走 ccr 路;auto-merge MCP 锁 1 同拒。 -- 直合仓 `PUT .../pulls/{n}/merge`;actor 记通道令牌:REST 按会话为 `claude[bot]` 或用户,MCP 恒用户。 +- 直合仓 `PUT .../pulls/{n}/merge`;actor 记令牌类,按账号非会话、逐写回读;见配额段,MCP 恒用户。 ## 不可迁移 —— 只有这三件,围着它们排计划;红窗守候规则住 `platform-readings.md` 配额段