SuperAPI AI Directory

aep-autopilot

Coding & development

View repository

Updated 10-16 00:28

About this skill

Autopilot

One command to go autonomous: initialize state, run the first tick, and keep ticking until the current layer is complete. The default driver is goal-driven (/goal, native on Claude Code and Codex) — each tick advances the layer and the runtime re-fires only while a completion condition is unmet, so autopilot stops on its own when every story in the current layer is merged + wrapped, or a human-judgment gate is hit, then hands back for the layer gate / /aep-reflect before you re-invoke it for the next layer. --loop selects a fixed-interval fallback driver. Rationale: docs/decisions/goal-driven-autopilot.md.

Where this fits: /aep-envision → /aep-map → /aep-validate → /aep-autopilot (goal: "layer N complete") → /aep-reflect.

Session: Main session only — Step ① guards it. State: .dev-workflow/autopilot-state.json (machine-readable) + .dev-workflow/autopilot-status.md (human-readable). Every legal field is typed in references/autopilot-state.schema.json; references/state-schema.md carries the atomic-write and tick-lock protocols and the runnable command that checks a state file (scripts/validate-state.mjs, over the shared scripts/json-schema.mjs).


STOP — Orchestrator Boundaries

You are an orchestrator, not an executor. All code operations happen inside workspace agents; the main session assesses progress via signal files and git metadata only and steers workspaces through executor.nudge(). Crossing this boundary is autopilot's most common failure, so it has exactly two prohibitions and they live here.

Two hard prohibitions. Both govern the orchestrator's own tool use, which no probe can observe from outside — so unlike every other rule in AEP they carry no machine check, and each is stated once, paired with its positive action:

  • Never read workspace source files (no Read/Grep/Bash into a worktree) — assess via signal files (.dev-workflow/signals/) and git metadata (gh pr view, git diff --stat) only. Pulling implementation into the orchestrator's context makes it form code-quality opinions and act on them. A code reviewer spawned from main is the same crossing in another shape: signals-only analysis is not code review, and handing that agent the worktree only completes the crossing.
  • Never gh pr merge from the orchestrator — guide the workspace agent to merge. The worker owns Phase 12 (rebase, CI, comment resolution) and merges when its checks pass; "PR ready" is not a worker stop point. This binds MAIN only — never leak it into a worker prompt: telling a worker "do not merge" / "stop at ready" leaves a CLEAN, mergeable PR parked forever (the codex-subagent shared-session failure). Every merge nudge says "merge when Phase 12 passes."

What the orchestrator does (positive)

Need Orchestrator action
Read workspace progress Read .feature-workspaces/<name>/.dev-workflow/signals/status.json
Check PR state gh pr view <number> --json state (observe only)
Trigger code review / gen-eval executor.nudge(<ws>, "<trigger>") — the workspace runs its own evaluator
Send feedback / instructions executor.nudge() or write .feature-workspaces/<name>/.dev-workflow/signals/feedback.md
Nudge a stuck agent executor.nudge(<ws>, "<nudge>")
Surface a human gate Read signals/needs-human.md; relay the human's answer on the mode's channel
Get code fixed / tested / evaluated / merged executor.nudge() — the workspace owns building, testing, eval, and Phase-12 merge; monitor eval-response-*.md for results

The CHECK delegate (below) reading signals is not a boundary violation — it offloads the orchestrator's own signal bookkeeping to a cheap context and reads no code.

Two gen/eval concerns — strictly separate

Concern Owner Evaluates Runs
Code quality Workspace agent correctness, security, completeness inside the workspace worker
Orchestration learning Autopilot (main session) cross-workspace patterns: failures, costs, retries main session → /aep-reflect (see references/orchestration-learning.md)

Detection logic for when a workspace's gen/eval must be triggered: references/review-trigger.md.

Executor transport

Every workspace action goes through three executor verbs: executor.nudge(ws, msg) (deliver an instruction), executor.liveness(ws) (is it really working — process exists AND worktree active, never roster/state membership), and executor.check(prompt, schema) (cheap, context-isolated read). Per-mode transport, the driver × backend matrix, the post-spawn liveness probe, and the spawn / orphan-re-adoption recipes are canonical in /aep-executor references/backends.md — consult it for the mode you detected. Autopilot requires a steerable, driver-compatible mode; if detection yields only workflow/headless (no mid-stage surface — that batch path is the /aep-dispatch … with workflow orchestrator, not something autopilot drives), report that autopilot needs a steerable mode and stop.


/aep-autopilot — start

Initialize autopilot, run the first tick, and start the driver — one command, no second step.

/aep-autopilot                  # goal driver (default) — drives the current layer to completion, then stops
/aep-autopilot --floor 3m       # goal driver, custom per-tick wait floor (default 5m)
/aep-autopilot --loop 10m       # fixed-interval loop driver (fallback), custom interval
/aep-autopilot status           # progress + escalations
/aep-autopilot stop             # gracefully stop the driver

Driver selection: the presence of --loop <interval> selects the loop driver; its absence selects the goal driver (default). --floor <dur> applies only to the goal driver (per-tick wait, default 5m); --max-turns <n> caps the goal driver as a runaway backstop (default 200).

Prerequisites

# 1. Must be on main workspace (not inside a feature workspace)
pwd | grep -q '.feature-workspaces' && echo "ABORT: Run from main workspace only" && exit 1

# 2. Product context must exist
[ -f product-context.yaml ] || echo "ABORT: Run /aep-envision and /aep-map first"

# 3. Autonomous routing must be enabled
# Check topology.routing.autonomous: true in product-context.yaml

Verify before proceeding:

  • Main workspace guard: pwd must NOT contain .feature-workspaces
  • Product context exists: product-context.yaml exists with a stories section
  • Autonomous enabled: topology.routing.autonomous: true is set
  • Stories available: at least one story is ready or in_progress
  • Validated: product context has passed /aep-validate (both passes)

Start Protocol

  1. Create .dev-workflow/ if absent:

    mkdir -p .dev-workflow
    
  2. Initialize .dev-workflow/autopilot-state.json (every field typed in references/autopilot-state.schema.json):

    {
      "version": 1,
      "status": "running",
      "started_at": "<ISO8601>",
      "last_tick_at": null,
      "tick_count": 0,
      "workspaces": {},
      "escalations": [],
      "stats": {
        "stories_completed": 0,
        "stories_failed": 0,
        "total_ticks": 0,
        "total_cost_usd": 0
      }
    }
    
  3. Write initial .dev-workflow/autopilot-status.md (Status: Running, tick count 0, no active workspaces yet — fill templates/autopilot-status.md.tmpl).

  4. Resolve the launch mode + driver pair (executor detect() + the driver × backend matrix). On Claude Code the default mode is native-bg-subagent (no team to create — if any agent-teams team is active, TeamDelete it first, since a live team poisons teamless background spawns). Announce the pair, e.g. "native-bg-subagent + /goal" or "codex-exec + launchd".

  5. Run the first tick immediately (see /aep-autopilot tick below).

  6. Start the driver per the selector below.

Driver selector

Both drivers keep the orchestrator long-lived (so any steerable mode works); the goal driver additionally self-terminates when the layer is done. The tick body is identical under either driver — only how the next tick is triggered differs. Full recipes (goal-condition text, per-host /goal / /loop commands, Codex launchd codex exec snippet): references/drivers.md.

Host / run Driver Start
Claude Code / Codex, attended goal (default) /aep-autopilot sets /goal "<layer-N condition>"; re-fires each turn until layer complete or paused
Any host, fixed cadence or no /goal loop (--loop) /loop <interval> /aep-autopilot tick
OS-scheduled, unattended loop via cron/launchd schedule codex exec … "/aep-autopilot tick" (OS-bound modes only)

Driver × backend compatibility: a long-lived driver (goal, or in-session /loop) supports any steerable mode; a cron/launchd fresh-session-per-tick run supports OS-bound modes only (claude-bg, codex-exec, legacy), driven by /loop or an external scheduler because /goal is in-session. Full matrix: /aep-executor references/backends.md. Goal-driver invariants: scoped to one layer; the evaluator judges only the signals-only surfaced status line (step ⑦); the per-tick wait floor is mandatory (without it /goal hot-loops the instant a turn ends); and --max-turns (plus a Codex token_budget) always bounds it.


Strategic gates — full_auto and auto_design

Two topology.routing flags govern the strategic ("what to build" / architecture) human gates; both default to keeping the human in control:

  • auto_design (default false): a story in a non-dispatch-ready band, or one that hits the canonical repeated-attempt override, pauses for the human. auto_design: true instead routes it through /aep-design (non-interactive), re-computes readiness, then /aep-launch — no pause.
  • full_auto (default false): the master switch above the finer-grained flags (auto_design, auto_outcome_eval, watch.auto_create) — full_auto: true implies all of them, so both the design-escalation pause and the qualitative outcome-contract evaluation auto-proceed via agent judgment. The default keeps every strategic pause with the human. The repeated-attempt override always escalates, even with these flags on.

Routing thresholds and the pause protocol live in the tick step that applies them (references/tick-protocol.md Step ⑥); the escalation entry is typed in references/autopilot-state.schema.json and the paused autopilot-status.md sections are in templates/autopilot-status.md.tmpl.

full_auto does not touch journey authoring. The journey file is always a pre-merge build deliverable (/aep-build Phase 6 Step A authors it from the layer's acceptance criteria, committed with the feature). So under full_auto: false + journey_timing: post-deploy the journey must already exist before the post-deploy layer-gate handback — the handback executes and flips, never authors. A layer reaching its gate with no journey file is a /aep-wrap COVERAGE FAILURE (the build should have authored it), not a human-authoring task at the gate.


/aep-autopilot tick

The per-tick handler invoked by the driver (goal or loop); also runnable manually at any time. Idempotent — re-running with no external state change produces no duplicate actions.

Execution model — CHECK → ACT. Each tick reads in a cheap, context-isolated agent (executor.check() — a Haiku subagent on Claude Code, a codex exec one-shot on Codex) that writes the updated autopilot-state.json + autopilot-status.md and returns an action list; the orchestrator then performs those actions (nudge, wrap, launch, escalate / design), so the token-heavy reading stays out of the long-lived session. On Codex the whole tick is already an isolated codex exec, so its CHECK can run inline. Which steps fall on which half is in references/tick-protocol.md; the action list is typed in /aep-executor references/action-list.schema.json.

Tick summary (①–⑦, invariant per step). Full step recipes — the CHECK prompt content, the ACT templates (including the ④b/④c nudge prompt texts), and every threshold — are canonical in references/tick-protocol.md:

① READ STATE       cat autopilot-state.json; exit if status≠running or a fresh tick lock is held; else take the tick lock.
② SYNC SIGNALS     fold each workspace signals/status.json into state; blocked_on==human → escalate human_gate (not stuck); orphan (session-bound mode, by real liveness) → emit a re-adopt launch into the existing worktree.
③ WRAP COMPLETED   story_status==completed → /aep-wrap (max one per tick, [one-wrap-per-tick]); remove from state; then skip to ⑦.
③.5 POST-MERGE GUARD  for each merged story, watch deploy health + host-aware dogfood across a window; UX finding → /aep-reflect story; confirmed hard regression → escalate (or revert if auto_revert). See references/post-merge-guard.md.
④ GUIDE COMPLETION per workspace: ④a check PR state; ④b quality gate — no eval PASS → trigger the workspace's own gen/eval via executor.nudge(); ④c eval PASS + PR open → nudge toward Phase-12 merge (once).
⑤ DETECT STUCK     (phase, completion_pct) unchanged vs last tick → run executor.liveness() before counting; then the stuck ladder (observe → nudge → escalate); human-gate exempt.
⑥ DISPATCH NEW WORK  capacity? run /aep-dispatch scoring (steps 1-3); wave + layer-gate + grouped-change ordering; dispatch-ready → /aep-launch, otherwise escalate or auto-design; max one launch per tick ([one-launch-per-tick]); runs after ③ ([wrap-before-dispatch]).
⑦ WRITE STATE + SURFACE + WAIT  atomic write state (.tmp → rename); append autopilot-history.jsonl; update autopilot-status.md; release the tick lock. Goal driver only: surface the signals-only AUTOPILOT status line, then wait the per-tick floor before ending the turn.

The [bracketed] tags are mechanical ordering invariants — deterministic WHEN+SHAPE rules that drift under prose recall and hold behind typed gates. A project scaling this loop should migrate them behind its own typed verbs: docs/decisions/deterministic-orchestration.md (catalog in references/deterministic-orchestration.md).


/aep-autopilot status

Read and display current autopilot state.

cat .dev-workflow/autopilot-status.md
ROOT=$(git rev-parse --show-toplevel); AP="$ROOT/.agents/skills/aep-autopilot"; [ -d "$AP" ] || AP="$ROOT/.claude/skills/aep-autopilot"
node "$AP/scripts/derive-workspace-state.mjs"   # logical state + flags per workspace

Render the explanation for the human in the /aep-easy-explain register: one line of context first, ASD-STE100 plain English, the project's own nouns.

Also parse .dev-workflow/autopilot-state.json and present: Status (running / paused / stopped); Uptime (since started_at, tick count); Active workspaces (name, story_id, phase, completion_pct, last_action); Pending escalations (type, story_id, reason); Stats (completed, failed, total cost). If paused: why, what human feedback is expected, how to resume.


/aep-autopilot stop

Gracefully stop autopilot and cancel the active driver.

  1. Set status: "stopped" in .dev-workflow/autopilot-state.json.
  2. Update .dev-workflow/autopilot-status.md with the stopped state.
  3. Log the stop event to .dev-workflow/autopilot-history.jsonl.
  4. Cancel the driver:
    • Goal driver: /goal clear (Claude Code / Codex). No further turn re-fires. (The goal driver also self-clears when the layer completes, so stop is mainly for early termination.)
    • Loop driver: cancel the /loop (loop skill's cancel mechanism), or remove the cron/launchd job for an OS-scheduled run.
  5. native-bg-subagent: background subagents are session-bound and stop when the orchestrator session ends. To stop early while preserving work, let each in-flight worker reach a phase boundary (or TaskStop <agentId>); all state lives in the worktree + .dev-workflow/, so a later /aep-autopilot re-adopts the orphan via the liveness probe.

Effect: no more ticks; running workspaces continue autonomously (they don't depend on autopilot — caveat: session-bound workers are orphaned when the lead session closes and re-adopted on the next start). Product context is not modified; no wraps or merges are triggered. To resume: /aep-autopilot.


Guardrails

Rules with no natural step home (the ordering invariants, atomic write, and tick lock live in references/tick-protocol.md / references/state-schema.md; the WIP ceiling topology.routing.concurrency_limit and the dependencies-completed gate are Step ⑥'s dispatch check):

  • Require a real PASS before nudging a merge — for integration/test stories that means at least one passing check OR an explicit eval-response PASS. SKIP-only results and "no checks" are absences of evidence, not passes.

Next Steps

After autopilot completes a layer or is stopped:

Action When
/aep-reflect After a layer completes — evaluate outcome contracts (Step 2.75), classify feedback
/aep-autopilot status Anytime — check progress and escalations
/aep-autopilot After resolving a pause — resume the driver (re-sets the layer goal)
/aep-dispatch Manual mode — pick a specific story interactively