Expand description
HTTP surface for the Task/Run persistence axis (issue #13 ID hierarchy;
GET /v1/tasks, GET /v1/tasks/:id, POST /v1/tasks/:id/runs,
GET /v1/runs/:id). POST /v1/tasks itself stays in this module (it is
the entry point tasks_start shares with the flow-eval path) — see the
tasks module doc for the split rationale.
HTTP surface for the Task/Run persistence axis (issue #13 ID-hierarchy
reconciliation: Blueprint → Task → Run → Step → Attempt).
GET /v1/tasks— list every persistedTaskRecord, newest first.GET /v1/tasks/:id— aTaskRecordplus everyRunRecordkicked from it.POST /v1/tasks/:id/runs— re-kick an existing Task: mints a freshRunId, re-resolves the storedblueprint_ref(refreshingBlueprint.default_init_ctxexactly like original launch time — issue #19 ST4), 3-layer-merges it withTaskRecord.input_ctxand an optionalRunKickRequestbody’sinit_ctx_override(see [merge_init_ctx_3layer]), dispatches throughTaskApplication::handle_with_run, and returns the new{task_id, run_id}pair. A body-less request (or one that omits both fields) preserves the pre-#19 rekick behavior byte-for-byte.GET /v1/runs/:id— a singleRunRecord(step_entriestrace included).GET /v1/runs/:id/bindings— requested/effective binding explain from the immutable launch snapshot (never from the current Blueprint).POST /v1/runs/:id/resume— resume anInterruptedRun under the SAMErun_id(replay cursor + stored launch-input snapshot).POST /v1/runs/:id/rerun-from— GH #71 Layer A. Rerun a terminal Run (Done/Failed/Interrupted) from a caller-specified step under the SAMErun_id; physically truncates the replay log at the cut point so re-dispatch does not collide with the pre-rerun rows. See [run_rerun_from] for the full contract + Known Limitations.
POST /v1/tasks itself (the flow-eval entry point, tasks_start /
run_flow_form) stays in crate::lib — it is the pre-existing
Operator-inject-aware dispatch path this module’s handlers re-kick
through, not a new one. This module owns the read/list/re-kick surface
plus the [finalize_run] persistence helper both paths share.
Authorization follows the same convention as the existing POST /v1/tasks
entry: no Authorization header is required (the route is open), and the
only Operator-session correlation available is the request-body-level
operator_sid (see crate::TaskLaunchRequest doc) — this module invents no
new auth mechanism.
Structs§
- RunBinding
Difference - Mechanical requested/effective comparison for one immutable binding.
- RunBinding
Explain Entry - Explain view for one agent, derived exclusively from the persisted Run snapshot rather than from the current Blueprint registry.
- RunBindings
Explain Response - Response body for
GET /v1/runs/:id/bindings. - RunKick
Request - Request body for
POST /v1/tasks/:id/runs(issue #19 ST4) — every field is optional, and the body itself is optional (seetask_rekick’sOption<Json<Self>>parameter); a caller that sends no body, or{}, or omits a field gets exactly today’s rekick behavior for that layer. - RunKick
Response - Response body for
POST /v1/tasks/:id/runs. - RunRerun
From Request - Request body for
POST /v1/runs/:id/rerun-from(GH #71 Layer A). - RunRerun
From Response - Response body for
POST /v1/runs/:id/rerun-from(GH #71 Layer A). - RunResume
Response - Response body for
POST /v1/runs/:id/resume. - RunSteps
Response - Response body for
GET /v1/runs/:id/steps. - RunTrace
Query - Query params for
GET /v1/runs/:id/trace— seemlua_swarm::store::trace::TraceQueryfor semantics (latestwins overafter;kindentries are comma-separated prefix matches). - RunTrace
Response - Response body for
GET /v1/runs/:id/trace. - Runs
List Query - Query params for
GET /v1/runs(the Run collection read). - Runs
List Response - Response body for
GET /v1/runs. - Task
Detail Response - Response body for
GET /v1/tasks/:id. - Tasks
List Query - Query params for
GET /v1/tasks.
Enums§
- RunBinding
Status - Whether a Run-scoped binding has only a declaration or also carries a provider attestation accepted by Core.
Functions§
- run_
bindings_ explain GET /v1/runs/:id/bindings. Explains the exact immutable agent bindings used by this Run. The handler never reads or resolves the current Blueprint; old Runs without a binding snapshot return422instead of guessed state.- run_
cancel POST /v1/runs/:id/cancel— record a cancel request on the Run’s trace stream (core.cancel_requested) and mark the Run’s status toCancelledfor still-in-flight rows. Idempotent: repeat calls re-append the trace event but keep the status setter idempotent on the store side. In-flight abort itself remains a v3 carry — the current effect is observational + status marker, matching theswarm_cancelMCP tool’s local semantics but reflected onto the server-sideRunTraceStoresoGET /v1/runs/:id/tracereflects it too.- run_
delete DELETE /v1/runs/:id— retention prune: deletes the Run row and its trace stream together (404when the Run row is absent; the trace stream is pruned best-effort either way). Replay rows are untouched —ReplayStorehas its own truncation semantics owned by the rerun-from path.- run_get
GET /v1/runs/:id. Returns a singleRunRecord(itsstep_entriestrace included).- run_
rerun_ from 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 SAMErun_id. Mirrorsrun_resume, with two deltas: it accepts any terminal status (Done/Failed/Interrupted) rather than onlyInterrupted, and it physically truncates the replay log at the cut point (viacrate::AppState::replay_store’sdelete_from) so that re-dispatch’sappenddoes not collide with the pre-rerun row and solist_by_runreflects the rerun’s real history rather than the pre-rerun ghost.- run_
resume POST /v1/runs/:id/resume. Resumes anInterruptedRun under the SAMErun_id(no newRunIdis minted): the stored launch-input snapshot (RunRecord.input_json) is rebuilt into aTaskApplicationInput, aReplayCursoris built from the Run’s logged step snapshots (ReplayStore::list_by_run), and the flow is re-dispatched with both wired into a freshRunContext. On dispatch the engine’s replay path returns each already-completed step’s stored value verbatim (cursor hit, no Adapter spawn) and dispatches only the steps that never finished — reconstructing the same final Ctx a restart-free run would have reached.- run_
steps GET /v1/runs/:id/steps— the Run’s terminal per-step stats (StepEntrylist) as a standalone sub-resource. Same dataGET /v1/runs/:idembeds; split out so stats consumers don’t drag the full RunRecord (launch snapshot etc.) per poll.- run_
trace GET /v1/runs/:id/trace?after=&limit=&latest=&kind=&step=&attempt=— the Run’s TraceEvent stream (the RunTrace rail). Note the trace rail is deliberately uncoupled fromRunStore(a trace can outlive or precede its Run row), so an unknown Run id returns an empty list, not 404.- runs_
list GET /v1/runs?task_id=&status=&limit=&offset=— filtered Run collection, newest-first. The collection read that was missing from the Run CRUD surface (onlyGET /v1/runs/:idexisted before the per-step run stats work).- task_
get GET /v1/tasks/:id. Returns theTaskRecordplus everyRunRecordkicked from it (RunStore::list_by_task, oldest kick first).- task_
rekick POST /v1/tasks/:id/runs. Re-kicks an existing Task: reads its storedblueprint_ref, re-resolves it through [TaskApplication::resolve] (issue #19 ST4 — refreshesBlueprint.default_init_ctxexactly like original launch time, rather than replaying a launch-time-only snapshot), 3-layer-merges{bp default, TaskRecord.input_ctx, an optional per-Run override}viamerge_init_ctx_3layer, resolves the Task-level canonical fields (RunKickRequest.task_input_override, falling back toTaskRecord.task_input_spec), mints a freshRunId, dispatches throughTaskApplication::handle_with_run(Operator-default unless the caller pins a live session viaRunKickRequest.operator_sid— the rekick parity forPOST /v1/tasks’ ownoperator_sid; the stored Task carries no persisted Operator preference of its own) plus a freshly-builtRunContext(issue #13 run_id propagation, so this kick’s steps get their ownstep_entriestrace), and persists the outcome via [finalize_run].- tasks_
list GET /v1/tasks?limit=N. Lists every persistedTaskRecord, newest first.