Skip to content

oversee: a crashed PLAN run is unrecoverable — planner claim-abort drops the task, stranding the [Plan] PR #2614

Description

@carlos-alm

Found while running /oversee 2088. The PLAN run died mid-pipeline and could not be resumed by
any supported route
, leaving [Plan] PR #2612 stranded with critic findings unaddressed, Greptile
findings unanswered, and no approval gate installed.

The deadlock

.claude/workflows/oversee-dispatch.js runs the plan side as one pipeline():

Stage 1 planner -> Stage 2 critic/revise -> Stage 3 plan-sweep

Stage 1 drops the task when the planner declines to claim:

if (!plan.claimed) {
  log(`* ${t.id}: plan already in flight (${plan.summary}) — skipping duplicate planner.`)
  throw new Error(`plan already in flight: ${t.id}`)
}

planPrompt correctly tells the planner to abort when an open [Plan] PR exists, so it never
opens a competing plan — that part is right. But the abort throws, which drops the item and
skips its remaining stages.

Consequence: once the [Plan] PR exists, Stage 2 and Stage 3 become permanently unreachable
for that task. Any re-invocation re-runs the planner, the planner aborts, the task drops. There is
no supported way to run revise or sweep against an existing plan PR.

Observed sequence

Run wf_61334660-456 (phase: "plan", task issue-2088):

  1. Planner completed — opened PR [Plan] issue-2088: receiver-correlated invoked-property evidence for object-literal value-refs #2612 with docs/plans/issue-2088.md.
  2. Critic completed — REJECT, rejectClass: "fixable", three blocking findings (one real
    soundness hole plus two fabricated code citations, all independently verified).
  3. Revise agent started and died before pushing anything. The plan branch still has exactly one
    commit, the planner's original.

Greptile then posted two unresolved findings on the PR. The sweep stage that would answer them
never ran, and the skill's gate install (which happens after the engine returns) never ran either.

Every subsequent attempt re-ran the planner, which aborted with
pipeline[0] failed: plan already in flight: issue-2088, dispatched: 0, dropped: 1.

The only way to make progress was to hand-patch a session-local copy of the script, replacing
Stage 1 with the planner result already recorded in the journal. That should not be necessary.

Two compounding resume defects

(a) A resume without args is a silent no-op reported as success.
Workflow({scriptPath, resumeFromRunId}) with no args returned:

{"phase":null,"dispatched":0,"dropped":0,"results":[]}

status completed, 0 agents, 12ms. args is a separate input from the resumed run's state, so
phase was undefined and every branch was skipped. Nothing warned that the resumed run had been
launched with no task. A resume that reconstructs nothing should fail loudly, not report success.

(b) The resume cache missed because the transcript directory moved.
The original run wrote to:

...\projects\H--Vscode-codegraph--claude-worktrees-oversee-2088-main\<session>\subagents\workflows\wf_61334660-456\

After a session restart the project key reverted to the launch directory, so the resume read:

...\projects\H--Vscode-codegraph--claude-worktrees-oversee-2088-df094e\<session>\subagents\workflows\wf_61334660-456\

That directory held no prior journal, so nothing replayed and the planner re-ran live (57k tokens)
only to abort. The transcript path is keyed on the session's project directory rather than on the
run id, so resumeFromRunId silently loses its cache whenever the session's cwd/project key
changes — exactly the crash-and-restart case resume exists for.

Suggested fixes

  1. Make the plan pipeline re-enterable. Either:

    • have the planner return claimed: false plus the existing planPR / planRef it found,
      and let Stage 1 pass that through instead of throwing — the critic and sweep then run against
      the in-flight plan; or
    • add an explicit phase: "plan-resume" that takes {planPR, planRef} and starts at Stage 2.

    Option 1 is preferable: it needs no new caller contract and makes a re-invocation naturally
    idempotent — re-running /oversee #<issue> would converge an existing plan rather than dropping
    it.

  2. Fail loudly on an argless resume — if phase is absent, error instead of returning a
    success-shaped no-op.

  3. Key the workflow transcript directory on the run id, not on the session project directory,
    so resumeFromRunId survives a session restart.

Related

The same run also produced #2613 (planner pre-seeds a look-alike approval checkbox).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions