# Chassis engine — the twelve owned capabilities

_Source: `system/core/lib/chassis-core/CHASSIS_CORE.md` (the engine's own contract doc) plus the
`index.mjs` public API it enumerates. No redaction required — framework engine documentation, no
secrets or customer data._

The Chassis is the generic autonomy-execution engine (`system/core/lib/chassis-core/`, version
`CHASSIS_CORE_VERSION = '1.0.0'`). A consumer declares a **profile** and gets these twelve
capabilities for free. Each is a property of the engine, not of any one consumer. The line/section
references below are to `CHASSIS_CORE.md` and the modules it cites.

| # | Capability | Owned by (engine surface) | Where in `CHASSIS_CORE.md` |
| --- | --- | --- | --- |
| 1 | **Durable run state** | `runProfile` persists/reads run state and keeps `retry_counts` crash-durable across a restart | `runProfile` §; `run-profile.mjs:57` |
| 2 | **Ordered stages** | `createOrchestrator({ stageResolver, … })` + the `runProfile` generic stage-walk; `stageResolver` is injected profile data, never a module constant | Reducer §; `orchestrator.mjs` |
| 3 | **Retry + budget** | `assessBudget({ runSpend, ledgerSpendByPlan, budgetThresholds, env })` + `buildBudgetFork`; crash-durable `retry_counts` in the driver loop | Budget + claim §; `run-cost-budget.mjs:51` |
| 4 | **Run claims + concurrency** | `atomicClaimPlan(planId, opts)` + `buildConcurrencyFork` — one run claims a plan, a second contending run is forked to elevate | Budget + claim §; `claim-plan.mjs:27` |
| 5 | **Crash / handoff recovery** | crash-durable `retry_counts` (resumes mid-run) + the `onHandoff` / `{ handoff }` runStage return (suspends indefinitely, resumes on a supplied result) | `runProfile` §; `run-profile.mjs` |
| 6 | **Registered decision forks** | `assertNoUnregisteredForks(log, registry)` + `assertRegistryCovered` — every fork the run takes must be declared in the injected registry; the lint runs over data, not a constant | Fork-registry lints §; `fork-registry.mjs:131` |
| 7 | **Deterministic resolution** | `createRealKernel({ log }) → { decide }`; deterministic forks auto-resolve via the declared rule, the engine recomputes `rejected_alternatives` from `options` (a supplied list is never trusted) | `decide` — the live kernel §; `judgment-resolver.mjs:115-117` |
| 8 | **Fail-toward-asking elevation** | judgment forks auto-resolve ONLY on a positive binding-rule-cited judgment (`rule_applied ∈ BINDING_RULES` AND `surviving_option ∈ options`); every other state elevates | `decide` — the live kernel §; `binding-rules.mjs` |
| 9 | **Append-only decision + dispatch logs** | `createDecisionLog(...)` (append-only, deep-immutable, the model never touches it) + `createDispatchLedger({ …, modelContractTable })` | Supporting engine exports §; `decision-log.mjs`, `dispatch-ledger.mjs:175` |
| 10 | **Run-path containment** | `resolveRunDir(runId, opts)` / `runsRoot(opts)` with `InvalidPlanId` / `PathEscape` errors — a run cannot write outside its own run directory | Path boundary §; `run-paths.mjs:49` |
| 11 | **Legitimate terminal states** | the `≤1-per-segment` terminal guard inside `runProfile` + the consumer's `buildTerminalSignal({ runId, segmentPushCount, state })` | `runProfile` §; `run-profile.mjs` |
| 12 | **Codification sink** | the codification-sink append inside `runProfile` (the `deps.codificationSink` I/O dep) — every WRAPPER_ERROR / unknown-token becomes a codification candidate | `runProfile` §; `run-profile.mjs:39-56` |

## Why these are engine, not profile

`CHASSIS_CORE.md` § "Engine purity (AC-06)" states the boundary the twelve sit behind: the engine
makes **no outbound import** to any consumer/auto path (`system/converge/…`, `system/_operator/…`)
and carries **no baked-in auto data** — no inline `|| AUTO_DEFAULTS`, no literal profile-path
`readFileSync`, no populated `ROWS`/`STAGES`/`MODEL_CONTRACT` constant. Everything a consumer varies
is injected as profile data; the twelve capabilities are what remains fixed. That is the whole reason
the engine is reusable: a second consumer inherits all twelve without re-implementing any of them.

The four kill switches (`AUTO_KERNEL=elevate_all`, `AUTO_GATE_ROUTING=elevate_all`,
`AUTO_MIDRUN_DISPATCH=manual`, `CODEX_TRANSPORT_SELECTOR=0`) each **degrade a capability to manual**,
never silently disable it (`seams/index.mjs:51-56`). Capability 8 (fail-toward-asking) is the floor
the switches fall back to — forcing the kernel stub makes every judgment fork elevate.
