From 75dc1fc15a983bf042b532af221db4619168ca43 Mon Sep 17 00:00:00 2001 From: Timmy Date: Wed, 13 May 2026 20:21:48 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20MergeFailureFinal=20=E2=86=92=20Coding?= =?UTF-8?q?=20via=20operator=20FixupRequested?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MergeFailureFinal was unreachable from move_story: the only transitions out were Freeze (→ Frozen) and a self-loop on MergemasterAttempted, so once mergemaster exhausted its 3-retry budget the only way to get a story coding again was to delete + recreate it. The respawn budget is a mergemaster bookkeeping detail, not a hard ceiling. A human operator inspecting a Final story can reasonably decide the gate failure is fixable, so this adds the same FixupRequested → Coding edge that already exists for plain MergeFailure. Co-Authored-By: Claude Opus 4.7 (1M context) --- server/src/agents/lifecycle.rs | 3 +++ server/src/pipeline_state/transition.rs | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/server/src/agents/lifecycle.rs b/server/src/agents/lifecycle.rs index 93a08a49..0e42e2f2 100644 --- a/server/src/agents/lifecycle.rs +++ b/server/src/agents/lifecycle.rs @@ -411,6 +411,9 @@ fn map_stage_move_to_event( (Stage::Merge { .. }, "current") => Ok(PipelineEvent::MergeAborted), // Story 971: send MergeFailure story back to Coding so a coder can fix it. (Stage::MergeFailure { .. }, "current") => Ok(PipelineEvent::FixupRequested), + // Operator override on the exhausted-respawn terminal state: still + // a coder fixup, but reached via the budget-exhausted path. + (Stage::MergeFailureFinal { .. }, "current") => Ok(PipelineEvent::FixupRequested), // Story 972: send MergeFailure story back to Qa for a QA agent to re-review. (Stage::MergeFailure { .. }, "qa") => Ok(PipelineEvent::ReQueuedForQa), // Story 974: reopen a Done story for a post-merge hotfix. diff --git a/server/src/pipeline_state/transition.rs b/server/src/pipeline_state/transition.rs index a956147d..a2194291 100644 --- a/server/src/pipeline_state/transition.rs +++ b/server/src/pipeline_state/transition.rs @@ -307,6 +307,15 @@ pub fn transition(state: Stage, event: PipelineEvent) -> Result Ok(Coding), + // ── FixupRequested: MergeFailureFinal → Coding (operator override) + // + // The exhausted-respawn-budget terminal state is not actually + // terminal as far as the operator is concerned; a human can decide + // the gate failure is fixable and send the story back for another + // coder attempt. The budget counter is a mergemaster bookkeeping + // detail, not a hard ceiling. + (MergeFailureFinal { .. }, FixupRequested) => Ok(Coding), + // ── ReQueuedForQa: MergeFailure → Qa (re-review) ──────────────── (MergeFailure { .. }, ReQueuedForQa) => Ok(Qa),