Summary
DevClaw allowed one issue to fork into multiple live PRs, then continued review and merge routing against stale PR metadata.
This is not just duplicate PR clutter. In the reproduced lane, the workflow ultimately merged the wrong PR after reviewer intent had already converged on a different canonical PR.
Reproduction case
Observed on First Light issue:
PR spray created for the same issue:
#426 feature/425-runtime-authority-move-budget
#427 feature/425-runtime-budget-source-of-truth
#428 feature/425-harness-runtime-source-of-truth
#429 feature/425-harness-runtime-sole-source-of-truth-for-move-limit-runtime-budget-during-simulation-and-replay-validation
#430 feature/425-harness-runtime-move-budget-authority
#431 feature/425-harness-runtime-authority
#432 feature/425-runtime-move-budget-authority
All were opened the same day by the same worker actor against main.
Core failure
The issue-level canonical PR pointer effectively stayed stale on #426 while later developer retries created and/or updated newer PRs. Reviewer dispatch and later merge routing continued to trust the stale tracked PR instead of the latest resolved canonical PR.
In other words:
- developer retries were allowed to create replacement PRs for the same issue
- issue state did not atomically switch canonical
prUrl to the replacement PR
- reviewer runs continued to inspect the stale PR
- merge routing later acted on stale PR identity
Evidence
1) Initial PR was #426
GitHub issue timeline for firstlight#425:
2) Repeated To Improve -> Doing redispatches created or updated newer PRs
Observed PR sequence:
#426 created 2026-05-29T16:24:43Z
#427 created 2026-05-29T16:34:53Z
#428 created 2026-05-29T17:30:49Z
#429 created 2026-05-29T17:47:28Z
#430 created 2026-05-29T18:13:22Z
#431 created 2026-05-29T19:32:24Z
#432 created 2026-05-29T20:26:04Z
Representative workflow events from orchestrator_intervention(get_events, issueId=425):
2026-05-29T17:31:27.527Z developer completed with prUrl=https://github.com/yaqub0r/firstlight/pull/428
2026-05-29T18:14:05.136Z developer summary explicitly says it opened PR #430, but event prUrl still points at https://github.com/yaqub0r/firstlight/pull/426
2026-05-29T20:26:28.927Z developer summary explicitly says it opened PR #432, but later workflow state still did not reliably converge on #432
This suggests the worker output and the issue’s tracked PR identity diverged.
3) Reviewer kept reviewing stale PR #426
Repeated reviewer completions reference #426, for example:
2026-05-29T17:33:36.819Z reviewer rejected after reviewing PR #426
2026-05-29T17:50:56.414Z reviewer rejected PR #426
2026-05-29T18:04:16.015Z reviewer rejected PR #426
2026-05-29T18:15:31.151Z reviewer rejected PR #426
2026-05-29T18:28:03.645Z reviewer rejected PR #426
2026-05-29T18:39:04.866Z reviewer rejected PR #426
2026-05-29T18:57:54.526Z reviewer rejected PR #426
2026-05-29T19:09:00.285Z reviewer rejected PR #426
So review routing stayed anchored to the stale PR handle even while the developer lane was moving.
4) Reviewer later converged on #432, but merge still targeted stale identity
Late-lane evidence:
2026-05-29T21:03:29.305Z developer completed and explicitly said canonical PR #432 is the correct fix branch; summary also states #426 is stale/superseded
2026-05-29T21:05:23.575Z reviewer approved after reviewing canonical PR #432
But the workflow lane had already shown evidence of stale PR routing earlier, and during the investigation we observed a particularly dangerous mismatch state:
- reviewer text referenced the canonical newer PR
- workflow merge routing still resolved through stale tracked PR identity
That is the integrity bug this issue is about.
5) Tester then showed the base branch still reflected stale behavior
A downstream test run on the merged/base branch still found the old runDoc.levelJson authority behavior. That means this is not merely cosmetic metadata drift. Stale PR identity can result in the wrong artifact being treated as the lane’s truth.
Expected behavior
For any one issue, DevClaw should maintain exactly one canonical live PR identity unless a replacement is explicitly performed.
If a developer run creates a replacement PR:
- the issue’s canonical
prUrl must update atomically
- older sibling PRs must be marked superseded or removed from routing
- reviewer dispatch must resolve from the new canonical
prUrl
- merge must target the exact same PR id that reviewer approved
Actual behavior
DevClaw allowed this inconsistent state:
- worker summary names PR
#X
- issue state still points at PR
#Y
- reviewer dispatch uses
#Y
- later approval text may refer to
#X
- merge/path routing can still act on
#Y
Likely fault domains
Please investigate at least these boundaries:
-
work_finish / completion ingestion
- when developer returns
prUrl and summary text, what becomes the canonical PR for the issue?
- can summary text and stored
prUrl diverge without a hard failure?
-
issue state / canonical PR storage
- where is the canonical PR for an issue stored?
- when a later developer run opens a new PR, why is the old PR still considered authoritative?
-
review dispatch resolution
- does reviewer dispatch pull PR identity from issue state, from labels, from PR cross references, or from worker summary text?
- why did repeated reviewer runs continue to inspect
#426 even after newer PRs existed?
-
merge routing
- what PR identifier is used when reviewer approves?
- can approval of one PR accidentally merge a different PR because merge consumes stale issue metadata?
-
duplicate-run / spawn dedupe
- repeated
To Improve -> Doing cycles were all sessionAction: "spawn"
- is there missing dedupe or reuse protection when an issue already has an active developer lane or open PR?
Acceptance criteria
- Reproduce or convincingly trace how one issue can end up with multiple open PRs while keeping a stale canonical PR pointer.
- Identify the exact source of truth for canonical PR identity in the workflow.
- Prove that reviewer dispatch always uses the current canonical PR, or fix it so it does.
- Prove that merge uses the same PR id that reviewer approved, or fix it so it does.
- If worker output names PR
#X but stored issue state still resolves to #Y, fail closed with an explicit routing error.
- Add protection so replacement PR creation requires explicit supersession handling for older sibling PRs.
- Add coverage for the failure mode where multiple PRs are cross-referenced to one issue during repeated
To Improve redispatches.
Nice-to-have safeguards
- one issue -> one canonical PR invariant
- explicit
supersedes / superseded-by handling in the workflow
- auto-comment or auto-label stale sibling PRs when canonical PR changes
- reviewer refusal when current issue PR identity and worker-reported PR identity disagree
- merge refusal when approved PR id does not equal canonical issue PR id
Operator impact
This bug makes the workflow unsafe to trust in fix/review loops:
- review can stay attached to stale artifacts
- newer corrective work can be ignored
- the wrong PR can be merged
- testers can be handed a base branch that does not actually contain the reviewed fix
Repo / environment notes
Local DevClaw repo assumed for this issue:
- repo path:
/home/sai/git/devclaw
- remote:
https://github.com/yaqub0r/devclaw.git
Suggested labels
- bug
- workflow-integrity
- review-routing
- pr-lifecycle
Summary
DevClaw allowed one issue to fork into multiple live PRs, then continued review and merge routing against stale PR metadata.
This is not just duplicate PR clutter. In the reproduced lane, the workflow ultimately merged the wrong PR after reviewer intent had already converged on a different canonical PR.
Reproduction case
Observed on First Light issue:
#425inyaqub0r/firstlightPR spray created for the same issue:
#426feature/425-runtime-authority-move-budget#427feature/425-runtime-budget-source-of-truth#428feature/425-harness-runtime-source-of-truth#429feature/425-harness-runtime-sole-source-of-truth-for-move-limit-runtime-budget-during-simulation-and-replay-validation#430feature/425-harness-runtime-move-budget-authority#431feature/425-harness-runtime-authority#432feature/425-runtime-move-budget-authorityAll were opened the same day by the same worker actor against
main.Core failure
The issue-level canonical PR pointer effectively stayed stale on
#426while later developer retries created and/or updated newer PRs. Reviewer dispatch and later merge routing continued to trust the stale tracked PR instead of the latest resolved canonical PR.In other words:
prUrlto the replacement PREvidence
1) Initial PR was
#426GitHub issue timeline for
firstlight#425:2026-05-29T16:24:44Zcross-reference to PR#4262) Repeated
To Improve -> Doingredispatches created or updated newer PRsObserved PR sequence:
#426created2026-05-29T16:24:43Z#427created2026-05-29T16:34:53Z#428created2026-05-29T17:30:49Z#429created2026-05-29T17:47:28Z#430created2026-05-29T18:13:22Z#431created2026-05-29T19:32:24Z#432created2026-05-29T20:26:04ZRepresentative workflow events from
orchestrator_intervention(get_events, issueId=425):2026-05-29T17:31:27.527Zdeveloper completed withprUrl=https://github.com/yaqub0r/firstlight/pull/4282026-05-29T18:14:05.136Zdeveloper summary explicitly says it opened PR#430, but eventprUrlstill points athttps://github.com/yaqub0r/firstlight/pull/4262026-05-29T20:26:28.927Zdeveloper summary explicitly says it opened PR#432, but later workflow state still did not reliably converge on#432This suggests the worker output and the issue’s tracked PR identity diverged.
3) Reviewer kept reviewing stale PR
#426Repeated reviewer completions reference
#426, for example:2026-05-29T17:33:36.819Zreviewer rejected after reviewing PR#4262026-05-29T17:50:56.414Zreviewer rejected PR#4262026-05-29T18:04:16.015Zreviewer rejected PR#4262026-05-29T18:15:31.151Zreviewer rejected PR#4262026-05-29T18:28:03.645Zreviewer rejected PR#4262026-05-29T18:39:04.866Zreviewer rejected PR#4262026-05-29T18:57:54.526Zreviewer rejected PR#4262026-05-29T19:09:00.285Zreviewer rejected PR#426So review routing stayed anchored to the stale PR handle even while the developer lane was moving.
4) Reviewer later converged on
#432, but merge still targeted stale identityLate-lane evidence:
2026-05-29T21:03:29.305Zdeveloper completed and explicitly said canonical PR#432is the correct fix branch; summary also states#426is stale/superseded2026-05-29T21:05:23.575Zreviewer approved after reviewing canonical PR#432But the workflow lane had already shown evidence of stale PR routing earlier, and during the investigation we observed a particularly dangerous mismatch state:
That is the integrity bug this issue is about.
5) Tester then showed the base branch still reflected stale behavior
A downstream test run on the merged/base branch still found the old
runDoc.levelJsonauthority behavior. That means this is not merely cosmetic metadata drift. Stale PR identity can result in the wrong artifact being treated as the lane’s truth.Expected behavior
For any one issue, DevClaw should maintain exactly one canonical live PR identity unless a replacement is explicitly performed.
If a developer run creates a replacement PR:
prUrlmust update atomicallyprUrlActual behavior
DevClaw allowed this inconsistent state:
#X#Y#Y#X#YLikely fault domains
Please investigate at least these boundaries:
work_finish / completion ingestion
prUrland summary text, what becomes the canonical PR for the issue?prUrldiverge without a hard failure?issue state / canonical PR storage
review dispatch resolution
#426even after newer PRs existed?merge routing
duplicate-run / spawn dedupe
To Improve -> Doingcycles were allsessionAction: "spawn"Acceptance criteria
#Xbut stored issue state still resolves to#Y, fail closed with an explicit routing error.To Improveredispatches.Nice-to-have safeguards
supersedes/superseded-byhandling in the workflowOperator impact
This bug makes the workflow unsafe to trust in fix/review loops:
Repo / environment notes
Local DevClaw repo assumed for this issue:
/home/sai/git/devclawhttps://github.com/yaqub0r/devclaw.gitSuggested labels