Halt Reasons
Documentation source v0.5.0-rc.1. Published releases
5. Halt reasons -> exit codes (canonical map)
The halt_reason string is the stable integration contract; the exit code is a coarse class CI can branch on without parsing JSON. Existing codes 0/10/11/12/20/30/40/50 predate this spec; 13-19 are new classes introduced here. The table below is the full normative taxonomy; which halt_reason strings the binary emits today versus holds reserved is tracked in section 11 - as of v0.2.0, every class 13-19 emits (their core reasons; 18 via inspect-cost, 15 via doctor --mutate-cmd), while only class 11’s task-list reasons (the task_list topology) stay reserved until their detectors ship. Every reason MUST be computed from observed state - --halt-reason is a hidden test fixture only.
| exit | class | halt_reason strings | owner |
|---|---|---|---|
| 0 | nominal | (loop ran, no halt) | LoopExec |
| 10 | converged | success_condition_met | LoopExec |
| 11 | terminal-blocked | no_actionable_tasks, human_required | human |
| 12 | iteration-cap | max_iterations_reached | LoopExec |
| 13 | integrity | blocked_path_modified, reward_hacking_detected, metric_integrity_violation, credential_scope_invalid, objective_unverified | LoopExec (det.) |
| 14 | oracle-untrusted | check_flaky, check_has_side_effects, check_not_hermetic, hermeticity_violation | LoopExec (det.) |
| 15 | check-inadequate | check_inadequate | LoopExec (det.) |
| 16 | resumable-judgment | escalation_pending, reviewer_rejected | Musketeer / human |
| 17 | no-convergence | no_progress_detected, same_failure_repeated, oscillation_detected, same_test_regressed, unsatisfiable_constraints, infeasible_suspected | LoopExec (det.) |
| 18 | budget | budget_exceeded, cost_anomaly | LoopExec (det.) |
| 19 | liveness/drift | heartbeat_stale, model_drift_detected, comprehension_debt_exceeded, context_budget_unsatisfiable | LoopExec (det.) |
| 20 | invariant | invariant_failed | LoopExec |
| 30 | workspace | workspace_invalid, isolation_unsatisfiable | LoopExec |
| 40 | execution | execution_failure | LoopExec |
| 50 | internal | internal_error | LoopExec |
Ownership stance. LoopExec owns everything a number can decide (exit codes, the F_i trajectory, a fingerprint hash, a coverage/mutation delta, a manifest hash, a spend ledger, a model-identity tuple, a sequential flake bound). Musketeer owns only what a number cannot express - reward_hacking_detected, reviewer_rejected - layered on top of the deterministic metric_integrity_violation, never replacing it. Humans own the irreducible decision and the ack. Judgment is never the stop oracle: it can veto a green loop or escalate, but it cannot promote a red one to green.
Migration. The legacy bare "blocked" string (one word, three meanings) is split: no_actionable_tasks (11), blocked_path_modified (13), human_required (11).