Problem
There is no supported way to re-run part of a completed run. The case: a run reaches done, and an operator needs to regenerate one downstream artifact, plus everything after it, without re-running upstream stations that were already approved. For example, re-run an image generation station against an approved brief because the image was rejected outside the flow, or because a prompt change should apply to that stage only.
Four mechanisms treat a completed card as final:
- The FSM has no transition out of a terminal state.
INTEGRITY_PASS on the last station produces { lane: 'done', status: 'complete' } (src/statemachine/transitions.ts:142), and no event is legal from there. This is a deliberate invariant.
- Resume never selects a terminal run or card. Run selection excludes finished runs,
WHERE status NOT IN ('done', 'complete', 'terminal', 'scrapped') (src/cli/main.ts:1371). The promote step only re-readies waiting and interrupted cards in non-terminal lanes (src/controller/executor.ts:698).
- Checkpoints would return stale outputs. A naive re-run from a station would find the downstream transform and gate checkpoints intact and skip them, reusing exactly the artifacts meant to be regenerated.
- Effectful stations would fire twice. The outbox key is
${flow.version}:${cardId}:${stationId}:${card.attempt} (src/controller/executor.ts:2099). Re-entry must advance attempt so that a re-fire is a new intent rather than a pending one that reconcileOnResume escalates to hold.
Proposed mechanism
A separate verb, so that an ordinary resume never takes the terminal path:
conduit reenter <flow.yaml> --run <id> --from <station> [--card <id>]
Within the run lease:
- Find the target cards: the run's
done cards by default, or the one named by --card. Fail if the run is not terminal or --from is not a station on the card's path.
- Reject a fan-out parent or child-lane entry point in v1.
- Add one FSM event,
REENTER { station }, legal only from a terminal complete state. It produces { lane: station, status: 'waiting', attempt: attempt + 1 } and leaves reworkCount unchanged.
- Invalidate checkpoints from
--from to the end of the path. Stations before --from keep theirs.
- Set the
runs row back to running and drive the engine as resume does.
- Write a journal entry for the re-entry (from which station, and when), separate from an ordinary attempt increment.
Open questions
- Should invalidation cover
--from to the end of the flow (safe, but those stations are billed again) or an explicit range?
- Should re-entry across an
effectful: true station be refused unless a flag such as --allow-effectful-refire is passed? The recommendation is to refuse by default.
- Is single-lane re-entry enough for v1?
Acceptance criteria
- A test for re-entering at a mid-flow station: stations before it are skipped from checkpoint, stations after it re-execute, and the run ends
done with the attempt advanced.
- Re-entry across an effectful station without the opt-in flag is refused.
- A test that verifies an effectful re-fire gets a new outbox key.
Problem
There is no supported way to re-run part of a completed run. The case: a run reaches
done, and an operator needs to regenerate one downstream artifact, plus everything after it, without re-running upstream stations that were already approved. For example, re-run an image generation station against an approved brief because the image was rejected outside the flow, or because a prompt change should apply to that stage only.Four mechanisms treat a completed card as final:
INTEGRITY_PASSon the last station produces{ lane: 'done', status: 'complete' }(src/statemachine/transitions.ts:142), and no event is legal from there. This is a deliberate invariant.WHERE status NOT IN ('done', 'complete', 'terminal', 'scrapped')(src/cli/main.ts:1371). The promote step only re-readieswaitingandinterruptedcards in non-terminal lanes (src/controller/executor.ts:698).${flow.version}:${cardId}:${stationId}:${card.attempt}(src/controller/executor.ts:2099). Re-entry must advanceattemptso that a re-fire is a new intent rather than a pending one thatreconcileOnResumeescalates to hold.Proposed mechanism
A separate verb, so that an ordinary
resumenever takes the terminal path:Within the run lease:
donecards by default, or the one named by--card. Fail if the run is not terminal or--fromis not a station on the card's path.REENTER { station }, legal only from a terminalcompletestate. It produces{ lane: station, status: 'waiting', attempt: attempt + 1 }and leavesreworkCountunchanged.--fromto the end of the path. Stations before--fromkeep theirs.runsrow back torunningand drive the engine asresumedoes.Open questions
--fromto the end of the flow (safe, but those stations are billed again) or an explicit range?effectful: truestation be refused unless a flag such as--allow-effectful-refireis passed? The recommendation is to refuse by default.Acceptance criteria
donewith the attempt advanced.