From 9098202ab58c18ff0890d1e28cc6b42d3fcbf05b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 15:52:59 +0000 Subject: [PATCH] =?UTF-8?q?docs(pm-dispatch):=20references=20platform-fact?= =?UTF-8?q?s=20sweep=20=E2=80=94=20seven=20measured=20reading=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Land the four member cards' measured platform/process facts plus the sweep's two scope amendments as self-contained reading rules across five files under .claude/skills/pm-dispatch/references/: gate readings pinned to the PR's current head and no reruns on superseded heads; the supersession signature; list_issues returning no assignees; issue_read body entity-escaping; account- scoped session handles and the outgoing PM's archive obligation; explicit mergeMethod with the echo unreliable in both directions; measured throughput parameters; the post-merge lane-inventory re-pull; and the author-side closing-keyword hygiene rule with the measured parser boundaries. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_018WuTtyckQa1VcXwgd52JpN --- .../references/dispatch-runbook.md | 8 +++++- .../references/landing-operations.md | 7 +++++ .../references/platform-readings.md | 28 ++++++++++++++++++- .../references/review-checklist.md | 17 +++++++++++ .../references/seat-post-protocol.md | 6 +++- 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/.claude/skills/pm-dispatch/references/dispatch-runbook.md b/.claude/skills/pm-dispatch/references/dispatch-runbook.md index 5c63a3398a..734bdb0161 100644 --- a/.claude/skills/pm-dispatch/references/dispatch-runbook.md +++ b/.claude/skills/pm-dispatch/references/dispatch-runbook.md @@ -68,7 +68,13 @@ branch-early、draft-PR 时点报告、transcript 复活与 worktree 接手协 `fire_trigger` + 用后即 `delete_trigger`);巡检退为兜底心跳,每轮先核订阅已覆盖哪 些面、只补盲区(会话停摆、未开 PR 的分支、姊妹仓动静)。CCR 云会话不注册跨会话消息 roster —— `ListAgents` 列不到、`SendMessage` 直投 not-reachable 是设计而非故障(维 -护者 2026-08-11 裁定:事件驱动架构即长期方案),⛔ 不复测 roster 路径。 +护者 2026-08-11 裁定:事件驱动架构即长期方案),⛔ 不复测 roster 路径。会话句柄同 +时是**账号作用域**的:`get_session` / `archive_session` / 绑会话的 poke 触发器,对 +另一个账号建的会话一律答 `not found`,且该回答与「会话从不存在」在响应里不可区分 +—— **⛔ 永不把它读作死亡信号**(误判死会招来往可能还活着的 worktree 里塞第二个 +agent);实测另一账号读作 `not found` 的会话,其 PM 在数十分钟前刚读到它 +RUNNING。跨账号接班时前任的会话三条路都不可达,活性判定只能走 GitHub 上的产出读 +数;draft-PR 时点交报的契约正是为此存在 —— 报告落在卡上,无需探活即可收口。 ## subagent 批(`mode:subagent`)派发前置:先快进本地检出 diff --git a/.claude/skills/pm-dispatch/references/landing-operations.md b/.claude/skills/pm-dispatch/references/landing-operations.md index 4ccae9bbd4..f313ae24fb 100644 --- a/.claude/skills/pm-dispatch/references/landing-operations.md +++ b/.claude/skills/pm-dispatch/references/landing-operations.md @@ -45,6 +45,13 @@ auto-merge 的顺序不可反(转回 draft 会同时掉 auto-merge 与队列成 ⛔ 不拆成「下轮巡检再摘」。同刻顺手读一次相关卡的 `closed_by_pull_requests`,确认没 有别的卡被正文里的闭合关键词误关(事实表见平台读数)。 +**每次合并后重拉一次车道盘点,与预期状态对账** —— 也是落地动作的一步,⛔ 不留给下 +轮巡检:取 open 卡清单,对照「本次合并应关哪些、不应关哪些」的预期 diff 一遍;预期 +之外从 open 消失的卡,就是被 PR 正文闭合关键词静默误关的卡(`completed` 状态对一切 +只看 open 的过滤与巡检隐身,不主动 diff 永远看不见)。与上一段互补而不互替: +`closed_by_pull_requests` 是逐卡核对、要先知道读哪张;盘点对账不需要先验名单,实测 +里抓住静默误关的正是这一步。 + **落地窗口给关键 PR 挂 `subscribe_pr_activity`**(会话型座位专用;Routine 座位每 fire 新会话收不到,维持轮询): diff --git a/.claude/skills/pm-dispatch/references/platform-readings.md b/.claude/skills/pm-dispatch/references/platform-readings.md index 6f50a4e77d..74c6437522 100644 --- a/.claude/skills/pm-dispatch/references/platform-readings.md +++ b/.claude/skills/pm-dispatch/references/platform-readings.md @@ -20,6 +20,12 @@ - **PR 转回 draft 会同时掉 auto-merge 与队列成员资格,且不自动恢复**;转正后必须 重新挂。反方向同理:要真踢出队列,只有转 draft —— `disable_pr_auto_merge` 单独 调用**不解除队列成员资格**,PR 照样落地。 +- **`enable_pr_auto_merge` 一律显式传 `mergeMethod: "SQUASH"`**:不传时静默退回 + 被禁的 merge-commit 方式 —— 等于无操作。但**回显在两个方向都不可靠**:实测队列 + 路径上回显为空(`method: `)而 timeline 的入队事件照发、PR 照落地;也实测显式 + 传 SQUASH 后回显 `MERGE`(参数被治理 `main` 的合并队列侧改写,落地历史仍是每 + PR 一提交)。⇒ 权威信号只有队列成员资格的 timeline 事件与最终 MERGED 状态, + ⛔ 不拿回显当任何方向的证据,也不读 `auto_merge` 字段。 - `enable_pr_auto_merge` 的空字段返回(`method: , enabled at `)对「入没入队」零区 分度,签名本身不构成任何方向的证据。序列:① 先验队列分支(给条目 ~20–30s 建 出);② 分支在 ⇒ 结束,⛔ 不翻转;③ 等待后仍缺席**且队列已见 churn**(更新的条 @@ -32,6 +38,9 @@ 件反射式重投。第三种签名:本 PR 名下**没有任何** `merge_group` run 且批次同伴的 run 全部 `success` = 队列重建的连带取消,不是红 —— 带签名读数收据重投一次(收据 留在 PR 上),⛔ 无收据不重投;同一 PR 第二次被踢移交队列管家。 +- **实测吞吐参数两则**:合并队列落地延迟 ≈ 每 PR 15–30 分钟且串行 —— 多 PR 在队 + 时按此估算落地窗口,⛔ 不据「还没落」提前判异常;单容器重验证(build+test)并 + 发甜点 ≈3 —— 排批时按它定同容器重验证卡的并发上限,再高互相争用、再低闲置。 ## API 配额 @@ -47,6 +56,17 @@ search 的 `label:a label:b`,或本地求交);`issue_write` 的 `labels` 是**整组替 换**不是追加 —— 不先读现值合并再写,会静默剥掉别的标签(状态机丢位);真追加走 REST `POST /issues/{n}/labels`;写后照标签纪律回读。 +- **`list_issues` 永不返回 assignees**(`fields` 枚举无此成员;不传 `fields` 响应 + 里同样没有)—— 已认领卡与空闲卡在响应里逐字节相同,车道清单因此**回答不了** + 「哪张能认领」这个它被用来回答的问题,失效完全静默、长得就像成功。清单只是 + **候选名单**:每一条在认领前都必须过一次完整 `issue_read`(它才返回 + `assignees`),⛔ 不把 `list_issues` 结果当候选集直接认领。 +- **MCP `issue_read` 的 body 是 HTML 实体转义过的**(撇号/引号/尖括号成实体),而 + comments 原样返回。⇒ MCP 座位做 body 往返(读 → 改 → `issue_write` 整体写回)不 + 安全:写回的是转义实体,或凭猜反转义 —— 长正文围栏里的箭头等显示编码不可靠逆 + 转,且同一套工具里无从对账真原文。机器可 grep 的行(`Blocked-by:` 一类)可能因 + 此落在评论首行而非 body —— 解锁扫描必须连评论一起扫(`in:comments`);确要改写 + body,先经 REST 取原始 body 对账再写。 ## 读数陷阱 @@ -59,6 +79,10 @@ - `rerun_failed_jobs` 复用原 run 的提交与合并 ref,不拿新 main 重算 —— 红因是基上 缺一个已合修复时重跑无效,只能推提交(`git merge origin/main`);判别:修复的合 并时间晚于 run 创建时间即是。 +- **同一 head 上轻量兄弟 workflow `success` + 重量级载体 `cancelled`,是普通取代 + 的预期签名,不是选择性失败**:兄弟 run 秒级跑完,载体要 10–15 分钟,新推送的 + cancel-in-progress 窗口只罩得住后者。见到它先比对 run 的 `head_sha` 与 PR 当前 + head(取代必有新 head),而不是开「为什么只取消了它」的调查。 - **CI 红了先取完整日志归档再下结论**:「completeness check 绿」只断言没有 worker 静默死,≠ 测试通过;并发输出的「相邻」≠「因果」(先查 `turbo.json` 依赖 边);⛔ 不只看 tail。公开发出的诊断被推翻时,更正发在同样公开的位置,据它开的 @@ -90,7 +114,9 @@ 面的否定词** ——「nothing here fixes #N」在合并时照关 #N,而写这句话的动机恰恰是声 明不修;好实践(读了兄弟卡、显式划界)反而制造了失效。安全写法:把号码放在没有关 键词打头的位置 —— `#N is not addressed here` / `out of scope: #N` / - `#N remains open`。 + `#N remains open`。实测的解析边界三条:关键词只绑**同一行**的 `#N`;动名词 + (closing/fixing)不是关键词,散文里出现不触发;行内反引号里的关键词不触发 + (code span 实测不建闭合链接;围栏块未独立实测,按同规则对待但留待复测)。 - **PR body 与 squash commit message 是两个独立解析源**:commit message 只有 `Fixes` 首行、看起来干净,不代表 body 干净 —— 只查 commit 会漏。误关的卡以 `completed` 状态对一切「只看 open」的过滤与巡检隐身,没有任何机械守卫覆盖这条路 diff --git a/.claude/skills/pm-dispatch/references/review-checklist.md b/.claude/skills/pm-dispatch/references/review-checklist.md index 4f54d579b2..073443d8ef 100644 --- a/.claude/skills/pm-dispatch/references/review-checklist.md +++ b/.claude/skills/pm-dispatch/references/review-checklist.md @@ -8,6 +8,15 @@ `Part of #`,否则合并会静默关掉一张正躺在决策箱里的卡,而 `needs-user-decision` 的收件箱过滤只看 open issue —— 卡一关,待裁问题就此无人可 见。翻 ready 之前亲核首行,别只信报告。 +- **`Part of` PR 翻 ready 前,再扫一遍正文的闭合关键词形状**:GitHub 的闭合关键词 + 解析器把 `close/fix/resolve` 及其变位(⛔ 动名词 closing/fixing 不在内)绑定到 + **同一行**的 `#N`,并无视周围全部散文 —— 否定、情态、警告一律被忽略,为**防止** + 误关而写的那句(「应当由 PM 另行关某卡」)恰恰就是执行误关的那句。写侧纪律: + ⛔ 永不把闭合关键词放在另一张 open 卡编号旁;安全写法是让编号不被关键词打头 + (「`#N` is not addressed here」/「out of scope: `#N`」),或把关键词放进反引号 + (实测:行内 code span 不触发;围栏未独立实测)。半状态巡查器带一条 report-only + 的「`Part of` 与闭合关键词绑定同一编号」矛盾检测,但它只巡开着的 PR —— 翻 + ready 前的这一扫是唯一挡在合并前面的人工步骤。 - **`Part of` 收口的卡不会自动关,`pm:dispatched` 必须手工摘**:`Fixes` 卡由 GitHub 关闭时标签随卡一起离开在飞视图;`Part of` 卡合并后仍然开着,标签留在原 地,于是 `label:pm:dispatched is:open` 把一张没有 dev、没有分支、没有任何在飞物 @@ -37,6 +46,14 @@ `conclusion` 已为 `success`(门禁族跑在其内),⛔ 不因报告写了「本地绿」跳过。收 敛期转红走补丁轮(SendMessage 续派原 dev —— 那是这笔交换已付过的价钱,不是 REWORK 的理由;红着合并才是)。重量级卡可在派发令显式写「本单等 CI」。 +- **每个门禁读数先钉到 PR 的当前 head**:先读 PR 的 `head.sha`,再比对 run 的 + `head_sha` —— 不一致的 run 是关于一个死提交的读数,绿与红**双向都不入账**:旧 + head 的绿会把「新推送未验」读成「消费者干净」,旧 head 的红会把当前 head 已修 + 掉的缺陷重新挂回 PR。 +- **被取代 head 上的 run 永不重跑**:非当前 head 上的 `cancelled` 结论零动作 —— + 新推送自带全套 run。重跑烧掉一整个重量级周期,还能忠实复现一个已被当前 head 修 + 掉的缺陷、给绿 PR 挂上假红;实测两次误重跑都源于读到 `cancelled` 没先比对 + head。 - **收益穿过必经边界之后还在吗?** 判据(不是每单都做):价值主张依赖某个下游组件 如实转发(HTTP 错误信封、序列化、日志汇聚、跨进程传输)⇒ 至少端到端验一次收益在 边界之后仍然存在 —— 精心写的拒收正文可能被 4xx 直通层整条替换,缺口在清单里不在 diff --git a/.claude/skills/pm-dispatch/references/seat-post-protocol.md b/.claude/skills/pm-dispatch/references/seat-post-protocol.md index 55a089e042..b96d708c16 100644 --- a/.claude/skills/pm-dispatch/references/seat-post-protocol.md +++ b/.claude/skills/pm-dispatch/references/seat-post-protocol.md @@ -57,7 +57,11 @@ 任会话 ID、注销时间、队列快照、**热文件串行队**、跨车道备忘);⑤ 审计评论存档 (接任指引:`/pm-dispatch 接手` + 先读座位贴);⑥ `list_triggers` 清点本会话全 部自设定时器,逐个清理**或随移交物转交**(绑着移交中 PR 的定时器随 PR 转交,不 - 清);⑦ 向维护者交最终报告 —— **不含 SKILL 更新建议清单**(该固定产物已退役,维护 + 清);⑥ʹ 本座位派出的 dev 会话由**离任 PM 自己**逐个 `archive_session` —— 会话句 + 柄是账号作用域的,换账号的接任者对它们一律 `not found`(细则见 + `dispatch-runbook.md`),归档义务**不可移交**;确须留跑的会话在座位贴里点名、写 + 明「归档不随交接转移」,⛔ 不把它留在接任者的欠账清单上 —— 那是一条接任者做不到 + 的待办;⑦ 向维护者交最终报告 —— **不含 SKILL 更新建议清单**(该固定产物已退役,维护 者 2026-08-12 裁定;零建议是好班次)。任期观察只可按三类上报:① 原则错/缺(不变量 级,罕见)→ 经专题通道给 skills 席;② 可机械化项(提议门禁或脚本,⛔ 永不散文)→ 正常立卡;③ 平台事实变化 → `references/` 事实表改一行。三类都以 `finding` 入