Skip to content

fix(work-driver): no pre-flight for an existing open PR — --restart rebuilds the issue and opens a duplicate (#358/#359) #362

Description

@randomm

Problem

The driver never asks whether an issue already has an open PR before building it again.

work-driver-commit.ts:212 calls gh pr create unconditionally. runBranch (work-driver-branch-develop.ts:45) never runs git rev-parse --verify <branch> or gh pr list. The only gh pr list --head call anywhere is a post-hoc repair inside the verification gate (work-driver-verify.ts:167), which adopts a PR number only when ops forgot to print the pr: marker.

The comment at work-driver-context.ts:177"Branch step's existing existing-branch detection handles worktree / branch leftovers at runtime" — describes logic that does not exist.

Live evidence — issue #5, 2026-08-05

/work was run with --restart while PR #358 was open for issue #5 on branch feature/issue-5-surface-thinking-only-output. --restart wipes the state file, so the driver treated #5 as greenfield: it branched a near-identical slug (feature/issue-5-surface-thinking-only-**model**-output), redid the entire implementation, and opened and merged PR #359.

Outcome:

Note the key insight: a branch-name-scoped lookup would not have caught this, because the second cycle chose a different branch name. The idempotency key has to be the issue number.

Proposed fix

A deterministic pre-flight at the top of runBranch, before the ops dispatch (so a doomed cycle burns zero tokens):

  1. gh pr list --state open --json number,headRefName,body in repoRoot.
  2. Match an existing PR for this cycle's issue by either signal:
    • body contains Fixes #<N> — the driver writes this into every PR body it creates (work-driver-commit.ts:178), so it is reliable for driver-authored PRs;
    • headRefName matches issue-?<N> on a word boundary — catches human-authored and pre-driver branches.
  3. If a match is found, emit cap-hit { cap: "existing-pr-detected", nextStep: "handoff" } carrying the PR number and its head branch.

Halt rather than auto-adopt. Adopting the PR number while the driver has committed to a different branch would attach our commits to a PR that does not contain them — the false-MERGED class this project has already hit twice (#245/#253). Deciding whether to resume the existing branch, retarget the PR, or close it is a judgment call, and per §7 doctrine a cap-hit produces a structured handoff rather than a question.

Escape hatch: PI_ENSEMBLE_PR_PREFLIGHT=0.

Acceptance criteria

  • A fresh /work N against an issue with no open PR is unaffected (no extra gh call on the hot path beyond the single pre-flight query).
  • /work N --restart against an issue with an open PR halts at branch with cap existing-pr-detected, before any ops/developer dispatch.
  • The handoff names the existing PR number, its head branch, and gives verbatim recovery commands (resume that branch, or close the PR and re-run).
  • explainCap renders a cause specific to this cap — not the generic else boilerplate that currently advises raising a spawn timeout.
  • Offline smoke test with an injected execFn covers: no-PR (proceeds), matching PR by Fixes #N body, matching PR by head-branch pattern, non-matching open PRs for other issues (proceeds), and gh failure (proceeds — fail-open, a lookup outage must not block work).
  • The stale claim at work-driver-context.ts:177 is corrected.

Out of scope

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions