State management and deterministic transitions

The workflow never asks the model to guess what happens next. A schema-validated variable bag holds the state; transitions are evaluated against it in declaration order; the first condition that matches wins. Conversational context plays no part in routing. Transitions, artifact contracts, binding provenance, and delivery payloads are all computed from definitions and state — same inputs, same path, with model judgment reserved for the work itself.

Canonical source: docs/state_management_model.md.

Transition evaluation When an activity completes, the orchestrator reads the variable bag and evaluates the activity's transitions in order. The first condition that evaluates to true selects the next activity, and next underscore activity is called with that identifier. The variable bag is mutated by checkpoint effects and worker outputs, and persisted by the server in session dot json with a sealed token file. activity completes evaluate transitions in declaration order first true condition or the default transition next_activity(id) variable bag schema-validated state server persistence session.json + .session-token atomic writes, seal verified read mutations: checkpoint effects · worker outputs
Transition evaluation. The variable bag is read when an activity completes; transitions are checked in declaration order and the first matching condition (or the default) names the next activity. Checkpoint effects and worker outputs are the only mutation paths, and the server persists every change atomically.

The variable bag

Every workflow declares its variables in workflow.yaml — name, type (string, number, boolean, array, object), and optionally a default. The session's state dictionary is initialized from these definitions at bootstrap.

Two things mutate it during a run: checkpoint effects (your choice at a checkpoint carries a declared setVariable effect) and worker outputs (an activity worker returns variables it determined from its domain work, like has_critical_bugs: true after a failing test run).

The rule of determinism

When an activity completes, the orchestrator consults the activity's transitions array. Conditions are structured objects — simple comparisons (==, !=, >, <, >=, <=, exists, notExists) composable with and, or, and not. The orchestrator must not ask you or the model what to do next: it evaluates conditions in array order against the current variables, and the first true names the next activity. Transitions can also come from decision branches and checkpoint-option effects.

Path variation works through ordinary state: an activation variable set early (by a detection step or a checkpoint) marks a variant — review mode, monorepo handling — and conditional transitions and step gates branch on it for the rest of the run.

Step completion manifest

When an activity ends, the orchestrator passes a step_manifest to next_activity — a structured summary of completed steps from the activity just finished. Each entry names a step_id and an output: a short summary string for a single-output step, or a JSON object keyed by output id when the step declares more than one output (e.g. {"reference_path": "lib/x", "component_name": "x"}). The server validates warn-only: every ungated top-level step present, declaration order preserved, non-empty outputs. Steps gated by when or condition may be omitted; loop-body step ids are accepted but never required.

The manifest is cross-checked against fetch observability: a manifested technique step with no technique_fetched or technique_bundled event during the activity visit draws a warning — the step was reported complete but its composed technique was never loaded. See Protocol — warnings.

Binding provenance

Where each technique input comes from and where each output lands is computed statically from the workflow definition — no session-state reads, byte-identical refetches stay byte-identical. A step-bound get_technique fetch annotates each of the technique's own inputs with a source: line (step-binding value, workflow variable, prior step output, declared default, or UNRESOLVED) and remapped outputs with a destination: line. Contract-inherited inputs are annotated only when the resolution says something the block note does not. For borrowed activities, producer scans resolve bound ops against the activity's source workflow. The same convention is enforced offline by check-binding-fidelity.

Server-managed persistence

The server owns the canonical state and writes it on every authenticated call — agents only ever pass their 6-character session_index. Two files live in the planning folder: session.json, the human-inspectable, schema-validated state (see the session-file schema), and .session-token, an HMAC-signed seal binding that state to the workspace and server key. Writes are atomic (write-temp, then rename) and ordered; reads verify the seal and any mismatch is a hard SealMismatchError.

Because state lives on disk rather than in the agent, a session can be paused, killed, or resumed at any point: resume is one start_session call with the planning slug, and server restarts are transparent.

Checkpoint effects are defined in the checkpoint model; how the state files sit inside the planning folder is part of artifact and workspace isolation; the enforcement that keeps reported transitions honest is workflow fidelity.