State management and deterministic transitions

The workflow never asks the model to guess what happens next. A schema-validated variable bag holds the state; an activity's exits are tested against it in declaration order; the first condition that holds wins. Conversational context plays no part in routing. Exits, 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.md.

Exit evaluation When an activity completes, the worker reads the variable bag and tests the activity's exits in order. The first exit whose condition holds, or else the default exit, is taken; the worker reads its destination from exit destinations, and the orchestrator calls next underscore activity with that identifier and the exit. 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 worker tests exits in declaration order first true condition or the default exit next_activity(id) variable bag schema-validated state server persistence session.json + .session-token atomic writes, seal verified read mutations: checkpoint effects · worker outputs
Exit evaluation. The worker reads the variable bag when an activity completes, tests the exits in declaration order, and takes the first whose condition holds (or the default); the graph names the next activity. Checkpoint effects and worker outputs are what agents write; the server also writes a fan's container, and on resume the new request and missing defaults. The server persists every change atomically.

The variable bag

A workflow declares the facts a session starts with in workflow.yaml — name, type (string, number, boolean, array, object), and optionally a default — and each activity declares the variables it writes under variables.writes. The session's state dictionary is seeded from these declarations when the session opens, together with user_request when one is passed and, from a working directory, the repository facts host_repo_path, target_repo, component_path and is_monorepo.

Agents write it in two ways 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 server writes two things of its own: the container a fan fills, when the fan opens, and on resume the new request and any declared default the bag lacks.

The rule of determinism

An activity declares its outcomes as exits, and the workflow's graph binds each exit to a destination. When the activity completes, the worker tests its exits in order against the current variables, takes the first whose when holds, and otherwise takes the one marked isDefault. An exit's when is the inline gate expression a step uses: comparisons with ==, !=, >, <, >= and <=, bare identifier truthiness, unary !, and && / || with parentheses. Nobody asks you or the model what to do next: the worker reads the destination from its exit_destinations, and the orchestrator calls next_activity with that id and the exit taken. A checkpoint option's exit effect names an exit outright.

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 exit conditions 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 JSON object keyed by the output id the bound operation declares, one output included (e.g. {"repo_root": "lib/x", "component_name": "x"}). The server validates warn-only: every ungated top-level step present, declaration order preserved, non-empty outputs, and every reported key an id the step's operation declares. 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 call that changes the session — agents only ever pass their 6-character session_index. Two files live in the planning folder under the engineering root: 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 server key. Writes are atomic (write-temp, then rename) and ordered; reads verify the seal and any mismatch is a hard SessionStoreError with code SEAL_MISMATCH.

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 folder's absolute path, and server restarts are transparent.

The planning folder

A session opens one planning folder under the engineering root. The code change stays in the workspace. The notes of the run — the progress README, the session files, and the documents an activity produces — stay in that folder. Where the folder sits is the workspace project layout.

The README carries a Progress table. The orchestrator marks a row in progress before it hands the activity to a worker, and complete once that activity's work is committed. The worker reports the documents it produced and does not edit the table. The table still advances when a worker is lost and replaced. How each document is named is in the worker bundle.

Checkpoint effects are defined in the checkpoints. The enforcement that keeps reported exits honest is fidelity.