Skip to content

Commit 9503465

Browse files
os-zhuangclaude
andauthored
feat(agents): pm-dispatch 首轮实跑经验回写 — 认领协议、跨仓查重、决策落原 issue、开发模型策略 (#4554)
* feat(agents): pm-dispatch round-1 lessons — claim protocol, cross-repo dedup, escalate-on-issue, dev model policy Four mechanics hardened from the first live dispatch round: - Claim protocol: shared GitHub identity means assignee alone cannot say WHICH agent claimed — a fixed-shape claim comment (round + branch name) carries identity, a comment read-back settles races (first comment wins), early branch push closes the claimed-but-invisible gap, and stale claims (24h, no branch, no PR) are reclaimed after inquiry. - Cross-repo dedup at triage: follow cross-references and keyword-search sibling repos for an issue's shadow; claimed shadow blocks dispatch, unclaimed shadow converges to one entry first, done shadow triggers a staleness check. The main backlog stays the only scheduling authority. - Escalation lands ON the issue it belongs to (analysis comment + needs-user-decision label as the maintainer's inbox); a separate [decision] issue only when no natural anchor exists — decision cards no longer accumulate. - Dev dispatches pass model: opus; the PM session keeps the stronger orchestration model (maintainer policy). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5 * feat(agents): pm-dispatch merge policy — accepted, fully-green dev PRs land via the merge queue Maintainer policy: once the PM's review verdict is ACCEPT and every CI check is green, the PM marks the dev-agent PR ready and adds it to the merge queue (the repo's sanctioned path — the queue lands it only if the rebuild against current main is green). Scope-limited: dev-agent PRs dispatched by this loop only; the PM's own tooling PRs stay with the maintainer; no red, unreviewed, or queue-bypassing merges. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012C2cd7tL8QDoZ2QKN3djJ5 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f61edce commit 9503465

2 files changed

Lines changed: 79 additions & 17 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
---
3+
4+
Internal agent tooling only (`.claude/` pm-dispatch claim protocol, cross-repo dedup, escalation-on-issue, dev-model policy) — releases nothing.

.claude/skills/pm-dispatch/SKILL.md

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,25 @@ routing isn't already decided:
149149
maintainer.** If after reading the code you genuinely cannot tell where a
150150
change lands, the issue is underspecified: escalate the *underlying
151151
product question* (step 8), not the routing.
152+
- **Cross-repo dedup — check the sibling repos for this issue's shadow**
153+
before it can be dispatched: follow every cross-repo reference on the
154+
issue's body/timeline, AND keyword-search open issues/PRs in the other two
155+
repos (module names, error strings). What you find decides the action:
156+
- shadow **claimed / PR in flight** → do not dispatch; write
157+
`Blocked-by: <repo>#<n>` + a comment, revisit for *remaining* work when
158+
it lands;
159+
- shadow **open, unclaimed** → converge first: cross-link, make it a
160+
sub-issue of the backlog issue (or close one as duplicate) so one thing
161+
has exactly one dispatch entry — then queue normally;
162+
- shadow **already done** → the backlog issue may be stale: verify what
163+
remains, recommend closing if nothing does;
164+
- **nothing found** → dispatch normally.
165+
The main backlog is the only scheduling authority — two queues must never
166+
dispatch the same work. Re-verify assignees (issue *and* linked shadows)
167+
at claim time, not just at triage: the gap between them is where races
168+
live. Known blind spot: work in a local worktree with no claim, no
169+
branch, no PR is invisible — that is what the claim-first rule (step 4)
170+
exists to shrink.
152171

153172
### 3. Select the batch
154173

@@ -162,16 +181,40 @@ not dispatch.
162181

163182
### 4. Claim
164183

165-
For each selected issue, **before dispatching** (repo rule: claim before code):
166-
assign it to yourself (`@me`) and add labels + a comment in Chinese, e.g.
167-
「已由 PM 循环派发给开发 agent(第 N 轮)。」Skip — and drop from the batch —
168-
any issue that acquired an assignee since step 1.
184+
All agents share one GitHub identity, so the assignee alone says "some agent
185+
claimed this" but never *which* — the claim comment carries the identity. For
186+
each selected issue, **before dispatching** (repo rule: claim before code),
187+
execute atomically, in order:
188+
189+
1. **Assign** to yourself (`@me`) and add `pm:dispatched`. Skip — and drop
190+
from the batch — any issue that acquired an assignee since step 1.
191+
2. **Claim comment** (Chinese), fixed shape — the branch name is the key,
192+
every later artifact (worktree, push, PR) hangs off it:
193+
> 认领:PM 循环第 N 轮
194+
> 分支:`claude/issue-<n>-<slug>`
195+
> Worktree:`<repo>-issue-<n>`
196+
3. **Race check**: assignment is idempotent, so two agents can both
197+
"succeed". Re-read the comments; if an earlier claim comment with a
198+
*different* branch name exists, you lost — touch nothing of theirs,
199+
reply 「已有认领,让行」, and pick another issue. First comment wins.
200+
201+
Dev agents push their branch early — a remote branch is the hardest evidence
202+
of work in flight, closing the gap between "claimed" and "PR exists".
203+
204+
**Stale-claim reclaim**: a claim older than ~24 h whose promised branch does
205+
not exist on the remote and has no PR is presumed dead — comment asking, and
206+
after another window of silence, remove the assignee (note why) and return
207+
the issue to the queue. Never reclaim a claim that has a live branch with
208+
commits.
169209

170210
### 5. Dispatch
171211

172212
One `Agent` call per issue, `subagent_type: "os-dev"` (fall back to
173213
`general-purpose` with the same prompt if the custom agent isn't loaded), run
174-
in parallel in the background. Prompt template — fill every placeholder, paste
214+
in parallel in the background. **Model split (maintainer policy): pass
215+
`model: "opus"` on every dev dispatch.** The PM session itself stays on the
216+
stronger orchestration model — triage, review, and decision framing are where
217+
its judgment pays; implementation goes to Opus. Prompt template — fill every placeholder, paste
175218
the full issue body, never a summary:
176219

177220
```
@@ -255,9 +298,14 @@ against the report's own claims:
255298
Verdict per issue:
256299

257300
- **ACCEPT** — comment on the issue (Chinese) linking the PR and summarizing
258-
what shipped; leave the PR for the normal human/merge-queue review flow.
259-
**The PM never merges** — merge discipline (CI-green, serial/queue) belongs
260-
to the repo, not this loop.
301+
what shipped. Then drive it to landing (maintainer policy: review passed +
302+
CI green ⇒ merge): once every check on the PR is green, mark it ready for
303+
review and **add it to the merge queue** — the queue rebuilds the PR
304+
against current `main` and lands it only if that result is green, which is
305+
the repo's sanctioned path. Never `--auto`-merge outside the queue; where
306+
no queue exists, merge serially per AGENTS.md §7 only after remote CI is
307+
fully green. This applies to **dev-agent PRs dispatched by this loop
308+
only** — the PM's own tooling PRs stay with the maintainer.
261309
- **REWORK** — concrete, itemized feedback; re-dispatch the same issue with
262310
the feedback block filled (same claim, new dev agent). **Max 2 rework
263311
rounds** per issue; a third failure escalates instead.
@@ -268,10 +316,19 @@ Verdict per issue:
268316
Whenever a dev returns `needs_decision`, an issue is too vague to dispatch, or
269317
rework has failed twice:
270318

271-
1. **File a new issue** titled `[决策] <一句话说清要拍板什么>`, labeled
272-
`needs-user-decision`, body in Chinese: 背景、具体问题、可选方案、你的
273-
建议、关联的原 issue / PR / 分支。**每个方案必须沿两条固定评估轴分析,
274-
这是决策分析的核心原则,不是可选项:**
319+
1. **Default: the decision lives ON the issue it belongs to — never a new
320+
issue.** Post the analysis as a comment on that issue, add the
321+
`needs-user-decision` label, drop it from the active queue. The label is
322+
the maintainer's inbox (filter `label:needs-user-decision` shows
323+
everything awaiting them); when they answer, the label comes off and the
324+
issue re-enters the queue. No bookkeeping issues accumulate. File a
325+
separate issue (titled `[决策] <一句话说清要拍板什么>`, same label) ONLY
326+
when the decision has no natural anchor — it spans several issues (file
327+
one, link it from each rather than duplicating the analysis) or arose
328+
with no issue of its own.
329+
2. The analysis, wherever it lands (Chinese): 背景、具体问题、可选方案、
330+
你的建议、关联的 issue / PR / 分支。**每个方案必须沿两条固定评估轴
331+
分析,这是决策分析的核心原则,不是可选项:**
275332
- **项目长远合理性** — 哪个方案符合北极星方向与可持续架构(Prime
276333
Directive #5 no workarounds、#8 North Star、#12 contract-first),
277334
而不是眼下最省事;临时补丁式的选项要明说其长期代价。
@@ -282,11 +339,9 @@ rework has failed twice:
282339
绝不让 AI 能声明一个运行时不兑现的能力。
283340
推荐意见必须基于这两条轴给出理由;两轴冲突时如实呈现权衡,交维护者
284341
拍板。
285-
2. Comment on the original issue linking the decision issue, add
286-
`needs-user-decision` to it too, and drop it from the active queue.
287342
3. If the session is interactive, additionally raise it via `AskUserQuestion`;
288-
the filed issue remains the durable record either way. **Never** answer a
289-
product/architecture question on the maintainer's behalf.
343+
the labeled issue remains the durable record either way. **Never** answer
344+
a product/architecture question on the maintainer's behalf.
290345

291346
### 9. Round report, then next round
292347

@@ -307,7 +362,10 @@ Stop the loop and report when any of these hits:
307362

308363
## Guardrails (binding)
309364

310-
- PM writes **no files** and merges **no PRs**. No exceptions.
365+
- PM writes **no files**. Merging is allowed for **reviewed, fully-green
366+
dev-agent PRs via the merge queue only** (see the ACCEPT verdict) — never
367+
its own PRs, never a red or unreviewed one, never bypassing the queue
368+
where one exists.
311369
- Never force-push, never push `main`, never reassign an issue claimed by
312370
someone else, never dispatch a `needs-user-decision` issue.
313371
- Every dev agent works in its **own worktree per repo** (enforced by

0 commit comments

Comments
 (0)