Artifact versioning & backtrack

Every phase of a running project promotes its producer's output to a pipeline artifact — a versioned, append-only record. This page covers the version model and the three backtrack levers built on it: retrigger, revert, and edit sessions.

The version model

An artifact row carries phase_slug, version (1, 2, 3, …), schema_slug, the content, provenance (produced_by_subtask_id, produced_by_attempt_id, produced_via — the attempt reason), and supersession pointers:

  • Exactly one version per phase is current (superseded: false); the rest are superseded: true, linked via supersedes_artifact_id / superseded_by_artifact_id.
  • Versions are never deleted or rewritten. Re-running a phase appends the next version; reverting flips the current pointer.

GET /v1/projects/{id}/artifacts?current_only=true (the default) returns just each phase's current version; current_only=false returns the full history. The portal's project page renders one card per phase with a version bar across the history.

Stale phases

When a phase's artifact changes (retrigger or revert), downstream phases that consumed the old version are out of date. What happens to them is the cascade mode:

Mode Downstream phases
lazy (default) Marked stale — visible, not re-run. You decide what to re-run and when.
strict Auto-invalidated and auto-retriggered.
none Untouched — pinned against the old version.
default Each phase's own cascade_on_upstream_change template setting.

Stale is a first-class task status. The portal shows an amber Stale badge on affected phases (tasks table, task page, artifact card) and a Has stale phases chip in the project header, mirrored by the project's has_stale_phases field. A stale phase is terminal — retrigger it when you're ready.

Retrigger a phase

Portal: the Retrigger button on a phase's artifact card or task page (shown only for terminal phases). REST:

POST /v1/projects/{project_id}/phases/{phase_slug}/retrigger
{ "addendum_brief": "Tighten the hook in scene 1.", "cascade": "lazy" }

Both body fields are optional. The addendum brief is threaded into the re-run's prompt on top of the original instructions; the new attempt lands with attempt_reason: cascade_retrigger (or manual_retry/manual_rework on the retry paths) and its output becomes the phase's next artifact version.

Only terminal phases (succeeded / failed / skipped / stale) can be retriggered — a pending / running / blocked / review phase is already queued or mid-flight, and resetting it would stomp in-flight attempts. The API answers 409 in that case. The response lists affected_phase_slugs, marked_stale, auto_retriggered, and a history-based cost_estimate_usd.

Revert an artifact

Portal: Revert to this version in the artifact card's version bar (on superseded versions). REST:

POST /v1/projects/{project_id}/phases/{phase_slug}/revert-artifact
{ "target_version": 2, "cascade": "lazy", "reason": "v3 lost the vintage filter" }

The revert is a pointer flip, append-only: the target version becomes current again, newer versions stay in history, and downstream phases get the cascade treatment (lazy default = marked stale). Guards:

  • 409 while the phase is running / blocked / review — an in-flight promote would race the pointer flip.
  • 409 when the target version is already current.
  • 404 for an unknown version.

Edit sessions

For scope changes that span phases — “swap scene 3's location to a rooftop garden” — use an edit session instead of hand-picking retriggers. Portal: the Edit with AI button in the project header. REST:

POST /v1/projects/{project_id}/edit-sessions
{ "instruction": "Swap scene 3's location to a rooftop garden", "cascade_mode": "lazy" }

This appends an edit_session task with three steps:

  1. Planner (agent) — drafts a plan of concrete actions (retriggers, reverts) with rationale, cost and risk.

  2. approve_plan gate (human) — you review the plan on its subtask page. Decisions: approved, rejected, request_changes (send it back to the planner with a comment), or edit_plan (approve your own revised_plan object).

    POST /v1/projects/{project_id}/edit-sessions/{task_id}/approve
    { "decision": "approved" }
    
  3. Enact (system) — executes the approved plan with the session's cascade mode.

The create call returns the spawned task_id plus the planner/gate/enact subtask ids. 409 when the plan gate is already resolved; 404 when the task isn't an edit-session task of this project. The project's brief stays immutable throughout — edit sessions are the sanctioned way to change scope mid-flight.

SDK coverage

Retrigger, revert, and edit sessions are REST-only for now — the SDKs' projects namespace doesn't wrap them yet. projects.listArtifacts / list_artifacts returns current-only artifacts (the SDK doesn't pass current_only=false).

Next