# Modes and loops

Millrace compiles and executes from a frozen runtime plan built from one mode plus one loop per plane.

## The two planes

Millrace currently runs two distinct planes:

- execution
- planning

Each plane still ships with a legacy loop asset that declares its stages, entry stage, edges, and terminal results. The compiler and runtime also materialize a graph-backed `compiled_plan.json` that becomes the authoritative runtime control-flow artifact.

## Shipped loop ids

The shipped built-in loop ids are:

- `execution.standard`
- `planning.standard`

## Shipped mode ids

The shipped canonical mode ids are:

- `default_codex`
- `default_pi`

Compatibility alias:

- `standard_plain -> default_codex`

Both shipped canonical modes point at the same loop ids:

- `execution_loop_id = execution.standard`
- `planning_loop_id = planning.standard`

They differ in runner bindings, not in loop topology.

## Shipped execution loop

`execution.standard` declares these stages:

1. `builder`
2. `checker`
3. `fixer`
4. `doublechecker`
5. `updater`
6. `troubleshooter`
7. `consultant`

Its legacy loop entry stage is `builder`.

Its current terminal results are:

- `UPDATE_COMPLETE`
- `NEEDS_PLANNING`
- `BLOCKED`

The shipped execution path is not a straight line. It is a repair-capable governance loop. In the shipped graph:

- `BUILDER_COMPLETE` moves `builder -> checker`
- `FIX_NEEDED` routes `checker -> fixer` and `doublechecker -> fixer`
- successful update terminates with `UPDATE_COMPLETE`
- blocked execution routes into `troubleshooter`
- `consultant` can hand the run back into troubleshooting or terminate with `NEEDS_PLANNING` or `BLOCKED`

## Shipped planning loop

`planning.standard` declares these stages:

1. `planner`
2. `manager`
3. `mechanic`
4. `auditor`
5. `arbiter`

Its legacy loop entry stage is `planner`.

Its current terminal results are:

- `MANAGER_COMPLETE`
- `ARBITER_COMPLETE`
- `REMEDIATION_NEEDED`
- `BLOCKED`

In the shipped graph:

- `PLANNER_COMPLETE` moves `planner -> manager`
- blocked `planner` or `manager` work routes into `mechanic`
- `MECHANIC_COMPLETE` loops back into `planner`
- `MANAGER_COMPLETE` is the normal planning terminal
- `BLOCKED` is the terminal recovery outcome from `mechanic`
- `auditor` routes `AUDITOR_COMPLETE -> planner` or `BLOCKED -> mechanic`
- `arbiter` terminates with `ARBITER_COMPLETE`, `REMEDIATION_NEEDED`, or `BLOCKED`

The graph-backed planning intake split is explicit:

- `spec -> planner`
- `incident -> auditor`

## What a mode defines

Modes are intentionally small. The current shape includes:

- `mode_id`
- `execution_loop_id`
- `planning_loop_id`
- `stage_entrypoint_overrides`
- `stage_skill_additions`
- `stage_model_bindings`
- `stage_runner_bindings`

Those are compile-time surfaces, not free-form runtime hints.

### `stage_entrypoint_overrides`

This map replaces the default stage entrypoint path for a stage. The path must be relative, must start with `entrypoints/`, and must end with `.md`.

### `stage_skill_additions`

This map attaches optional advisory skill paths to the frozen node binding. It does not change runtime-owned routing.

### `stage_model_bindings`

This map sets a mode-level model name for a stage.

### `stage_runner_bindings`

This map sets a mode-level runner name for a stage.

## Shipped harness posture

The shipped modes use identical loop topology but different harness bindings:

- `default_codex` binds every shipped stage to `codex_cli`
- `default_pi` binds every shipped stage to `pi_rpc`

That means the runtime control model stays constant while the stage runner adapter changes.

## What the compiler freezes

During compile, Millrace materializes one `CompiledRunPlan` with fields such as:

- `compiled_plan_id`
- `mode_id`
- `execution_loop_id`
- `planning_loop_id`
- `execution_graph`
- `planning_graph`
- `compiled_at`
- `source_refs`

Each materialized node binding records:

- `node_id`
- `plane`
- `entrypoint_path`
- `entrypoint_contract_id`
- `required_skill_paths`
- `attached_skill_additions`
- `runner_name`
- `model_name`
- `timeout_seconds`

This freeze step is what makes the runtime deterministic and inspectable.

## Compile inspection surfaces

Use:

- `millrace compile validate`
- `millrace compile show`
- `millrace modes list`
- `millrace modes show MODE_ID`

Those are the operator surfaces for checking which mode, loop, runner, skill, entrypoint, and completion behavior the runtime actually froze.

See also:

- `/ai/runner-architecture.md`
- `/ai/arbiter-and-completion.md`
- `/ai/cli-reference.md`
