@@ -102,7 +102,23 @@ known case: accepting a `repo:objectui` PR ⇒ file a `pm:queue` issue in
102102` objectstack ` — "run ` pnpm objectui:refresh ` and land the console bump",
103103referencing the merged PR, blocked-by it until it actually merges.
104104
105- ** 4. One board, no second tracker.** The pm labels above are the state
105+ ** 4. Multiple PM sessions shard by repo — never share one queue.** The
106+ claim protocol makes concurrent PMs * safe* , not * useful* : batch
107+ independence (file-disjointness) is only checked within one PM's view, so
108+ two PMs on the same queue can claim different issues that collide on
109+ shared files, and the merge queue is one lane regardless. Scaling order:
110+
111+ 1 . One PM, bigger batch (` batch:5 ` is the maintainer's chosen operating
112+ point, riding on the resource discipline above), heavy tasks via
113+ ` mode:cloud ` — adds compute without adding schedulers.
114+ 2 . When one PM genuinely can't keep up: a second session takes a ** whole
115+ repo** as its shard (` /pm-dispatch repo:objectstack-ai/objectui ` ) —
116+ file universes are disjoint by construction. A sharded PM states its
117+ shard in every claim comment and ** never claims outside it** ; cross-repo
118+ parent/sub-issue chains stay with the main-backlog PM.
119+ 3 . Multiple PMs on the SAME queue: prohibited — all cost, no throughput.
120+
121+ ** 5. One board, no second tracker.** The pm labels above are the state
106122machine; an org-level GitHub Project pulling issues/PRs from all three repos
107123gives the maintainer a single view (filter by ` repo:* ` and ` pm:* ` ). The PM
108124maintains no tracking state outside GitHub — that invariant is what keeps
@@ -189,18 +205,34 @@ execute atomically, in order:
1892051 . ** Assign** to yourself (` @me ` ) and add ` pm:dispatched ` . Skip — and drop
190206 from the batch — any issue that acquired an assignee since step 1.
1912072 . ** Claim comment** (Chinese), fixed shape — the branch name is the key,
192- every later artifact (worktree, push, PR) hangs off it:
208+ every later artifact (worktree, push, PR) hangs off it. The session ID is
209+ NOT optional: under the shared identity it is the only line that lets a
210+ later reader — including your own future self after a context reset —
211+ answer "is this claim mine?". A claim without it caused the #4555 /#4559
212+ duplicate (#4588 ): the second session saw its own shared name as assignee
213+ and could not tell the claim was someone else's.
193214 > 认领: PM 循环第 N 轮
215+ > 会话:` session_<id> `
194216 > 分支:` claude/issue-<n>-<slug> `
195217 > Worktree:` <repo>-issue-<n> `
1962183 . ** Race check** : assignment is idempotent, so two agents can both
197219 "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.
220+ * different* session ID or branch name exists, you lost — touch nothing of
221+ theirs, reply 「已有认领,让行」, and pick another issue. First comment wins.
200222
201223Dev agents push their branch early — a remote branch is the hardest evidence
202224of work in flight, closing the gap between "claimed" and "PR exists".
203225
226+ ** Multiple GitHub accounts (colleagues' Claude Code sessions) simplify
227+ this, not complicate it.** Across accounts the assignee alone already says
228+ * who* : ` assignee isn't you → taken, never touch ` is the entire cross-account
229+ protocol, and it's already the rule. The claim-comment ritual (branch name,
230+ round, race check) matters * within* one account's sessions. When several
231+ accounts work the backlog, partition it the same way as multi-PM sharding —
232+ by repo or by an agreed label per account — and record the assignment table
233+ once in a pinned issue or the round report so nobody triages another
234+ account's shard.
235+
204236** Stale-claim reclaim** : a claim older than ~ 24 h whose promised branch does
205237not exist on the remote and has no PR is presumed dead — comment asking, and
206238after another window of silence, remove the assignee (note why) and return
@@ -242,6 +274,19 @@ Follow your operating procedure (you are the os-dev agent). Non-negotiables:
242274Return ONLY the JSON report defined in your agent definition.
243275```
244276
277+ #### Resource limits — parallel agents share ONE container
278+
279+ Memory peaks come from ** build + test** , not editing, so the fix is not less
280+ parallelism but serialized heavy phases: the os-dev definition requires every
281+ build/test run to hold the container-wide verification lock
282+ (` flock /tmp/os-heavy-verify.lock ` ), a ` NODE_OPTIONS=--max-old-space-size `
283+ heap cap, scoped ` --filter ` builds/tests, capped vitest/turbo workers, and
284+ worktree cleanup after the PR is up. PM-side: treat ` batch:3 ` as assuming
285+ normal-sized tasks — for build-heavy ones (dependency-family upgrades, full
286+ regression passes) drop to ` batch:2 ` , or dispatch that issue via
287+ ` mode:cloud ` so it gets its own container. If an agent dies with a
288+ heap/OOM signature, redispatch it alone rather than into a full batch.
289+
245290#### Dispatch backends
246291
247292** ` mode:subagent ` (default).** The ` Agent ` tool, as described above. The devs
@@ -313,8 +358,36 @@ Verdict per issue:
313358
314359### 8. Escalate uncertainties to the maintainer
315360
316- Whenever a dev returns ` needs_decision ` , an issue is too vague to dispatch, or
317- rework has failed twice:
361+ ** First, apply the escalation bar — most things that FEEL like decisions are
362+ not.** The maintainer's words: 「明显的问题直接修,不是事事都需要我确认」.
363+ Escalate ONLY when at least one of these holds:
364+
365+ - the options genuinely diverge on ** product semantics or public contract
366+ shape** and neither the issue, AGENTS.md, ADRs, nor existing code norms
367+ determines the answer;
368+ - the fix requires a ** destructive or hard-to-reverse action** (stored-data
369+ migration shape, deleting a shipped capability, force operations).
370+
371+ Everything else is the PM's call — decide, dispatch, and give the maintainer
372+ a ** veto window instead of a permission gate** : state what you decided and
373+ why in the issue comment and the round report; they can stop it, but you do
374+ not wait for them. Named non-escalation classes (act immediately):
375+
376+ - ** Restore-invariant fixes.** When the repo already states the invariant —
377+ one contract version across the family, declared = enforced, a gate must
378+ actually compile/run what it claims to check — a finding that the
379+ invariant is broken carries its own decision. A dual-version dependency
380+ graph, an inert tripwire, an unwired gate: queue it, dispatch it, report
381+ it. Asking "may I restore the invariant?" is the anti-pattern.
382+ - ** Sequencing and dependency ordering** between technical tasks.
383+ - ** Verification strategy** (what regression pass a risky-but-decided change
384+ needs) — that is scoping the work, not deciding it.
385+ - A dev's ` needs_decision ` that, on PM review, falls into the classes above:
386+ answer the dev yourself with the decision and rationale; do not relay it
387+ upward.
388+
389+ Whenever a dev returns ` needs_decision ` that passes the bar above, an issue
390+ is too vague to dispatch, or rework has failed twice:
318391
3193921 . ** Default: the decision lives ON the issue it belongs to — never a new
320393 issue.** Post the analysis as a comment on that issue, add the
0 commit comments