Walkthrough

How it works

Follow a single task from the moment a user files it, through workflow dispatch, session boot, the turn phase machine, and the trust boundary that decides what reaches your repository.

~/dev/myrepo trace
$ praetor task add "fix the flaky integration test" --deps lint

task      t_01HX  ·  waiting on  lint
lint      done
task      t_01HX  ready
dispatch  session-02 (idle)    t_01HX
boot      phases 1..7 (reject)  ·  phase 8 install  ·  9..10 hooks
turn      accept  prepare  preflight  stream  complete  exec  persist  finalize
commit    reviewer imports, verifies, commits a3f1c89
The task

You file work, not sessions.

The user-facing primitive is the task, not the session. You describe what needs doing and declare any dependencies (tests must lint, deploy must build). The runtime stores the task in a graph alongside everything else queued, in flight, or done.

Sessions exist, but you do not open them by hand. Filing a task is closer to submitting a job than opening a tab.

The workflow

A scheduler watches the graph.

A workflow layer sits above the engine. It scans the task graph for work whose dependencies are satisfied, finds an idle session, picks an agent for it (Claude Code, Codex, your own), and hands the task in.

This is the layer that turns "many sessions" from a tab manager into a job scheduler. From the outside, the user files tasks and watches them tick toward done. From the inside, autonomous routing keeps idle workers busy.

The coordinator

One router, four channels.

Inside the engine, the coordinator is a thin central router. It owns four inbound channels and a single select! dispatches across them:

  1. control_rx, lifecycle
  2. command_rx, in-flight commands
  3. message_rx, streamed model output
  4. submit_rx, new turn submissions

The split is load-bearing. command_rx must keep working during streaming so a turn can be cancelled mid-flight. submit_rx is gated to coordinator-idle so a new turn cannot start while another is mid-stream. The coordinator never runs turns itself, which is exactly what enforces that turns are never reentrant.

The session

An execution context that boots.

Each session is a self-contained execution context: its own lifecycle state machine (idle, active, cancelling, shutting-down), its own scripting environment, its own configuration, its own conversation history, its own agent. The scripting environment is not safe to move between executors, so sessions are pinned to their worker.

Boot is staged. Cold start and hot reload walk the same ten-phase pipeline:

1.  bootstrap
2.  prompt JS
3.  stdlib
4.  repo root global
5.  config files
6.  extract typed config
7.  prompt pre-activation
8.  install effects   ← effects-live boundary
9.  startup hooks
10. prompt post-activation

Phases 1 through 7 run with the effects bridge in reject mode: scripts can register handlers and declare intent, but no real side effects fire. A broken configuration cannot half-write to the world, because nothing real fires before phase 8.

The turn

An explicit phase machine.

Once a session is booted and the coordinator dispatches a submit, a turn runs. The turn is an explicit phase machine, not a free-running loop. Each phase has a name, so progress is observable, failures land at known boundaries, and recovery is deterministic.

  1. accept input
  2. prepare
  3. preflight
  4. stream from model
  5. handle completion
  6. execute script blocks
  7. recover from overflow
  8. persist
  9. finalize

Overflow triggers compact-and-retry. Cancellation interrupts at the next phase boundary, never in the middle of unknown state. One turn per session at a time; additional submits queue.

The effects bridge

Intent and execution, separated.

Configuration is code. The behavior of the system is shaped by scripts the user writes: hooks, prompt builders, tool definitions, workflow logic. Those scripts run inside the session's scripting environment with access to a host bridge.

The bridge separates intent from execution. A script says "read this file" or "call this tool"; the host carries it out. Reject mode keeps configuration phases re-runnable without consequences. Once phase 8 installs the live bridge, declared effects start firing for real.

Shutdown

A signal, not a method drain.

Engine shutdown does not flow through the session manager as a call chain. It is a shared cancellation token that the coordinator and every session worker listen for. When the engine drops, the token fires; every component unwinds on its own.

Shutdown is a signal, not a method. That keeps the failure mode simple: nothing has to coordinate, nothing has to chain, nothing can deadlock waiting for a peer to acknowledge.

Trust

Reviewer and executor.

Two roles, two boundaries. The trusted host is the reviewer and Git owner. It pulls, commits, merges, pushes, and reads executor reports as evidence, not authority. The untrusted executor edits files and runs checks inside a sandbox. It never receives credentials, never pushes, never modifies remotes or hooks.

Changes flow trust-ward through review, not the other way. An agent can do an enormous amount of useful work without ever holding the keys to the kingdom, which is the point.

File a task. The runtime decides which session runs it, walks the phase machine, surfaces the work for review, and only then does anything reach your repository.

For the topology and load-bearing invariants, see Architecture. For why the boundaries are shaped this way, see Manifesto.