Skip to main content

run_rerun_from

Function run_rerun_from 

Source
pub async fn run_rerun_from(
    __arg0: State<AppState>,
    __arg1: Path<String>,
    __arg2: Json<RunRerunFromRequest>,
) -> Result<(StatusCode, Json<RunRerunFromResponse>), ApiError>
Expand description

POST /v1/runs/:id/rerun-from — GH #71 Layer A. Re-executes a specific step (and every downstream step) of a terminal Run under the SAME run_id. Mirrors run_resume, with two deltas: it accepts any terminal status (Done / Failed / Interrupted) rather than only Interrupted, and it physically truncates the replay log at the cut point (via crate::AppState::replay_store’s delete_from) so that re-dispatch’s append does not collide with the pre-rerun row and so list_by_run reflects the rerun’s real history rather than the pre-rerun ghost.

§Status codes

  • 400 — invalid run_id, malformed body, or launch-snapshot decode failure.
  • 404 — no Run with this id.
  • 409 — the Run is Running / Pending (would race the in-flight driver), OR a concurrent transition won the compare-and-set.
  • 422 — the Run has no recorded launch-input snapshot, OR from_step is not present in this Run’s replay log, OR the run’s replay log is empty next to a non-empty RunRecord.step_entries trace (a prior rerun-from reached the truncate stage and consumed the log), OR the current-head Blueprint fails to compile (unresolved operator_ref etc.) — the deterministic pre-flight gate that keeps the replay log untouched on a compile-fail.
  • 202 Accepted — accepted; the flow re-runs in a detached background task (same tokio::spawn + run-TTL ceiling shape as run_resume). Poll GET /v1/runs/:id for the terminal status.

§Order of operations

The compare-and-set runs BEFORE the delete_from on purpose: a losing cas returns 409 without ever touching the store, so a lost race can never leave the store truncated while the status stayed at its old terminal value. The compile pre-check runs BEFORE the compare-and-set for the same reason: a deterministic compile failure fires a 422 that leaves both status and the replay log untouched, so the caller can fix the Blueprint and retry against the same run.

  1. 404 check.
  2. Status gate (fast 409 for Running / Pending).
  3. Decode launch snapshot (fast 400 / 422).
  4. Compute cut index via list_by_run + .position(step_ref == from_step) (fast 422 when the step is not present, with a distinct message when the log is empty but RunRecord.step_entries shows the run did trace steps — a consumed log from a prior rerun-from).
  5. Pre-flight compile check via TaskApplication::precompile against the launch snapshot’s Blueprint (fast 422 on any CompileError). Prevents compile-fail-inside-tokio::spawn from consuming the replay log via step 7’s delete_from.
  6. Atomic transition <current terminal> -> Running (409 on loss).
  7. Physical delete_from(cut) on the replay store — safe now because we won the cas and own the Run.
  8. Build ReplayCursor from the truncated entries.
  9. Detached dispatch, same tokio::spawn + default_run_ttl shape as run_resume.

§Known limitations (Layer A)

  1. from_step is a raw step_ref (agent name) — projection alias resolution via StepNaming is Layer B territory. For undeclared steps step_ref == canonical so this is only visible when AgentMeta.projection_name is in use.
  2. BlueprintRef::Inline freezes the BP in the launch snapshot — the rerun re-decodes the same inline BP, so agent-definition edits landed on disk between the original dispatch and the rerun are NOT honored for inline runs. Use BlueprintRef::Id for the iterate-and-rerun workflow.
  3. Loop bodies match the first occurrencestep_ref is the agent name, so .position(|e| e.step_ref == from_step) finds the FIRST occurrence and truncates from there. Rerunning a specific loop iteration needs Layer B semantics.
  4. Structural BP change is out of scope — if steps were added / removed / reordered between the original dispatch and the rerun, the flow-ir re-eval will naturally miss the step or dispatch a different downstream. Start a fresh run in that case.