Turns, retries and hand-off
What happens from dispatch to hand-off, how failures are retried, and how to stop, retry or resume a task by hand.
A task's run follows the Symphony state machine: dispatch, turns, then either a hand-off, a terminal state, or a retry. SI Fabrik adds a graceful hand-off drain, stall detection and guardrails on top.
From dispatch to hand-off#
- Dispatch
On each poll, eligible tasks are sorted by priority, then age, then identifier, and dispatched while slots are free:
agent.max_concurrent_agents, any per-state cap inagent.max_concurrent_agents_by_state, and the host-wide Max agents running at once. Before dispatch, a preflight checks credentials, the gateway, links, Docker or the Cloudflare bridge; a failing preflight blocks the whole project with a reason in its header. - Prepare
The workspace is created or reused, the task branch checked out,
origin/<base>refreshed, andbefore_runrun. Linked services and the app preview start. The prompt is rendered. - Turns
The first turn gets the rendered prompt. After each turn, SI Fabrik refreshes the task from the tracker. While it's still active and under
agent.max_turns, the next turn starts with a short continuation message. Budgets are checked before every turn. - After the attempt
after_runruns, screenshots are collected, and the branch is saved into the project repository. If the task is still active, a continuation check runs a second later to start the next attempt.
Hand-off and terminal states#
Reconciliation runs on every poll and compares running tasks with the tracker:
| The task moved to | SI Fabrik |
|---|---|
| A hand-off state (not active, not terminal), such as Human Review | Lets the in-flight turn finish so its summary and usage are kept, starts no more turns, and force-stops the run after 90 seconds. Then it pushes the branch and opens or updates the pull request. The card shows Handing off. |
| A terminal state, such as Done or Canceled | Stops the run immediately, saves the branch and removes the workspace. |
| Gone from the tracker | Stops the run. |
Failures and retries#
An attempt fails when the agent process exits with an error, a turn fails, a hook fails, the silence timeout is hit, or the run stalls. Failed attempts are retried with exponential backoff:
| Failed attempt | Delay |
|---|---|
| 1st | 10 s |
| 2nd | 20 s |
| 3rd | 40 s |
| nth | 10 s × 2ⁿ⁻¹, up to agent.max_retry_backoff_ms (default 5 minutes) |
A retry that finds no free slot is re-queued without counting as a real failure.
Two watchdogs apply to both runners:
- Silence timeout (
codex.turn_timeout_ms, default 1 hour): a turn with no output for this long fails. - Stall detection (
codex.stall_timeout_ms, default 5 minutes,0to turn off): a run with no agent activity for this long is stopped and retried with outcomestalled.
Without guardrails, the spec retries forever. SI Fabrik parks a task after limits.max_attempts_per_issue failures in a row (default 5), and pauses a project after limits.pause_after_consecutive_failures failures in a row across tasks (default 5). See Parking, pausing and credential errors.
Run outcomes#
Every attempt is recorded in Runs with one of these outcomes:
| Outcome | Meaning |
|---|---|
succeeded | The attempt finished normally |
failed | The agent, a turn or a hook failed |
timedOut | The silence timeout was hit |
stalled | No activity for stall_timeout_ms |
canceled | Stopped by you, by a terminal state, or by a hand-off drain |
Act on a task by hand#
| You want to | Do |
|---|---|
| Start a task now | Board → right-click → Dispatch Now |
| Stop a running agent | Stop in Live Agents, or Stop Agent on the card |
| Retry without waiting for the backoff | Retry now in Mission Control's retry queue |
| Give up the claim on a queued retry | The × next to it in the retry queue |
| Resume a parked task | Resume Retries on the card or in the project banner |
| Resume a paused project | Resume in the project header |
| Stop dispatching but let agents finish | Pause the project (the pause button in its header) |
| Stop everything in a project | Stop in its header |
Restarts#
SI Fabrik recovers from the tracker and the filesystem: on startup it removes workspaces of tasks already in a terminal state, and active tasks are dispatched again on the first poll. Spend, parked tasks and automatic pauses are kept across restarts, so a crash loop can't start burning tokens again on its own.