From bfdaf439e76a7c612763777ba187fa5fe0b737be Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 20:12:12 +0000 Subject: [PATCH 1/2] =?UTF-8?q?docs(pm-dispatch):=20=E6=8E=A2=E6=B4=BB?= =?UTF-8?q?=E5=85=A5=E4=BE=8B=E8=A1=8C=E3=80=81=E6=8A=A5=E5=91=8A=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E7=9B=B4=E6=8E=A5=E9=AA=8C=E6=94=B6=E3=80=81=E5=BA=A7?= =?UTF-8?q?=E4=BD=8D=E8=A1=8C=E8=87=AA=E6=9F=A5=E7=AD=89=E4=BA=94=E5=A4=84?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2026-08-05 实测教训回写(维护者指示): 1. step 6 新增「探活」固定动作:完成通知在宿主进程重启下会静默丢失—— 三个在飞 dev 死亡数小时无任何信号;每轮巡检对无产出的 dev 发状态 询问,「no active task; resumed from transcript」回包即当场复活。 2. step 6 新增「报告丢失直接验收」兜底:PR 全绿 + agent 确认死亡/静默 ≥2h + 报告未达 ⇒ 直接按 PR 对照 origin/main 验收(#5550/#5556 先例)。 3. 座位表协议新增「每轮核对自己的正文行」:协议升级迁移状态可把在任 PM 记成待认领,叠加惰性回收条款即误回收风险(spec 座位当日实例)。 4. worktree 接手协议前置 SendMessage 复活路径(更便宜、保全上下文), 接手协议降为 resume 不可用时的后备。 5. step 7 新增两条评审判据:验收判据本身可被 dev 用测量证伪(#5452 字面 grep 归零判据不成立,改钉配平不变量);tests/docs-only PR 走 skip-changeset 标签且 rerun 无效需新 labeled 事件(#5497/#5502)。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D --- .claude/skills/pm-dispatch/SKILL.md | 57 ++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/.claude/skills/pm-dispatch/SKILL.md b/.claude/skills/pm-dispatch/SKILL.md index e802958c40..2e0b968692 100644 --- a/.claude/skills/pm-dispatch/SKILL.md +++ b/.claude/skills/pm-dispatch/SKILL.md @@ -323,6 +323,12 @@ issues that collide on shared files — and「谁来分诊」原本是每个 PM —— Routine 座位查调度器(`last_fired` / `next_run`),会话座位查它最近一条 产出评论的时间戳,**>24h 无产出即可回收**(编辑该行 + 一条审计评论)。子树/ 批次里在飞的认领仍按认领协议由原认领者跟完。 +- **每轮巡检核对自己的正文行。** 协议或表格结构升级会迁移状态:2026-08-05 + spec 座位的在任 PM(接管时按升级前惯例只发过登记评论)在正文表格化迁移后 + 被记为「⏳ 待认领 / 前任已收官」—— 而上一条的惰性回收会把这个错位变成 + 误回收,别的会话也可能照着「待认领」真来接管。自查一行的成本是零;发现 + 不符,当场改行 + 审计评论,不等冲突发生。这与「从 labels 重建状态」同源: + 正文行也是状态,读它、修它,不靠记忆。 - **epic 委托不在座位表登记** —— `pm:epic` 父单正文自带会话与领地, `label:pm:epic` 即全量索引(见「Epic 子树车道」)。座位表只记常设座位,一件 事只记一处。 @@ -914,8 +920,11 @@ disobedience. `/compact`, and any host-level interruption of the PM session, kills running subagents together with their pending tool calls — #4700 and #4775 both died on -the same second. **The agent is not resumable; its worktree, branch and commits -are intact.** Never re-run the original dispatch prompt over that state: a fresh +the same second. **先试 SendMessage 复活,再谈接手**:对已死 agent 发一条消息, +宿主会「从 transcript 恢复」—— 带着它全部上下文接着干,worktree、分支、提交 +都还在(2026-08-05 三个死 agent 全部由此复活并各自收尾,见 step 6 探活)。 +仅当 resume 不可用(跨会话接手、transcript 丢失)时才走下面的 worktree 接手 +协议。Never re-run the original dispatch prompt over that state: a fresh agent that follows it will try to create the worktree that already exists, or redo work already committed. Dispatch a **new** agent with these four additions instead: @@ -1020,9 +1029,26 @@ connector grant 只能传递调用会话自身持有的,CCR 平台注入的 gith ### 6. Collect -**Subagent mode:** wait for the background task notifications — do not poll, -do not fabricate a pending agent's result. A dev that dies or returns -malformed output counts as `status: "blocked"` with its raw output attached. +**Subagent mode:** wait for the background task notifications — do not poll +for *results*, do not fabricate a pending agent's result. A dev that dies or +returns malformed output counts as `status: "blocked"` with its raw output +attached. + +**探活是每轮巡检的固定动作 —— 完成通知不可靠,它的缺席什么都不证明。** +下面的停摆纠偏处理「带任务中状态的通知到了」;这一条处理更隐蔽的另一半: +**通知根本不来**。宿主进程重启会把运行中的 subagent 连同其完成通知一起 +静默杀掉 —— 2026-08-05 实测,五个「在飞」dev 里三个(#5050/#5515/#5483) +已死数小时,批次视图仍显示 5/5,实际吞吐 2/5,零信号。规程三条: + +- 每次巡检(定时器唤醒、轮间隙)对**每个已派发且尚无远程分支/PR** 的 + dev 发一次状态询问(SendMessage,措辞「回一段简报后继续干活」,不改变 + 任务);派发后 ~45 分钟无任何远程产出即到探活门槛。 +- 两种回包都有价值:活着 → 拿到进度与阻塞点;**「no active task; resumed + from transcript」→ agent 生前已死,这次询问本身就是复活** —— 从其 + transcript 带全部上下文恢复,比 worktree 接手协议(step 5)便宜得多, + 优先用它;resume 不可用时才走接手协议。 +- 判据永远取正向证据(远程分支、PR、报告、探活回包),⛔ 绝不把「还没 + 收到失败通知」读作「还在跑」。 **A stalled subagent is this half's most common failure, and it never self-heals.** When a dev stops mid-task reasoning that "a background watcher will @@ -1069,6 +1095,14 @@ dispatch 由下一轮按同一判据处置(~2h 无报告即 `blocked`),`delete_t Routine 的取舍是:凡验证管线可能超过一个 fire 的活,**一开始就走 `mode:cloud`**, 把恢复权交给下一轮的 GitHub 读数,而不是赌它能在本轮内被唤醒。 +**报告丢失 ≠ 验收停摆(直接验收兜底)。** dev 的 JSON 报告是证据来源之一, +不是验收的先决条件 —— 状态模型第一句就是「所有状态在 GitHub」。同时满足 +(a) draft PR 已存在且 CI 全绿、(b) 探活确认 agent 已死或 ≥2h 无任何推送、 +(c) 报告未达 —— 则 PM 直接按 PR 验收:逐文件核对 diff 与认领申报的文件面, +对照 `origin/main` 复核 PR 正文的前提声明与验证叙述,step 7 其余判据不变 +(2026-08-05 的 #5550/#5556 即此路径落地并合并)。顺序保护:agent 可能还 +活着时**先探活、后翻 ready** —— 抢先翻会与它的收尾推送竞态。 + ### 7. Review each report You are the reviewer of record. For each report, verify against GitHub — not @@ -1103,6 +1137,19 @@ against the report's own claims: correction belongs in the PR/issue comments so the next reader inherits the corrected premise, and a wrong premise still sitting in an issue body gets its own follow-up issue rather than being silently dropped. +- **验收判据本身也是前提的一部分,可被 dev 证伪。** #5452 的 issue 把验收写成 + 「某条字面 grep 归零」,dev 实测证明该 pattern 修前修后命中数不变(修好的 + 正确输出同样匹配它),于是改钉真不变量(行内代码跨度花括号配平)做门禁, + 并因此多抓出 2 处 issue 的 grep 天然看不见的同根因缺陷。评审姿势:dev 用 + 测量推翻字面判据、换上等价或更强的不变量门禁 = 好运行,照 ACCEPT;但推翻 + 过程必须写在 PR 正文里,且新判据要附在 main 语料上的实测信噪比(误报为零 + 的证据),否则按 REWORK 要证据。 +- **Tests/docs-only PR 走 `skip-changeset` 标签,不走空 changeset**(空 + changeset 滞留发布,#4898)。标签由 PM 在验收时打。⛔ 打完标签后 Check + Changeset 的 **rerun 无效** —— rerun 重放的是旧事件载荷,看不见新标签; + 需要一个**新的 labeled 事件**(摘掉再打回)才翻绿(#5497/#5502 实测)。 + 边界:改动若含读者可见的生成产物(如参考文档),dev 选 changeset 而非 + 标签是对的 —— 以 PR 正文说明的理由为准,两条路都有效,别来回改。 - **`+0/-0` in a PR diff is not proof of an empty file.** git renders a file as binary — zero added, zero removed — as soon as it contains a NUL byte. #4870's 347-line test file showed `+0/-0` and was briefly misread as an From 6ee8735de733d01fa8dd618c95bd3263d66a5aa8 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 21:27:47 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs(pm-dispatch):=20skip-changeset=20?= =?UTF-8?q?=E6=9D=A1=E7=9B=AE=E9=9A=8F=20#5625=20=E6=9B=B4=E6=96=B0=20?= =?UTF-8?q?=E2=80=94=E2=80=94=20=E9=97=B8=E9=97=A8=E5=B7=B2=E5=AE=9E?= =?UTF-8?q?=E6=97=B6=E8=AF=BB=E6=A0=87=E7=AD=BE,=E6=95=99=E8=AE=AD?= =?UTF-8?q?=E6=94=B9=E8=AE=B0=E4=B8=BA=E3=80=8C=E8=BD=BD=E8=8D=B7=20vs=20?= =?UTF-8?q?=E7=8E=B0=E7=8A=B6=E3=80=8D=E9=80=9A=E7=97=85=E6=A0=87=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D --- .claude/skills/pm-dispatch/SKILL.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.claude/skills/pm-dispatch/SKILL.md b/.claude/skills/pm-dispatch/SKILL.md index 6312d72d2f..c3000100e4 100644 --- a/.claude/skills/pm-dispatch/SKILL.md +++ b/.claude/skills/pm-dispatch/SKILL.md @@ -1150,9 +1150,12 @@ against the report's own claims: 过程必须写在 PR 正文里,且新判据要附在 main 语料上的实测信噪比(误报为零 的证据),否则按 REWORK 要证据。 - **Tests/docs-only PR 走 `skip-changeset` 标签,不走空 changeset**(空 - changeset 滞留发布,#4898)。标签由 PM 在验收时打。⛔ 打完标签后 Check - Changeset 的 **rerun 无效** —— rerun 重放的是旧事件载荷,看不见新标签; - 需要一个**新的 labeled 事件**(摘掉再打回)才翻绿(#5497/#5502 实测)。 + changeset 滞留发布,#4898)。标签由 PM 在验收时打。历史坑(#5497/#5502 + 实测):该闸曾从**事件载荷**读标签,rerun 重放旧载荷看不见新标签,得靠 + 「摘掉再打回」制造新 labeled 事件 —— **#5625(#5580)已根治**,闸门改为 + 实时读 PR 标签,rerun 即翻绿。留此一条是因为它是一类通病的标本:**任何 + 从事件载荷而非现状读判据的闸,rerun 都复现旧世界** —— 撞上同形状的红, + 先查该闸读的是载荷还是现状,再决定是补事件还是改闸。 边界:改动若含读者可见的生成产物(如参考文档),dev 选 changeset 而非 标签是对的 —— 以 PR 正文说明的理由为准,两条路都有效,别来回改。 - **`+0/-0` in a PR diff is not proof of an empty file.** git renders a file