Skip to content

Commit 9a56630

Browse files
os-elon-muskclaude
andauthored
docs(pm-dispatch): three write-side lines of the REST channel table, and the pipeline trap in post-stamped (#18391)
Fixes #18337 Fixes #18339 Fixes #18360 Three write-side lines of `.claude/skills/pm-dispatch/references/rest-channel.md` — the table a seat opens before hand-rolling a REST write — plus the same pipeline trap in the header of `scripts/pm/post-stamped.mjs`, which is the file opened before that idiom is typed. One commit per card. The file sits at its ratchet ceiling with zero headroom (82 / 82), so the two added rules are paid for by two retired rows, each declared below with the surviving home of every clause it carried. **82 lines before, 82 lines after.** No re-wrap was used as currency, and `references/platform-readings.md` is untouched. ## Per card ### #18337 — the pipeline-exit trap, beside the two tools it names **Landing point (by content):** the write-side block, immediately after the `label-write.mjs` row. **Before:** no such line; the trap was written down only in one lane's private Routine prompt. **After (verbatim):** ``` - ⛔ `post-stamped`/`label-write` 永不接进管道再 `&&`:拒收读成 0;看尾先落文件或 `set -o pipefail`。 ``` **Second half — the tool's own header.** `scripts/pm/post-stamped.mjs` gains one section, `## ⛔ Never pipe this tool, then && the write that follows`, which names the half-state shape (refused audit comment, label landed anyway, card graded with nothing saying why), the redirect-then-capture spelling, and `set -o pipefail`. It states the shape rather than citing a card number, and it does not restate the generic mechanism: that reading keeps its single home in `references/platform-readings.md`. **How this card reads independently:** both places carry the trap — the write-side block (grep the table for `pipefail`: was 0, is 1) and the tool header a seat opens before typing the idiom (`grep -c 'set -o pipefail' scripts/pm/post-stamped.mjs`). **Paid by:** retiring the write-side row ``` - 入队读 timeline `added_to_merge_queue`,落地读 `git rev-list --parents`;⛔ `auto_merge` 与回显都不作数。 ``` Every clause of it survives, twice over: `added_to_merge_queue` is already a judgment row in this same file's queue-routing block; `git rev-list --parents` is a spelling this file's own 第三桶 block declares to live in `platform-readings.md`, where it is written with its `-n1`; and the auto_merge / echo-back unreliability is in `platform-readings.md` twice plus this file's own queue-routing rule that the answer comes from the attempted action and not from the attribute field. It is the row this file's own line 「本表只指路,⛔ 不在两处各存一份」 refuses to keep. ### #18339 — the mandatory header, on the table seats consult **Landing point (by content):** the write-side block, immediately after the request-body line 「请求体走文件…或引号定界 heredoc」, which is the hook the card names. **Before:** the block carried zero mentions and zero cross-references of the header (`content-type`, case-insensitive, over the whole table: 0 hits, with `GET` at 12 hits as the firing control). **After (verbatim):** ``` - 每个写请求必带 `Content-Type: application/json`;缺头的 415 与判别式见配额段。 ``` **How this card reads independently:** the reader path from the write-side block to the rule is now **0 hops** — the rule is in the block — and the reading it is measured from (the 415, and the discriminator that tells a proxy refusal from a GitHub one) is **1 hop**, named rather than copied. Was ∞. The two `platform-readings.md` rows are not copied and not edited; the pointer uses this table's existing spelling for that section, which its own opening line and its 不可迁移 heading already use. **Paid by:** retiring the reading beside it, ``` - 双引号内 shell 先展开反引号、`$(...)`、`$VAR`,请求尚未成形;只标题坏而正文完好即此形。 ``` Both of its facts are in `platform-readings.md` verbatim: the backtick expansion with the broken-title-intact-body symptom, and the quoted-delimiter rule whose general form is 「请求体永不过会展开的 shell 上下文」, which is what covers `$(...)` and `$VAR`. The prescription those bytes explained — body via file or quoted heredoc, never an inline double-quoted string, with both spellings — stays untouched on the line above. ### #18360 — the direct-merge actor follows the token class, not the session **Landing point (by content):** the write-side block's direct-merge row. **Before (verbatim):** ``` - 直合仓 `PUT .../pulls/{n}/merge`;actor 记通道令牌:REST 按会话为 `claude[bot]` 或用户,MCP 恒用户。 ``` **After (verbatim):** ``` - 直合仓 `PUT .../pulls/{n}/merge`;actor 记令牌类,按账号非会话、逐写回读;见配额段,MCP 恒用户。 ``` **How this card reads independently:** `grep -c '按会话' ` on the file is 0 (was 1, that row); the row now says the actor is the token class, bound to the account and read back on each write; the fact-table rows landed by #18359 are pointed at, not copied; `MCP 恒用户` stays, because it is this table's own fact and has no other home. In place, 120 bytes, at the cap the ratchet sets. ## Line budget | | lines | |:---|---:| | `rest-channel.md` before | 82 | | `rest-channel.md` after | 82 | | ceiling | 82 | Added 2 rules, retired 2 rows, re-keyed 1 row in place. `scripts/pm/post-stamped.mjs` carries no ceiling (it is not in the ratchet's CEILINGS map — checked, not assumed). Every touched line is within the 120-byte cap; the two new rules measure 119 B and 98 B, the re-keyed row 120 B. ## Gates All 39 commands derived by `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` against this branch's real change set were run, exit code captured before any pipe. 38 of 39 are green, including `pnpm check:pm-skill-ratchet` (its own verdict line: `rest-channel.md is 82 lines (ceiling 82; headroom 0)`), `pnpm check:pm-post-stamped` (233 cases), `pnpm check:nul-bytes`, `pnpm check:doc-authoring`, `pnpm check:pm-governed-merges` and `pnpm check:pm-skill-id-lint`. `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first exited 3, PREREQUISITE NOT MET (unbuilt workspace packages, nothing measured); after `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` it exits 0. The remaining command, `pnpm check:pm-dispatch-gates` (a 430–450 s battery), was still running when this PR was opened; its verdict is in the dev report on #18337. `node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/rest-channel.md` exits 3 — GOVERNED, `.claude/**`. ⛔ No seat flips this ready, enqueues it or arms auto-merge; it lands after the skills seat's contract-tier review. ## Acceptance notes - Noted, not filed: nothing else in the write-side block was touched. The `-d @file` flag and the quoted-heredoc delimiter spelling, the shell-expansion prescription, and every ✓ channel row are unchanged. - To file (out of scope here, `scripts/pm/check-half-states.mjs`): H64's header quotes this same retired premise as its rationale — 「REST 按会话为 `claude[bot]` 或用户,MCP 恒用户」, sourced to `rest-channel.md`, with the surrounding prose reading "the write identity follows the CHANNEL rather than the account" and, further down, "the token class is handed to a session at start rather than chosen at write time". The landed fact table says the opposite in as many words: 「类按 Claude Code 账号定,⛔ 不按会话定」 and 「一会话内两次写之间可无席位动作地翻转」. The row's runtime message text carries the same sourcing. Prose only, no enforcement change; this PR's claim names `rest-channel.md` and `post-stamped.mjs` and nothing else, so it is reported rather than fixed here. Dedup words: `H64 按会话` · `check-half-states token class` · `write identity follows the channel` · `session-handed token`. ## 维护者速读(草稿) **改了什么** —— pm-dispatch 的 REST 通道对照表(席位手搓写请求前查的那张表)的写侧段,加了两条规则、改写了一条、退役了两条重复行;另外给 `scripts/pm/post-stamped.mjs` 的文件头加了一段管道陷阱说明。表的总行数没变(82 行,正好在棘轮天花板上)。 **为什么改** —— 三张 p3 卡,同一个形状:规则存在,但不在读者动手时会看的那张表上。① 把写脚本接进管道再 `&&`,管道退出码取末端,于是审计评论被拒、标签照落,卡上留下「改了标、没理由」;这条陷阱原先只写在某条车道的私有唤醒提示词里。② REST 写必带 `Content-Type: application/json`,缺了代理回 415、一个字节都不写 —— 规则写在事实表里,写侧段零提及,一个班次内六次独立踩中、四个端点,而失败长得像「别人并发把我的标签冲掉了」,会把一个加一个头就能修的问题误诊成分布式竞态。③ 直合的 actor 那行还写着「按会话」,而事实表已经改成「按 Claude Code 账号」,并记录了一个会话内无人操作就翻转两次的实测。 **风险与代价(含回滚)** —— 纯文本面,无代码行为改动,无 changeset(`.claude/**` 与 `scripts/pm/**` 不发布)。代价是退役了两行:每一行的每个子句都在别处有家(逐条列在上面的 Paid by 里),这是天花板零余量下唯一合法的付账方式 —— 删内容,不是折行。若判定某条退役行不该退,回滚是一次 revert:三个 commit 逐卡独立,可以只回其中一个。 **席位意见** —— **你要做的** —— 复核两处:① 两行退役是否同意(它们的存续副本是否真的够用);② 三条新/改写的行文字是否准确 —— 尤其 #18360 那行为了压进 120 字节,把「按 Claude Code 账号」压成「按账号」,完整拼写留在它指向的配额段。这是受管面(`.claude/**`),⛔ 不会有任何席位把它 ready 或入队。 --- _Generated by [Claude Code](https://claude.ai/code/session_01Bz6hxDBqK62NP2W1LATvnt)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 37af653 commit 9a56630

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

‎.claude/skills/pm-dispatch/references/rest-channel.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@
3636
- ✓ 评论 `POST .../issues/{n}/comments`;改评论 `PATCH .../issues/comments/{id}`。
3737
- ✓ 标签加法 `POST .../issues/{n}/labels`,定向删 `DELETE .../issues/{n}/labels/{name}`;加法优先。
3838
- 标签/assignee 写恒经 `scripts/pm/label-write.mjs`:四步内建、回读、回退整组 PATCH 回传 assignees。
39+
- ⛔ `post-stamped`/`label-write` 永不接进管道再 `&&`:拒收读成 0;看尾先落文件或 `set -o pipefail`。
3940
- ⛔ 永不 MCP `issue_write`(锁 1 已拒);会话分类器拒改动 ⇒ 无通道,交有通道席位立卡。
4041
- ✓ 建卡带标签 `POST .../issues` · 改正文 `PATCH .../issues/{n}` · 认领 `POST .../issues/{n}/assignees`。
4142
- 请求体走文件(`-d @file`)或引号定界 heredoc(`<<'EOF'`),⛔ 永不内联双引号串。
42-
- 双引号内 shell 先展开反引号、`$(...)`、`$VAR`,请求尚未成形;只标题坏而正文完好即此形。
43+
- 每个写请求必带 `Content-Type: application/json`;缺头的 415 与判别式见配额段。
4344
- ✓ 请求复审 `POST .../pulls/{n}/requested_reviewers` · 开 PR `POST .../pulls` 带 `draft=true`。
4445
- ✓ `origin/main` 合进 PR head:`PUT .../pulls/{n}/update-branch`,PM 席位、零文件写、真合并提交。
4546
- `expected_head_sha` 须完整 40 字符 SHA(短 SHA 回 422);base 未动回 422 = 无事可做,不是失败。
@@ -49,9 +50,8 @@
4950
- ✓ `POST .../ccr/comments/{id}/resolve` · `/unresolve`;`{id}` 是评审评论 id,⛔ 只在自己 PR 上探。
5051
- ✓ auto-merge 挂载 `PUT .../pulls/{n}/ccr/auto_merge` 带 `{"merge_method":"SQUASH"}`,`DELETE` 卸载。
5152
- ⛔ `PUT .../ccr/auto_merge` 在 draft 上 422 零存储;`DELETE` 无挂载回 422 = 本就没挂,非失败。
52-
- 入队读 timeline `added_to_merge_queue`,落地读 `git rev-list --parents`;⛔ `auto_merge` 与回显都不作数。
5353
- ⛔ 永不 MCP `update_pull_request`(锁 1 已拒);ready/draft 翻转只走 ccr 路;auto-merge MCP 锁 1 同拒。
54-
- 直合仓 `PUT .../pulls/{n}/merge`;actor 记通道令牌:REST 按会话为 `claude[bot]` 或用户,MCP 恒用户。
54+
- 直合仓 `PUT .../pulls/{n}/merge`;actor 记令牌类,按账号非会话、逐写回读;见配额段,MCP 恒用户。
5555

5656
## 不可迁移 —— 只有这三件,围着它们排计划;红窗守候规则住 `platform-readings.md` 配额段
5757

‎scripts/pm/post-stamped.mjs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@
6262
* `{{NOW}}` is never refused — so mixed alone would not have caught the
6363
* recorded failure, whose comments carried no token at all.
6464
*
65+
* ## ⛔ Never pipe this tool, then `&&` the write that follows
66+
*
67+
* The exit register above is worth exactly what the caller reads. A pipeline's
68+
* status is its LAST command's, so the habitual seat idiom
69+
* `post-stamped … | tail -3 && label-write …` hands the `&&` tail's 0 and the
70+
* refusal is gone: the audit comment is REFUSED, the label lands anyway, and the
71+
* card is left graded with nothing on it saying why — the half-state the
72+
* comment-before-label ordering exists to prevent, and the direction of it that
73+
* nobody can recover from a later read. Measured on a live card, where the seat
74+
* noticed eight seconds on; a turn that had ended there would have left it.
75+
*
76+
* So read the output without spending the code — redirect first, then capture:
77+
*
78+
* node scripts/pm/post-stamped.mjs … > /tmp/p.log 2>&1; EXIT=$?; tail -3 /tmp/p.log
79+
*
80+
* or arm `set -o pipefail` before the pipeline. The rule is the caller's, not
81+
* this tool's, so it covers every writer beside it — `label-write.mjs` included.
82+
*
6583
* ## The quoted route has a DIRECTION, not only a shape (#17763)
6684
*
6785
* Checking that a `{{WAS:…}}` value is shaped like a stamp leaves the estimate

0 commit comments

Comments
 (0)