Autopilot architecture
Purpose
Autopilot packages a complete orchestration process as one Claude Code plugin. It provides an auto-invokable orchestration skill, explicit-only setup skill, nine native roles, optional enforcement hooks (Python command hooks plus a function-hooks mod), and a registry-driven worker layer. User and project configuration stays outside the installed plugin.
The central invariant is producer-independent quality: native agents, CLI tools, and MCP workers receive equivalent briefs and re-enter the same intake gates.
Boundaries
- The current session is the only dispatcher. Agents and workers cannot recursively delegate.
- Plugin metadata does not set the main model, fallback model, or main-session effort. Setup proposes user-setting changes after approval.
- The plugin does not ship an Explore agent because plugin agents cannot shadow the built-in role. Setup offers a user-level override from
templates/Explore.md. - Skill and agent bodies use relative doctrine paths. Plugin-root expansion is reserved for hook commands where the host supports it.
- Runtime state lives in
~/.autopilot/and<project>/.autopilot/.
Components
Skills
orchestrate implements a ten-step loop: restate, check, choose review mode, route, ticket, decompose, dispatch, intake, zoom out, and close. Supporting references hold the canonical roles, dispatch templates, judgment rubrics, review loop, diagnosis, maintenance, and worker doctrine.
setup is disabled from model invocation. It inspects first, presents one merge plan, waits for approval, backs up user settings once, and applies only approved key/file changes.
Native roles
| Agent | Alias / effort | Boundary |
|---|---|---|
| architect | opus / high | Designs and arbitrates; no production code |
| engineer | opus / medium | Normal code, scripts, data |
| engineer-lite | haiku / medium | Fully specified, self-contained code with an acceptance command |
| senior-engineer | opus / high | Code where a defect is expensive |
| engineer-doc | opus / medium | Visual/doc work with render inspection |
| security-engineer | opus / high | Security-sensitive work |
| verifier | opus / high | Refutes or confirms; never fixes |
| reviewer | opus / high | Adversarial severity findings; never fixes |
| supervisor | fable / high | Judges the job at gates; never edits or dispatches |
Efforts are tuned for Opus 5.5, whose medium does roughly what Opus 5 did at high. engineer and senior-engineer are separate definitions because the Agent tool has no per-call effort. Global and project overlays can override the model per Agent call; effort overrides take effect on the sc path. Model identifiers remain aliases.
Worker registry
Each record under global or project workers/ follows Draft 2020-12 JSON Schema. Project ids replace global ids. Common fields describe capabilities, model family, selection guidance, enabled state, and trust. Exactly one variant is present:
subagent: Agent type and optional alias/effort overrides.cli: executable, argv template, prompt delivery, environment, timeout, accepted exits.mcp: scoped tool, argument template, result field, timeout.
Capability matching establishes eligibility. Preference/avoidance notes, overlay order, and model-family independence break ties. No match means native fallback.
CLI runner
The installed runner accepts a worker id, absolute brief path, project root, optional output directory and timeout, plus an approval bypass for already approved runs. It:
- Loads global then project registry records.
- Validates the selected record and executable.
- Resolves six known template variables without shell evaluation.
- Displays untrusted commands for approval.
- Enforces timeout and captures output.
- Writes deterministic output and metadata.
Runner exits are 0 success, 2 worker/output failure, 3 registry/record/command error, 4 declined approval, and 124 timeout. The runner executes only CLI records; the orchestrator owns subagent and MCP invocation.
Completion callback
A dispatched worker must be able to wake the session that dispatched it. The orchestrator resolves its own sc target once per job and passes it down; whoever owns the worker’s process emits the ping when that process ends. For CLI workers that is the runner, which sends on every exit path and records callback: { target, idempotencyKey, delivered, error } in metadata; --detach re-execs the runner in its own session so the dispatching turn can end while the worker runs. For sc-managed role agents there is no wrapper, so the brief carries the send command as a mandatory final action with sc agent wait as fallback. MCP dispatch returns inline. Agent-tool subagents run in the background, and Claude Code’s own task notification wakes the session when each finishes; the same notification covers a runner launched with Bash run_in_background when no sc address resolved.
The design point is that the guarantee lives in the wrapper, not in the worker’s goodwill: Claude, Codex, and Grok are all covered identically because none of them is trusted to remember. It exists because the driving session is often headless (claude -p), where the process exits at turn end and a queued callback is the only thing that restarts it. Claude Code’s task notification needs a live process, so it serves persistent interactive sessions only. A headless session without a resolved callback address stays fully in-turn, which is the older behavior and always correct.
Intake
Every producer passes:
- Artifact spot-check for gaming and unsupported limitations.
- Conditional fresh-context red-team.
- Criterion-by-criterion sign-off using real evidence.
- Native verification when R7 fires — always for external-worker code.
Review is independently optional and adversarial. The final gate runs a panel of at least two independent reviewers (reviewPanelSize), preferring distinct model families and degrading to a solo native reviewer. Reviewers presume the deliverable defective and know their misses and false findings are cross-scored against the other panelist; producers are briefed to pass in one round, with disclosure always cheaper than concealment. Findings merge by union with orchestrator spot-check, only critical/major trigger a new round, and round three escalates to architect arbitration.
Hooks
The Python command hooks run on any build that runs command hooks, claude -p included. The Bash guard is fail-closed only for a positive catastrophic-pattern match in strict mode. Advisory mode turns the same match into a permission prompt, so a person decides. The brief check, on Agent dispatches of architect, engineer, engineer-lite, senior-engineer, engineer-doc, and security-engineer, looks for non-empty acceptance criteria in the inline <dispatch> block or the .md brief the prompt names; strict denies a brief without them, advisory adds a note to the model’s context, and an unreadable brief is not judged. The ledger nudge is advisory. All three catch internal errors and return success so a parsing or platform defect cannot break normal host behavior. They resolve enforcement from the plugin setting, then ~/.autopilot/config.jsonc, then advisory.
The function-hooks mod (hooks/register.tsx) needs a mods-capable Claude Code (written and tested on 2.1.287) and reads only the plugin setting. In a Fable main session it hides autopilot:security-engineer from the agent list and refuses its dispatch in advisory and strict; at spawn time strict denies and advisory toasts. It also denies (strict) or toasts (advisory) a spawn whose parent is an autopilot:* agent, which only a misconfigured role could trigger. /ledger opens a read-only ledger pane in every mode, off included. The mod sees only Agent-tool dispatches: sc-managed role agents and registry CLI workers are outside it, so on those paths the doctrine is the only enforcement. Its handlers pass the event through on error.
Trust model
Registry files authorize executable commands with the user’s privileges. Records default to untrusted and require review of the fully resolved argv. Briefs are delivered through stdin or file where possible and placeholders become argv elements rather than shell text. Setup never makes trust decisions for the user.
External output is untrusted evidence, not an accepted result. Its model family, brand, or capability claim grants no quality shortcut.
Failure and degradation
The native roles are the availability floor. Missing registries, commands, servers, runner, or reviewers cause a logged fallback rather than a broken command loop. First worker failure feeds evidence into one retry; second failure escalates to native execution. A third attempt must change approach, worker, or tier.
If all native roles resolve to one tier, role and context separation still apply. The orchestrator compensates with more explicit criteria and lower concurrency.
Runtime data
Project .autopilot/ contains:
ledger.md
dispatch/
artifacts/
archive/
workers/
config.jsonc
lessons.md
Artifacts are never automatically deleted. Completed ledger rows may be archived after the documented threshold; their regenerable briefs may then be removed.
Distinct from this gitignored harness state, a project may keep a committed root DEVLOG.md as its cross-agent handover surface: a rewritten one-screen State header plus append-only numbered entries, maintained by the orchestrator at close-out and readable by any harness (Claude, Codex, or another tool doing a cold pickup). It is presence-activated — Autopilot maintains an existing devlog but never creates one uninvited. Single writer: workers read it as material and never append; ledger Notes point at entries (see DEVLOG §N) instead of accumulating narrative.
Contributor invariants
- Keep plugin content free of user and machine specifics except the single labeled illustrative environment comment in the overlay example.
- Preserve relative references in skills and agents.
- Preserve alias-only role frontmatter.
- Add registry capabilities through data, not runner branches.
- Maintain fail-open behavior for every hook error.
- Demonstrate runner, timeout, schema, and hook behavior with real commands.
- Never let optional review or an external worker become load-bearing.