# Aep Autopilot

> Runs dispatch through wrap continuously for a whole layer, unattended. Use for "autopilot" or "hands-free"; for a single story use /aep-dispatch.

- Skill: `memorysaver/aep-autopilot` (Agent Skill, multi-file: 15 files)
- Install (CLI): `npx skillmds@latest add memorysaver/aep-autopilot`
- Raw SKILL.md: https://api.skillmd.com/api/skills/memorysaver/aep-autopilot/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: memorysaver (https://skillmd.com/u/memorysaver)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/memorysaver/aep-autopilot

---


# 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/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

```bash
# 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:

   ```bash
   mkdir -p .dev-workflow
   ```

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

   ```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.

```bash
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                                   |

