Skip to content

Warp-Drive Guide

Autonomous development loop for Claude Code. Picks up approved work from GitHub Issues, breaks it into chunks, codes, tests, commits, and reports — with minimal human intervention.

Two ways — but they do different jobs. The slash command runs the loop (it needs a live Claude Code session); the terminal warp command only observes and controls a loop that is already running — it cannot start one.

Slash command — run the loop (in a Claude Code session):

/warp-drive 42 # work on issue #42
/warp-drive # auto-discover next approved issue
/stop-warp-drive # graceful stop

Terminal — observe / control a running loop (any shell):

Terminal window
warp status # check on the loop
warp stop # abort from outside

Warp Drive operates at the bottom of the product hierarchy (vision → capability → requirement → chunks). It picks up requirements (not capabilities or vision) and turns acceptance criteria into commits. Each requirement is a GitHub Issue with checkboxes; warp-drive works through them one commit at a time.


These must be installed on your machine:

Dependency Check Install
Node.js node --version nodejs.org
jq jq --version brew install jq
GitHub CLI gh --version brew install gh
Git git --version brew install git

Warp Drive is part of BoB (Big ol’ Brain), the global Claude Code tooling framework at ~/.claude/. If you have BoB installed, warp-drive is already available. The key files:

~/.claude/
├── bin/warp # CLI (this is new)
├── commands/warp-drive.md # /warp-drive slash command
├── commands/dev-up.md # /dev-up slash command
├── skills/stop-warp-drive/SKILL.md # /stop-warp-drive skill
├── scripts/warp-drive/state-machine.js # State machine engine
├── scripts/dev-lifecycle/ # Dev environment IaC scripts
│ ├── dev-up.sh # Full lifecycle orchestrator
│ ├── health-check.sh # Lightweight health probe
│ ├── provision-users.sh # Test user provisioning
│ └── check-seed-coverage.sh # Seed data coverage gate
├── hooks/warp-drive-gate.sh # Blocks commits outside committing phase
├── hooks/warp-drive-inject.sh # Resumes state on session start
├── hooks/warp-drive-edit-tracker.sh # Doom loop detection
├── hooks/warp-drive-pre-exit.sh # Self-verification before phase advance
├── hooks/warp-drive-stop.sh # Blocks exit when report is pending
├── templates/dev.json # Dev manifest template
├── templates/seed/ # Seed data templates
└── docs/warp-drive.md # This file

Add this to your ~/.zshrc (or ~/.bashrc):

Terminal window
# BoB CLI tools
export PATH="$HOME/.claude/bin:$PATH"

Then reload: source ~/.zshrc

Verify: warp help

The warp-drive hooks are registered in ~/.claude/settings.json automatically. If you’re setting up from scratch, the hooks need entries in:

Hook Type File Purpose
SessionStart warp-drive-inject.sh Resume from last phase
PreToolUse (Bash) warp-drive-gate.sh Block commits outside committing phase
PreToolUse (Bash) warp-drive-pre-exit.sh Self-verification before phase advance
PostToolUse (Edit|Write) warp-drive-edit-tracker.sh Track edits, detect doom loops
PostToolUse (Edit|Write|Bash) warp-drive-docs-detector.sh Auto-detect the AC tick (gh issue edit <req> --body…) or a doc edit → advance state (#1852)
PostToolUse (Bash) warp-drive-commit-detector.sh Auto-detect git commit → advance state
Stop warp-drive-stop.sh Block exit when report is pending

Every auto-advance and gate in this table is a declared behaviour with a stable fingerprint — see Declared automatic behaviours; state-machine.js declared "$(pwd)" reports whether each one is present and wired on this machine.

If hooks aren’t registered, warp-drive still works — you just lose the safety rails (commits could happen at the wrong time, doom loops won’t be caught, auto-detection won’t fire, etc.).

If the phase stays at committing after a successful commit, the hook did not fire. Hooks run from the deployed copy in ~/.claude/hooks/, so a fix merged to BOB_SOURCE is inert until scripts/deploy.sh syncs it; the detector also refuses to advance while HEAD still equals the sha recorded at tests_passed (a commit a commit-msg hook rejected). Check git log -1 first, then state-machine.js status. Firing transition … committed by hand is a recovery step, not the normal path — until #1830 every commit needed it because the hook gated on a payload field the harness never sends; that is fixed and covered by tests/test-commit-gate-detect.sh against the real payload shape.


1. Initialize your project with BoB tooling

Section titled “1. Initialize your project with BoB tooling”

Terminal (Terminal only — cdi is a shell alias; there is no slash form, but /automation will run it for you if you skip it):

Terminal window
cdi -y /path/to/your/project

This creates the .claude/ directory structure in your project. If you skip this, /automation will run it for you.

Inside Claude Code, in your project directory:

Slash command:

/automation level 2

Terminal (the -a2 startup flag on the claude shell wrapper sets the same level when you launch a session):

Terminal window
claude -a2

This writes permission rules to .claude/settings.local.json. Warp-drive requires Level 2 or 3.

Level Who It’s For Merge Strategy
Level 2 (Trusted Dev) Active development, you review PRs Creates PR for review
Level 3 (Autonomous) Trusted mode, minimal intervention Direct merge to master

Create a requirement as a GitHub Issue:

Slash command (Slash command only — authoring the issue needs the session’s conversation context):

/requirement

This creates an issue with acceptance criteria checkboxes, labeled req. When ready for warp-drive, add the approved label (plain gh, from any shell):

Terminal window
gh issue edit 42 --add-label approved

Slash command (Slash command only — no terminal entrypoint; Level 3 auto-creates the branch instead):

/start-work

Level 3 auto-creates branches. Level 2 requires you to start one first.

Slash command (Slash command only — the loop runs in the Claude Code session; the terminal warp only observes/controls it):

/warp-drive 42 # work on issue #42
/warp-drive # auto-discover next approved issue

From inside Claude Code, in your project directory:

Slash command (Slash command only — runs in-session; the terminal warp observes/controls but never starts the loop):

/warp-drive # finds next issue with labels: req + approved
/warp-drive 42 # works on issue #42 specifically
/warp-drive --area billing # works the whole 'billing' workstream (see below)

Warp-drive checks prerequisites (automation level, branch, test command), then enters the loop.

Kickoff scope: what a run is authorized to work

Section titled “Kickoff scope: what a run is authorized to work”

The kickoff argument defines the run’s scope, not just its first work item (#992):

  • /warp-drive 42 (or --issue 42,43) authorizes only the listed issue(s). When they are done, the run ends cleanly — it does not discover the next approved issue, and it does not segment into a fresh self-spawned session. Completed issues are recorded in session.completed_issues, so even a resumed segment of a multi-issue scoped run works only what remains.
  • /warp-drive --area billing authorizes the workstream’s approved queue.
  • /warp-drive (unscoped) authorizes the full approved queue — the run continues until the queue is empty or a breaker trips.
  • /warp-drive --flightplan <N> authorizes the ordered items of a persisted flight-plan issue — it consumes them in order, never widens, and ticks each off as its requirement completes. This is the most explicit scope and wins over --issue and --area. A plan may contain gating human-action todos (#1088): flightplan.js remaining returns only runnable work, so a req gated by a pending todo is skipped, not failed — the run proceeds to non-gated items and the gated req re-enters once the todo carries the canonical completed label (a todo closed without completed never releases; the gated work is moot). Released todos are reconcile-ticked in the plan issue automatically. Ordered plans stop at the gate instead of skipping it (#1415): when the gated item is labelled serial-only, it is a barrier — every later item is withheld too, so a run can never execute downstream of a human checkpoint it has not cleared. Withheld items name the barrier holding them rather than a todo of their own, and clearing that one gate frees the chain down to the next barrier in a single step. A plan with no serial-only items keeps skip semantics; a <!-- flightplan:gate-mode barrier|skip --> marker in the plan body forces either mode. If remaining comes back empty while status still shows gated, withheld, or moot items, the run ends cleanly — report gated work as waiting on its named todos, withheld work as blocked behind its barrier, and moot items as cancelled, in the session summary rather than as failures.

Run-boundary plan maintenance (#1667). A flightplan-scoped run maintains its plan’s lifecycle at both natural boundaries, using the #1666 engine — never a re-implementation. Kickoff: init runs flightplan.js lifecycle <N> before consuming the plan. stale (scope drifted since build) is refused with the exact build refresh command at L1/L2, and auto-refreshed via the same idempotent build path at L3+ (logged as plan_lifecycle in the init response); drained is refused with its gate(s) named — parity with a fully-gated remaining result; exhausted is refused as already-complete and closed via the guarded reconcile path. The gate fails open if the engine or gh is unreachable. Session end: the run converges the plan it consumed with flightplan.js reconcile --apply --plan <N> — an exhausted plan is closed with the engine’s dated final summary (a plan with an unchecked open item is never closed), a drained plan is stamped with its gates named and left open, a stale plan is reported with its refresh command. Both boundaries are conditional on session.kickoff.flightplan — unscoped, --issue, and --area runs are untouched.

Flight-plan provision overlay (#1133). A flightplan-scoped run can carry the plan’s registry items for the duration of the session without editing the project’s base manifest. At prerequisites the run applies a plan-scoped overlay — cdprov overlay apply --plan <N> derives the items from the plan’s issues via scripts/orchestrator/plan-scope.js (labels, area:* labels, stack signals in title/body) and links them, recording state at .claude/.provision-overlay.json. While the overlay is active, cdprov refresh keeps its links (union with the manifest) and cdprov status surfaces it first. At session_ending the run restores the base manifest with cdprov overlay restore (overlay-only links removed; items also in the manifest stay). Crash safety: an interrupted run leaves the state file behind — the next session’s prerequisites (or any cdprov status) detects it, and cdprov overlay restore recovers it; restore is idempotent. Both transitions post a comment on the plan issue, so the lifecycle is visible from GitHub.

A flight plan wins over an issue list, which wins over --area, when more than one is given. The scope rule lives in one place (kickoffScope() in scripts/warp-drive/kickoff.js) and is enforced at every point that could otherwise widen a run: the segmentation decision computes “more work” within scope, the discovering phase refuses to fall back to the queue on a scoped run, and the resume context handed to a fresh segment names the scope explicitly. warp status shows the reach on its Scope: line. See the Kickoff Flags reference.

Within a single repo, issues for distinct workstreams — a module, a feature, a capability rollout — are intermixed. An area:<slug> label tags every issue in one workstream (a capability owns the slug; its child requirements inherit it), so you can warp-drive the cluster as a unit instead of hand-picking issue numbers (#260).

Slash command:

/warp-drive --area billing

narrows discovery to req + approved + area:billing and works those requirements in priority order, one per cycle. Manage and inspect areas with the helper script (Terminal only — these label helpers have no slash form):

Terminal:

Terminal window
~/.claude/scripts/area-labels.sh list # existing area:* labels
~/.claude/scripts/area-labels.sh issues billing # preview the cluster (status overview)
~/.claude/scripts/area-labels.sh ensure billing # idempotently create/refresh the label

See the handbook’s Area labels section for the full convention.

/warp-drive used to start coding on whatever the machine happened to have: an unwired or stale hook (#1856, #1897, #1913) meant the agent reached a phase whose auto-advance never fired and compensated by hand. The #1833 family reports that after the fact; the preflight gate prevents it. It is a real phase — prerequisites → preflight → discovering | blocked — and the decision is made in the state machine, never in the prompt.

What it runs. state-machine.js preflight <root> [--converge] calls the engine scripts/warp-drive/host-preflight.js (also bob preflight), which reuses two existing verdicts verbatim:

Check Engine Asks
host bob doctor (scripts/checks/host-drift.js, #1921) Is this machine’s BOB_HOME what deploy.sh would produce — surfaces, symlinks, toolchain, settings hook wiring?
project bob ready --dry-run (scripts/ready/ready.js, #1922) Is this project warp-drive-ready — .claude/ initialised, manifest links materialised, labels, port band, dev.json/checks.json valid?

Non-mutating by default. --converge runs the real bob ready (every step is idempotent) first, then re-runs both checks — the fresh re-check decides, never the convergence run’s own optimism. The result is a warp-preflight/1 document: per-check verdict, every failure with its remediation command, a fingerprint over the failing set, and the todo it filed.

Pass. The engine fires preflight_okdiscovering. The verdict — machine id, per-check verdicts, timestamps — is recorded at state.preflight, shown by warp status (Preflight: line) and state-machine.js status (.preflight), and reported in the session summary.

Fail. The engine fires preflight_failedblocked, after filing one todo (todo,warp-drive,p2-high, assigned to the caller) in the project repo whose body conforms to the todo format contract: a summary, one numbered step per failed check with its exact fix command, a closing re-check step, and a Blocked by #NN link to the in-scope issue. An open todo carrying the same fingerprint marker (<!-- warp-preflight: … -->) is bumped with a comment, never refiled — repeat runs of the same drift converge on one issue. RDB, when enabled, gets the [warp-drive] BLOCKED notification.

From blocked the coding loop is unreachable: the only exits are preflight_retry (back to preflight — run the gate again after the human acted; a pass resumes exactly where the run was) and blocked_end (→ session_ending, the summary reports the verdict). preflight_ok / preflight_failed are engine-fired only: a hand-fired transition … preflight_ok is refused with the command to run the gate. An engine that cannot run — a missing script, an unparseable answer — is verdict error and fails closed, never a pass.

Mid-session re-check. Drift does not only happen before a run. At next_chunk (every chunk boundary) and continue_yes (every requirement boundary, which is also where a resumed segment re-enters) the state machine re-runs the cheap host section (~150 ms). New drift blocks the next chunk exactly the way a dev-health failure does — the transition is refused, the run moves to blocked, the todo is filed/bumped, and preflight_retry → preflight_ok returns it to the interrupted phase. Each re-check is recorded under state.preflight.rechecks. _workflow.preflight_recheck: false opts out of the re-check (the kickoff gate itself has no opt-out).

cdfork fork --from-issues runs the same gate once, before fanning out, so N worktree sessions never start on a machine one session would refuse.

Warp-drive used to re-derive the ordered approved queue from scratch every cycle. Discovery is now fingerprint-cached (#1069): each cycle, scripts/warp-drive/discovery-cache.js queue makes one cheap gh issue list call and hashes the actionable set — the approved open req/bug issue numbers plus each one’s priority label and serial-only flag (the attributes that determine queue order). That 12-char hash is the invalidation signal:

  • Unchanged fingerprint → the cached ordered queue (stored under discovery_cache in .claude/.warp-drive-state.json) is reused as-is; no re-derivation, no extra discovery passes. On a multi-chunk run where nothing material changes, discovery after the first cycle is ~free.
  • Changed fingerprint — an issue was closed, opened, re-prioritized, newly approved, or had serial-only flipped — → the queue is re-derived through discover-queue.sh (still the single ordering source, #517) and the cache refreshes. A newly-approved p1 that appears mid-run therefore invalidates immediately and is picked up exactly as before; the cache never trades freshness for tokens. Title edits and other order-irrelevant changes deliberately do not invalidate.

The cache is in-run session state only: it lives inside the warp-drive state file and is deleted with it at session end — never a persisted PM file. It applies to --area and full-queue runs; --issue and --flightplan runs have a fixed scope and never consult the live queue (the cache composes with a flight plan but is not required by it). Inspect or drop it with discovery-cache.js status|invalidate --cwd <project-root>.

An approved item can turn out to have zero agent-side work remaining — every open AC is human-only, it carries the blocked label, or it is gated by an open todo (Blocked by #NN). Two mechanisms keep such items from wedging a run:

Queue exclusion (before selection). Blocked work never surfaces as runnable in the first place:

  • discover-queue.sh enforces the whole membership rule — approved, the req/bug type filter, and the blocked exclusion — in one shared jq expression, so every consumer inherits it identically regardless of how the rows were fetched (#1794). Passing --label approved to gh is a server-side narrowing optimisation on top of that, never the gate itself.
  • discover-queue.sh (and everything built on it — warp-drive discovery, /what-next, the discovery cache) excludes blocked-labelled issues from the runnable queue. Removing the label restores the issue; a label flip also changes the discovery-cache fingerprint, so a cached queue can never serve a newly-blocked item.
  • flightplan.js remaining excludes plan items gated by any open todo — in-plan todo rows and off-plan todos whose body names the item in a Blocked by #NN line. The standard completed done-signal releases the exclusion (an open todo labelled completed imposes no gate; one closed with completed leaves the open list). A gated serial-only item additionally acts as a barrier: the run order is truncated there and every later item is withheld (#1415) — see flightplan.md.

The skip path (after selection). If a blocked item still reaches planning (e.g. it was blocked after the queue was fetched, or the blockage is only visible in the issue body), the loop fires work_skipped instead of aborting: the item is recorded to state.skipped_issues as {issue, reason} and the run returns straight to discovering for the next item — state-only, with no commit and no prerequisites re-run. Skipped issues are excluded from re-discovery for the remainder of the run, so the loop can never re-select the same blocked item in a loop.

Skips are surfaced everywhere the run reports: warp status shows a Skipped: line (issue + reason, omitted when none), and the session summary includes a “Skipped (blocked)” section. The skip exit exists only at planning — blockage discovered mid-coding (after edits exist) stays on the error-escalation/todo path, which defines cleanup.

Once running, warp-drive works through phases automatically. At Level 2/3, it only interrupts you for project decisions — architecture questions, ambiguous requirements, new dependencies, or scope expansion. Everything else is autonomous.

Each chunk produces:

  • Code changes implementing 1-3 acceptance criteria
  • A test run
  • A conventional commit (feat(scope): description)
  • A chunk report filed as a GitHub Issue

After all chunks are done, warp-drive:

  1. Runs a final test
  2. Merges (L2: creates PR / L3: direct merge to master)
  3. Asks “Continue to next task?”
  4. If yes → discovers next approved issue
  5. If no → files a session summary and exits

The warp command is a terminal tool for observing and controlling warp-drive from outside Claude Code. Think of it as docker ps for the autonomous loop. It is Terminal only (there is no slash equivalent) — and it observes/controls a running loop rather than starting one; use /warp-drive to start.

Terminal:

Terminal window
warp status # What phase is it in? What issue? How many commits?
warp status --log # ...plus the phase-history log below (works with --watch too)
warp log # Full phase transition history
warp viz # Open the live session graph page in the browser (alias: warp status --web)
warp viz --tailnet # ...serve it on this machine's Tailscale address (watch a farm run from a laptop)
warp config # Show workflow settings for this project
warp stop # Graceful abort (moves to aborted phase)
warp stop --hard # Emergency reset (deletes state file)
warp session start <branch> [--area <slug>|--flightplan <N>] # Start an integration-branch stream, optionally scope-bound (see below)
warp session end # Clear the session branch + scope binding
warp session status # Resolved branch config
warp finalize [--dry-run] # Ship the integration branch to main
warp help # Help text

Shows the live state of the loop:

WARP DRIVE ACTIVE
Project: bodmail
Phase: coding
Issue: 42
Branch: feature/issue-42-email-parser
Level: 2
Area: email
Host: warp-seg-bodmail-2 (tmux — tmux attach -t warp-seg-bodmail-2)
Preflight: PASS on bodmail-mac — host:converged, project:READY · re-checks: 1
Chunk: 2 / 5
Commits: 1 Chunks: 1 Tests: 3 Reports: 1
Started: 2026-02-18T14:30:00Z
PID: 12345

If the Claude session has ended but the state file remains, it shows (stale).

The Host row (#1742). An active run always says where it lives, so a blank spot can no longer mean either “not tmux-hosted” or “the lookup failed”. The resolution order is: the tmux session recorded at kickoff → tmux resolved from the run’s PID by walking its process ancestry (covers sessions predating the recording) → the iTerm window recorded at kickoff (iTerm · <profile>) → detached when none of those is known. Where the host can be re-entered, the row shows the command that does it (tmux attach -t <session>).

The Preflight row (#1923). The preflight gate’s last verdict for this run: PASS / FAIL / ERROR, the machine it graded, each check’s verdict, the todo it filed or bumped when blocked (todo #77 (bumped)), the number of mid-session host re-checks ((last FAILED) when the latest one blocked) and, after a mid-session block, the phase a retry resumes at. It reads not yet run until the gate has decided — the same .preflight object that state-machine.js status emits.

The same resolution backs the warp viz status card — both read scripts/warp-drive/host-resolve.js, so the CLI and the page cannot disagree about where a run is. An idle project has no host row at all, and an active run whose resolver cannot be reached reads unknown (host resolver unavailable) rather than going silent.

warp viz opens the live session graph page (/warp on the project’s dashboard server) — the engine’s phase topology as an SVG, with the traversed path highlighted and the current phase pulsing, streaming over SSE.

Watching a farm-hosted run from a laptop (--tailnet, #1736). A run on the farm needs a monitor you can open from wherever you are. warp viz --tailnet (equivalently cds --tailnet, or DASH_BIND=tailnet in a service definition) serves the monitor on that machine’s Tailscale address instead of loopback and prints the resulting URL — which is the whole interface when you are on the far end of an SSH session:

Terminal window
ssh farm-01
cd ~/projects/bodfeed && warp viz --tailnet
# http://farm-01.tail3bfead.ts.net:6152/warp <- open this from the laptop

No port forwarding, no public DNS, no reverse proxy: the tailnet is the transport, exactly as it already is for the cross-machine audit (#415). Per-project ports are unchanged — the URL’s port is still the project’s +2 band slot from the ledger, so several projects can be served at once on the same box.

The bind is deliberately narrow. Tailnet mode binds only the tailnet address (loopback is not bound in that mode, and neither is the LAN or public interface — on a farm box with a routable public IP, that address answers nothing). The resolved address must fall inside Tailscale’s CGNAT range or the server refuses to start, and Tailscale being down is likewise a refusal naming its reason — never a fallback to a wider bind. The monitor is unauthenticated and shells out to git/gh on the host, which is why it is never exposed further than the tailnet; the full reasoning, and what would have to change to revisit it, is in Project Monitor Exposure Posture.

Deploy-fresh server reuse (#1603). A running dashboard server is reused only when it is running the currently deployed code. The server exposes a deploy stamp at /warp/server.json (the mtime of the script the process loaded); before reuse, warp viz runs scripts/dashboard/server-fresh.sh <port> [host] against it and restarts the server when the deployed file is newer than the stamp — or when the stamp endpoint 404s on an otherwise-alive server (a pre-stamp process is stale by definition). Without this, a long-running server would keep serving pre-deploy routes and templates indefinitely (Node caches modules at process start — the same pitfall as the launchpad :7777 server).

Live self-update (#1734). The launch-time probe alone still left the monitor a command you re-run: an already-open page had no way to learn the server underneath it changed. Both halves now watch continuously. The server stats the on-disk script it loaded (15s tick, DASH_SELF_WATCH_MS override) and, when a deploy rewrites it, marks its stamp stale, waits for a second stable mtime reading (a mid-write deploy never launches a half-copied script), then restarts in place — SSE clients are ended so their retry: hint reconnects them to the successor spawned on the same port. The page captures the deploy stamp at load and re-probes it every 15s plus on every SSE reconnect; a changed stamp reloads the page (?root= rides the URL; theme, view, and active tab persist in localStorage). Anti-thrash: the page holds off while the stamp reports stale (reloading then would refetch the old code) and caps auto-reloads at one per 30s, so a flapping stamp can never loop. The utility tabs are generated from a single PANES registry — each row declares its endpoint, renderer, and (optionally) a relevant(st) predicate evaluated on every state push — so a pane that becomes relevant mid-session (a flight-plan kickoff, the CI tab to come) appears without a reload, and the next pane is one registry row rather than edits scattered per feature.

The page has two views, switched by the view: iso/flat header button (persisted in localStorage, per browser):

  • iso (default) — the layered phase DAG projected onto a 2.5D isometric staircase, using both screen axes so the whole graph fits a typical viewport (1440×900) with no scrolling. Loop-back edges arc through the open area above the staircase; traversal counts, visited/current highlighting, the side lane, and the stale grayscale treatment all behave exactly as in flat view. The view scales to fit the window down to a legibility floor (0.6×); a graph too large even for that keeps the floor and scrolls inside the graph pane instead of shrinking further or clipping.
  • flat — the original vertical spine layout; taller than a screen for the full engine topology, but with strictly top-to-bottom reading order.

Both layouts are pure functions of /warp/graph.json — live state colors the picture but never moves nodes, so the shape is stable across sessions.

Fleet links (#1744) — navigation, not aggregation. The sidebar’s This project elsewhere section lists the fleet’s machines (from machines.json, the existing registry) and links each one to this project’s monitor there, using the machine’s tailnet address and the project’s declared port band. The current machine is marked and not linked.

It is a row of hyperlinks and nothing more. Nothing is probed to build it, so it makes no liveness claim — no status dot, no phase, no as-of stamp — and a machine that is down or off-tailnet simply fails when you click it, at no cost per refresh. Where the port ledger has no entry for the project, the row says the port is unknown rather than composing a plausible URL. A single-machine fleet, or an unreadable registry, hides the section entirely. Rendering remote sessions (#1737) is a different problem — it must not present stale remote state as live — and is deliberately not what this row does.

Host row on the status card (#1742). The card’s host row answers the same question warp status does, from the same resolver: the tmux session, the recorded iTerm window, or detached, labelled with the kind, plus an attach row carrying the command when the host can be re-entered. See warp status for the resolution order.

Work row on the status card (#1825). The card’s work row is the issue reference alone — #906, linked to the issue — and asserts no work type. The type it used to print came from work_type, which the status payload defaults to req whenever the loop did not detect one, so a bug run read work req #906 with nothing in the UI marking the word as a default rather than a fact. The type is still shown where it is genuinely known: the issue carries its own bug/req label, and a real bug run gets bug phase names on the graph and in the card’s phase row.

Phase tooltips (#1740). Node labels are the engine’s raw phase names, so hovering a node explains it: what happens in that phase, what advances it, how long the session has spent there, and a link into that phase’s row in Phase Details (new tab). The same nodes are reachable by keyboard — Tab to focus, Escape to dismiss — and the tooltip flips and clamps to stay inside the viewport in both views. In bug mode it names the overlay label and the underlying engine phase, and revalidating is explained rather than silently substituted (see Phase overlays). The copy is served with the topology on /warp/graph.json (phase_meta, sourced from PHASE_META beside the engine’s PHASES), never duplicated into the page: a phase the engine adds without metadata falls back to the plain name-only tooltip rather than showing something wrong.

Session switcher (#1587). On a cdfork fan-out, a header dropdown appears whenever more than one session is detected (via /warp/sessions.json, the same scanner warp status uses), listing each as name — phase; picking one flips the graph by navigating the existing ?root=<worktree> parameter, and the adjacent all sessions link opens the /warp/sessions fleet index. If the session you’re viewing ends or its worktree disappears, the page falls back to the primary session automatically. With a single session the header is unchanged — no switcher chrome.

Attention badge (#1586). When the run is waiting on a human — the budget_exceeded checkpoint at any level, or the Level-2 “continue?” prompt — a backgrounded viz tab signals it: the favicon switches to an amber alert variant, the title gains a (!) prefix, and the banner names the reason with a link to the requirement issue. All of it derives from the read-only attention block in the engine’s status payload, arriving over the existing SSE stream (no polling). The notify: header button opts in to a desktop notification on the transition into the waiting state — the browser’s Notification permission is requested only when you click the button, never on page load.

Token burn-down (#1585). The sidebar’s Token burn section shows the session’s token spend at a glance: the current spend total, and — when a budget policy applies (a kickoff --budget/--max-tokens ceiling or _workflow.max_session_tokens/max_session_usd) — the remaining budget as an absolute figure and percent, with a usage bar that shifts to amber past 75% used and red past 90%. Below the figures, a compact inline-SVG sparkline traces cumulative spend across the session’s chunk snapshots, updating live with the rest of the page. The data is the existing token-snapshot machinery, delivered as a budget block inside token_usage in /warp/state.json / the SSE payload and resolved through the same budget-policy.js helper the cost breaker enforces — the display and the enforcement ceiling can never disagree. The section hides itself when the session has no snapshot data yet.

When a req is active, the sidebar shows the issue’s acceptance criteria as a live checklist with a done/total summary — mirrored from the GitHub issue body (/warp/acs.json) and refreshed as warp-drive checks items off, so the page doubles as a run progress bar. Each line links to the req issue; the section hides itself when no req is active, the issue has no checkboxes, or gh is unavailable (#1584).

The page header is titled per project — the project name alone, since the page is the project’s monitor and the warp session is one card inside it (a ?root=<worktree> view is titled by that worktree’s project; #1733) — and issue references in the session sidebar, gitlog subjects, and feed entries are hyperlinks to the GitHub issue; commit hashes link to the commit on GitHub. Links resolve from the repo’s origin remote — a repo with no remote shows plain text instead (#1583).

Utility pane (#1583, #1602). A gitlog/tree/feed/ci (plus, on flightplan-scoped runs, flightplan) pane whose presentation follows the view mode. In iso view it is a collapsible overlay at the lower-left of the graph area; its collapsed/expanded state and last-active tab persist in localStorage like the theme and view toggles. In flat view it instead docks as a full-height first column to the left of the graph (the graph shifts right to make room) and is always expanded — the persisted collapse state is neither applied nor overwritten while docked, so iso’s preference survives flat↔iso round-trips, and the active tab carries across view switches (#1602). The pane is hard-capped at a third of the viewport (max-width: 33vw) in both modes, and a long gitlog subject or feed title scrolls horizontally within its own row instead of wrapping or widening the pane — message length never dictates pane width. The pane’s inner edge is grabbable (col-resize cursor on hover): dragging resizes it between a 180px floor and the 33vw cap, the chosen width persists for the browser session, and dragging to/near the minimum collapses the pane in the floating (iso) mode — docked stays always-expanded per #1602 and simply clamps at the floor (#1627). The tabs, each backed by a read-only server endpoint that honors ?root=:

  • gitlog (/warp/gitlog.json) — recent commits: short hash, subject, relative age; refreshed by light polling while the pane is open.

  • tree (/warp/difftree.json) — the uncommitted working set (staged + unstaged + untracked) as directory groups with per-file +adds/-dels counts (additions green, deletions red); when the tree is clean it shows the HEAD commit’s files, labelled as such. When the repo has linked git worktrees (a cdfork fan-out), the tab adds a section per worktree — branch + path, then that worktree’s changed files via the same per-root diff — so the whole fan-out’s working state is visible from one page; a worktree whose diff can’t be read (pruned/locked) degrades to an inline unavailable note for its own section only, and a repo with no linked worktrees renders exactly as before (#1628). The base checkout is a section too (#1741): its files sit under the same header treatment, showing its branch, a · current marker naming it as the page’s own root, and the root path when there are other sections to tell it apart from — so a fan-out reads as one list of labelled sections rather than anonymous files followed by named worktrees. The branch is resolved server-side from the resolved root, so a ?root=-scoped page names that worktree’s branch; a detached checkout reads as its short SHA rather than the literal HEAD. A clean tree with no linked worktrees still renders the plain no changes empty state.

  • feed (/warp/issues.json) — recently updated GitHub issues (number, title, labels, relative age), newest first. The feed degrades to a quiet empty state when gh or the network is unavailable — the endpoints never write via gh.

  • ci (/warp/ci.json, #1735) — the repo’s delivery pipeline: recent workflow runs with status glyph, workflow · branch · event, duration, start age, and a runner-locus badge (runner name such as farm-01, or hosted derived from the labels), so the CI-locus rollout is observable from the monitor. The current branch’s run is highlighted; a failing run names its failed job/step inline and links out to the run; a queued run whose requested self-hosted labels no online runner carries is flagged loud (⚠ queued — no online runner) — the silent-stall condition. Strictly read-only. API budget: one gh run list per 30s while the pane is open, jobs enrichment capped per cycle and memoized permanently for completed runs, runner roster every 120s — steady-state ≈ 2 requests/min per project, so a monitor left open all day stays far under the REST limit. Degraded states are distinct and readable (gh missing / unauthenticated / rate-limited); the tab hides only for a project with no CI workflows, via the #1734 pane registry. If CD lands (#1725 option D), deploy rows join this pane as a second section.

  • flightplan (/warp/flightplan.json, #1644) — visible only when the session is flightplan-scoped (session.kickoff.flightplan; the tab hides itself otherwise, falling back to gitlog if it was active). Renders the plan’s ordered item checklist in the same style as the status card’s AC checklist — ☑/☐, item number + title, each row linked to its issue, headed by flight-plan #N — done/total. The server merges flightplan.js items with the remaining --all lifecycle classification (#1666): held items (gated / withheld / waiting / moot, plus human todo gate rows) render dimmed with a state tag, so a plan stalled on a human gate is visibly different from one with plain unworked items. Plan data that can’t be derived (gh/network down) degrades to an inline notice, same convention as the feed — the payload answers {plan: N, unavailable: true}, never a well-formed 0/0 checklist, so “the plan is empty” and “we could not read the plan” stay distinguishable (#1780). That guard lives in the derivation itself (scripts/dashboard/flightplan-view.js), which treats any non-array items as unreadable, so every failure path reports one degraded shape.

    Done-ness comes from live state, not the checkbox (#1757). An item that has landed and closed on GitHub but whose plan checkbox is not ticked yet appears in no remaining bucket — the gate resolver drops closed items by design — so it used to fall through to the runnable default: the tab invited you to work something already merged, and the counter read 0/N for a whole run. The payload now also carries flightplan.js lifecycle’s untickedClosed, and such a row renders landed: ticked, counted in done/total, and tagged, with the header naming the lag (4 not yet ticked in the plan). landed is kept distinct from done on purpose — the plan body genuinely is stale at that moment, and the tag disappearing is how you see reconcile catch up.

    Ticking itself no longer depends on the agent remembering: the transition into awaiting_continue runs the #1068 check-off for the finished requirement (_workflow.flightplan_tick_on_item_boundary, default on). It is idempotent, and best-effort — a gh failure is reported in the transition output rather than blocking the loop, and the session_ending reconcile still sweeps whatever it missed.

If a tab’s endpoint fails before it has rendered anything (404 from a stale server, network error), the pane replaces the loading… placeholder with an explicit “data unavailable” note naming the failure and the likely fix (restart via warp viz); once a tab has rendered real data, a later failure keeps the last good content instead (#1604).

The Area: line appears only when the session is area-scoped (/warp-drive --area <slug>); an unscoped run omits it (#613). The Scope: line shows the run’s authorized reach (#992): issues #42, #43 (1 done, 1 remaining) for an issue-scoped run, flight-plan #N for a flightplan-scoped one, full approved queue for an unscoped one (an area-scoped run’s reach is the Area: line). A scope inherited from a stream binding (#1177) shows the same way. The warp viz status card carries the same reach, split across two rows (#1644, #1825): a flightplan row linking #N to the plan issue for a flight-plan run, and a scope row worded exactly as the Scope: line for an issue-scoped (issues #42, #43 (1 done, 1 remaining)) or unscoped (full approved queue) run. Each row is omitted entirely — no placeholder — when it has nothing to say, so an area-scoped run shows neither (its reach is the card’s area row) and a flight-plan run is never described twice.

The Tmux: line names the tmux session hosting the run, so you can find where it lives — invaluable across a cdfork fan-out where several sessions run at once (#990). It is recorded at kickoff when warp-drive starts inside tmux (the declarative source of truth); for sessions that predate this it is best-effort resolved by matching the recorded PID against tmux pane ancestry. It is omitted entirely when the run is not tmux-hosted (same omit-when-empty convention as Area:). The warp viz status card shows the same tmux row, resolved in the same order (recorded kickoff context first, PID-ancestry fallback) via the page’s state.json/SSE payload, and likewise omitted when the run is not tmux-hosted (#1628). When more than one active warp-drive session is detected on the machine (e.g. cdfork worktrees), warp status lists every session — project, branch, phase, and tmux name — so a single warp status surfaces the whole fan-out, not just the current project’s run.

Live/watch mode (#612). Pass --watch (aliases: --live, -w) to re-render the status on a fixed interval instead of printing once — handy parked in a tmux/iTerm split:

warp status --watch # refresh every 5s (default)
warp status --watch --interval 2 # refresh every 2s
warp status -w # short alias
warp status --watch --log # also show the phase-history log below

--interval <seconds> tunes the cadence (default 5s; non-numeric input falls back to the default, sub-1s clamps to the 1s floor). The view redraws in place (no scroll spam) and exits cleanly when the session ends, goes stale, or is aborted — leaving a one-line summary on screen. With no active session it prints the usual “No active session” message and exits. Ctrl-C exits cleanly and restores the cursor.

--log (#1003). Pass --log to append the phase-history log (the same table warp log prints) beneath the standard session info. It works on both the one-shot warp status and the live --watch view — in watch mode the log re-renders each tick, so new transitions appear in place as they happen. When the session has no recorded history the log section is omitted entirely (no empty header).

From outside Claude Code, warp stop transitions the state machine to the aborted phase. The next time Claude Code starts in that project, it picks up the abort and runs cleanup (WIP commit, abort report).

warp stop --hard deletes the state file immediately. No cleanup, no report. Use this when the state file is corrupted or you just want a clean slate.

Shows every phase transition with timestamps:

Phase History (bodmail)
# Phase Event Time
── ──────────────────────── ─────────────────── ─────────────────────
0 prerequisites init 2026-02-18T14:30:00Z
1 discovering prerequisites_ok 2026-02-18T14:30:05Z
2 planning work_selected 2026-02-18T14:30:12Z
...

Shows the current workflow tuning for the project — automation level, RDB status, and all _workflow settings with their defaults.


By default warp-drive merges (or PRs) each requirement to main on its own. For a cluster of related requirements — a capability rollout, a multi-part feature — you often want to accumulate them on one branch and ship once. That’s an integration-branch stream (capability #268).

  • The requirements are tightly coupled (shared files, a staged feature) and you’d rather review/ship them as a unit than as N separate merges.
  • You want main to stay clean until the whole stream is green, with a single consolidated PR (L2) or one rebase+merge (L3) at the end.

Use a plain per-requirement merge (the default with no session branch) for independent issues.

The stream controls are Terminal only (warp session / warp finalize have no slash form); the loop itself is still started with /warp-drive inside the session, as noted in step 2.

Terminal:

Terminal window
# 1. Open the stream — creates the branch off main and records it as _branch.session.
# Bind the stream's scope at the same time (#1177) so kickoffs line up automatically:
warp session start integration/billing-rollout --area billing # or --flightplan <N>
# 2. Run the loop over the cluster (each requirement merges into the session branch, not main)
# On a bound stream a plain /warp-drive defaults to the bound scope — no flags needed.
# An explicitly different --area/--flightplan is refused; --issue <N> is allowed (narrower).
# 3. Inspect at any point
warp session status # main / session / current / scope binding / merge target & strategy
# 4. Ship the accumulated branch in one shot
warp finalize --dry-run # preview the exact git/gh commands
warp finalize # L2: one consolidated PR --base main | L3: freshen (merge main in), merge to main, push
# 5. (If finalize didn't already) clear the stream
warp session end # removes _branch.session + the scope binding; the git branch is left untouched

A stream is freshened from main at every start-of-work seam — warp-drive kickoff (prerequisites), each segment resume, and warp session start on an existing branch — via the shared scripts/warp-drive/stream-freshness.sh. The sync merges main into the stream (never rebases — the stream is pushed; direction fixed by the #1179 policy): a single fetch + ancestry check makes the up-to-date case a fast no-op, a behind stream absorbs main routinely instead of all at once at finalize, and clean syncs are recorded in the run’s phase history (warp status --log shows stream_sync(synced)). A conflicting sync is a blockable event: the merge is aborted with the stream untouched, and the loop files a todo naming the conflicted files and stops — the standard completed-label contract resumes the stream once a human resolves. From the warp session start CLI the same conflict refuses the start with the files named.

The freshness pass also ends on the stream branch: if a binding is active but HEAD is elsewhere (main, a stale child branch, detached HEAD), stream-freshness.sh checks the stream out ("checked_out": true in its JSON) so a kickoff never silently works off-stream. A dirty working tree that makes git refuse that checkout is a distinct blockable signal — status blocked, exit 4, HEAD and local changes untouched — and the loop files a todo / prompts per level rather than continuing on the wrong branch. On the enforcement side, hooks/session-branch-guard.sh (active whenever _branch.session is set) additionally refuses git commit on main or on any branch that is neither the stream nor a child of the stream; per-requirement child branches — which merge back into the stream via the LOCAL accumulate path — remain allowed, and the refusal message names the bound stream. check-branch.sh (the edit-on-master guard) is unchanged.

warp session start <branch> --area <slug> / --flightplan <N> records the scope binding as _branch.scope alongside the branch pin (per-checkout, in settings.local.json) — see the branch-config reference for the schema and enforcement rules. In short: an unscoped /warp-drive on a bound stream defaults to the bound scope (so unrelated queue work can never land on the stream branch), a conflicting explicit scope is refused with the override paths named, and the binding survives session end, segmentation, and restarts until warp session end / warp finalize clear it with the pin. Because it is per-checkout, cdfork worktrees each carry their own binding — parallel streams on different areas coexist.

When a session branch is set, merge_target != main, and _workflow.session_merge is local (the default), the merging phase accumulates locally: it merges each requirement’s child branch into the session branch per merge_strategy, pushes the session branch, deletes the child branch, and never touches main / opens no PR. This applies at both Level 2 and Level 3.

Set _workflow.session_merge to pr to keep the legacy behavior (a PR or direct-to-main merge per requirement) even with a session branch. With no session branch set, behavior is unchanged regardless of the value.

session_merge session branch set merging behavior
local (default) yes merge child → session, push session, delete child; main untouched
pr yes per-requirement PR (L2) / direct-to-main (L3)
any no unchanged legacy behavior

warp finalize refuses to run when there is no integration branch or while a warp-drive session is mid-stream (it only proceeds with no active state or at awaiting_continue/completed, so it never ships a half-done requirement). On a conflict, push, or auth failure it aborts cleanly and leaves _branch.session (and any _branch.scope binding) intact so you can fix and retry. --dry-run prints the exact commands without executing them.

Finalize and start-of-work freshness are one coherent policy: merge-in, never rebase. The L3 finalize path freshens the stream through the same stream-freshness.sh engine (a rebase would replay the stream’s absorbed sync-merge commits into mangled history), then ships to main with the configured _branch.merge_strategy flag — ff-only (default) works because a freshened stream strictly descends from main, and preserves the sync merges in history; configure merge-commit for an explicit --no-ff ship commit. Rebase applies nowhere in warp tooling — it is acceptable only manually on a never-pushed branch. Full policy: branch-config reference.


A multi-hour autonomous run degrades in quality as one Claude context accumulates hours of history. Session segmentation lets a Level 3/4 run checkpoint at a clean per-requirement boundary, end the session, and resume in a fresh context — so quality stays high across an arbitrarily long queue. It pairs naturally with an integration-branch stream: each segment keeps accumulating requirements onto the same session branch, and you warp finalize once at the end.

It reuses the substrate that already exists — the persisted state file, the warp-drive-inject.sh resume hook, and the cdfork-style fresh-process spawn. There is no new resume engine.

It is on by default (segment_after_tokens: 120000, #959): usage analysis showed 73% of spend happens at >150k context, and segmentation-at-requirement-boundary is the loop’s equivalent of “/clear when switching tasks”. Short sessions are unaffected in practice — a run that never crosses 120k tokens in a segment never segments. Set an explicit _workflow.segment_after_tokens: 0 to opt out.

At awaiting_continue (a requirement is done, merged onto the integration branch, git clean) a Level 3+ run consults the segmentation decision before continuing:

  1. Threshold — if the tokens or chunks accrued since the last checkpoint cross _workflow.segment_after_tokens / segment_after_chunks, the run segments; otherwise it continues in-process exactly as before. Seams lower the bar (#960): when the next queued item belongs to a different cap (Part of #NN) or area: label than the one just finished, the lower segment_at_seam_tokens bar (default 40000) applies instead — warm context is worth less across a semantic boundary. A dimension only counts when known on both sides; missing metadata never seams, and an --area-scoped run has no area seams by construction (cap seams within the area still can). Seam-triggered segments are marked kind: "seam" in the decision and checkpoint reason.
  2. Checkpointstate-machine.js checkpoint records the boundary on the state file (appends session.segments[], anchors the per-segment counters) without changing phase or deleting state. It requires a boundary that is clean of work — see What a checkpoint counts as dirty.
  3. Fresh spawnsegment-spawn.sh launches a fresh claude -a<level> "/warp-drive" in its own per-segment warp-seg-<repo>-<N> tmux session (mirroring cdfork). The current session stops; the prior segment’s now-idle session is reaped at the next handoff.
  4. Resume — the fresh session’s SessionStart inject hook reads the still-present state file, sees the segment lineage, and continues the same queue on the same branch. Nothing is re-passed on the command line — all kickoff options live in the persisted state, so no state is lost.

Segmentation only fires when more work remains within the run’s kickoff scope (#992): an issue-scoped run whose issue(s) are done reports moreWork: false and routes to a clean session end instead of handing the approved queue to a fresh self-spawned session — the incident that motivated the scope rule.

The trigger measures per-segment usage (delta since the last checkpoint), never whole-run cumulative — otherwise the persisted state would re-trip the threshold immediately in the fresh session and loop forever. warp status surfaces the thresholds, the per-segment usage so far, and the checkpoint lineage under segmentation. See the state reference for the fields and CLI actions.

A --flightplan run applies a plan-scoped provision overlay at prerequisites and only restores it at session_ending. Applying it rewrites the cdprov-managed ignore block in the tracked .gitignore, so the tree carries a diff for the entire window in which segmentation can fire. A checkpoint that simply asked “is the tree clean?” therefore refused every time — segmentation was effectively off for precisely the long runs it exists for.

The checkpoint classifies each dirty path instead:

What it found What happens
The overlay’s own ignore-block edit Tolerated — this is bookkeeping, not work, and the checkpoint proceeds.
Changes that pre-date the session Refused (kind: "preexisting") — the run cannot commit your changes for you. Commit or stash them outside the run, then let it retry.
Anything else Refused (kind: "inflight") — the requirement is not committed yet. Unchanged from before.

The two refusals are separated because the action you take is completely different, and the old shared message could not tell you which one you were looking at. When both are present the message reports the in-flight work first — that is the half the run itself owns — and lists the pre-existing paths under also_preexisting.

Overlay tolerance is deliberately narrow. It applies only when an overlay is genuinely active and the working .gitignore matches HEAD exactly once the managed block is stripped from both. So a hand edit elsewhere in .gitignore still refuses, and a run with no overlay is completely unaffected — there is nothing to excuse.

If you hit the preexisting refusal mid-run, the fix is entirely outside warp-drive: git stash push -u -- <the named paths>, let the run finish, then git stash pop. You no longer need the older workaround of restoring and re-applying the overlay around the checkpoint.

segment_after_tokens defaults to 120000 (enabled, #959); segment_after_chunks defaults to 0. The default is resolved in one place (the state-machine config layer), and an explicit _workflow value — including 0 to disable — always wins over it.

Auto-surfacing the continuation on macOS (#996, #1022, #1023)

Section titled “Auto-surfacing the continuation on macOS (#996, #1022, #1023)”

Every re-segment hands the queue to a fresh claude process, and it always surfaces as a new window — never a split of the pane you’re watching (#1022 reversed the earlier pane behavior):

  • A fresh per-segment session, one window each — segment-spawn creates the segment’s own warp-seg-<repo>-<N> session (N = the segment it hosts) as a single-window session, the same path whether or not warp-drive is running inside tmux. It never splits the spawning pane. Attached via iTerm2 -CC, the auto-attach below pops this as a fresh native iTerm2 window near your work.
  • A new window every segment (#1023) — because each segment gets its own session, and a brand-new session is always unattached, the auto-attach fires on every handoff — not just the first. (The earlier shared warp-seg-<repo> session, reused across segments, meant once a -CC client was attached to it later continuations surfaced as tabs within that window; per-segment sessions pop a distinct window each time.) The warp-seg- prefix is unchanged, so warp reap / warp status / _reap_seg_session keep matching.

The fresh segment is then focused (select-pane + select-window by unique id) so an attached client lands on the running segment, never a stale same-named window from an earlier segment.

Because a per-segment session is always unattached, on macOS with iTerm2 running segment-spawn opens an iTerm2 window in control mode (tmux -CC attach -t <session>) so the continuation actually pops on screen — every time. Control mode (not a plain attach) is used deliberately — a plain attach would contend for terminal size against any other client. The first use triggers a one-time macOS Automation permission prompt (iTerm2 controlling itself via osascript); approve it once. iTerm2 is detected via TERM_PROGRAM/AppleScript is running (#1016 — the old pgrep check never matched on macOS).

-CC tab semantics: each tmux window shows as a native iTerm2 tab within its control-mode window. Closing a tab kills that tmux window — to leave the run alive, detach the control-mode client instead (tmux detach), don’t close individual tabs. See iTerm2 tmux -CC.

The surfacing is strictly best-effort and safe by construction:

  • Fresh session, always unattached: each per-segment warp-seg-<repo>-<N> session is brand-new, so a new iTerm2 control-mode window is opened on every handoff — the intended behavior (a distinct window per segment). The already-attached gate (which the shared session tripped for later continuations) no longer applies to continuations, only to a redundant re-spawn onto the same session.
  • Prior segment reaped at handoff: once the fresh segment is live, its resume-time inject reaps the previous segment’s pane by id — which drops that segment’s one-window session, so idle warp-seg-* windows don’t accumulate across a long run. The reap is guarded to warp-seg-* sessions only, so your own launch window (the first handoff’s “previous” pane) is never closed out from under you.
  • Non-GUI / headless: silently skipped on non-macOS, over SSH, or when iTerm2 isn’t running. The new tmux session still exists (it’s just tmux); farm/headless machines are otherwise unaffected.
  • Opt-out: set WARP_SEG_AUTOATTACH=off to disable the iTerm2 window (the tmux session is still created).

If the osascript path fails for any reason, it’s non-fatal — the printed attach: tmux attach -t <session> hint line still stands and the spawn still succeeds.


Warp-drive is a phase-based state machine backed by a persistent JSON file at .claude/.warp-drive-state.json in your project. Hooks enforce phase ordering — commits are blocked outside the committing phase, session exit is blocked without a report.

prerequisites → discovering → planning → chunking
coding → updating_docs → testing → committing → reporting
↑ ↓
└──────────── next chunk ←──── chunk_complete ─────┘
↓ (all done)
requirement_complete → qa → merging
│ ↑ ↓
bugs found ───┘ └─ pass/skip
(file + fix in-session,
loop back to coding)
awaiting_continue
↙ ↘
discovering session_ending
(next task) (done)
─── Circuit-breaker checkpoint ──────────────────────────────
any phase ──(phase_timeout | ┌─ budget_continue → interrupted phase
retry_exceeded | │
coding_cycles_exceeded | │ (resume work where it halted;
no_progress | │ falls back to coding)
qa_cycles_exceeded | │
total_chunks_exceeded | │
session_timeout)──→ budget_exceeded ─┤
└─ budget_abort → aborted
(give up & clean up)
─────────────────────────────────────────────────────────────

The state machine routes any phase into budget_exceeded when an enforced budget trips (see Configuration for the trigger reasons). From budget_exceeded, a budget_continue event resumes at the exact phase the breaker interrupted (captured in budgets.exceeded_from_phase — #1183; falls back to coding for older state files), and each approval extends the chunk cap by 10 (#1149); budget_abort moves to aborted.

The total_chunks_exceeded breaker gates starting new chunk work only (next_chunk, chunks_defined, and the like): wrap-up transitions that land or close work already in flight (merge_ready, qa_passed/qa_skipped, merged and its retries, promoted/promote_skipped/promote_failed, continue_no) are exempt (#1183), mirroring the qa-cap rule — so a session at the cap can always merge, promote, and end cleanly. The cost/token and session-time ceilings are not exempted; they halt everything. The aborted phase has its own cleanup events: abort_resolved (cleanup OK → completed), abort_cleanup_failed (cleanup failed → completed anyway), and restart (back to idle).

Phase What Happens Advances When
idle No run in flight — a state file exists but nothing is being worked start (kickoff)
prerequisites Checks automation level (2+), branch (not main), test command; runs the baseline-docs check (greenfield README/runbook/CLAUDE.md gap → unapproved req, non-blocking) All checks pass
preflight The engine grades this machine (bob doctor, #1921) and this project (bob ready --dry-run, #1922) via state-machine.js preflight — non-mutating; --converge applies the idempotent bob ready steps first, then re-checks. A failure files (or bumps, by fingerprint) one todo naming every failed check and its fix. preflight_ok / preflight_failed are engine-fired only — a hand-fired transition is refused (see Preflight gate) Engine decides: pass → discovering, fail → blocked
blocked Held on a failed preflight (at kickoff, or a mid-session host re-check at next_chunk / continue_yes). The coding loop is unreachable; nothing is compensated by hand — the todo carries the remediation preflight_retry re-runs the gate (a pass resumes the interrupted phase); blocked_end ends the run cleanly
discovering Finds work via the fingerprint-cached queue (discovery-cache.js queuediscover-queue.sh); an unchanged actionable set reuses the cached order (see Discovery caching) Issue selected
planning Reads issue, drafts approach if complex (>3 ACs or architecture decisions); a blocked item (no agent-side work) is skipped state-only via work_skipped → discovering (see Skipping blocked items). Entered across a cap/area seam it runs as the revalidating sub-mode first (see Per-Area Re-Validation) Plan ready (or item skipped)
chunking Splits work into commit-sized pieces (max 3 ACs each) Chunks defined
coding Implements the current chunk’s acceptance criteria Code complete
updating_docs Checks off completed ACs in the GitHub Issue Hook auto-detects
testing Runs project test suite; retries up to 3 alternatives on failure Tests pass
committing Creates conventional commit; hook auto-detects Commit created
doc_drift_check (opt-in) Runs /doc-sync (mechanical fixes) and /doc-audit --fail-on-drift after the commit. Active when _workflow.doc_drift_gate=true. drift_clean → reporting / drift_blocked → coding
reporting Files chunk report as GitHub Issue; adds an “Iteration trail” section when the chunk took >1 verify attempt (#589) Report filed
chunk_complete Checks if more chunks remain Next chunk or done
requirement_complete Final test, close issue via PR Merge ready
qa (#868) Runs Playwright E2E (webapp-testing) against the dev-up app; records a passing run as “fully functioning” evidence before promotion. Bugs found are filed as bug issues and fixed in-session (reproduce → regression test → fix), then QA re-runs. Clean no-op skip when there is no dev/E2E surface. Bounded by max_qa_cycles (default 3) → budget_exceeded. qa_passed/qa_skipped → merging; qa_bugs_found → coding
merging L2: push + PR / L3: rebase + push master Merged
promoting Deploys to the project’s promotion ceiling through the shared gate (promotion.js decidedeploy.sh); an external/pr ceiling is a clean no-op promoted/promote_skipped → awaiting_continue; promote_failed retries
awaiting_continue Asks: “Continue to next task?” User responds
session_ending Files session summary, notifies, cleans up state Done
budget_exceeded Mandatory human checkpoint reached by a tripped breaker (cost, tokens, chunk cap, QA cycles, no-progress stall) — see Cost Budget budget_continue resumes at the interrupted phase; budget_abort → aborted
aborted Run aborted; cleanup runs from here. Reachable from any phase abort_resolved/abort_cleanup_failed → completed; restart → idle
completed Terminal — the run is over and the state file no longer drives a loop Nothing; a new kickoff starts a fresh run

Two overlays relabel a node in place rather than adding a phase, so the topology stays one graph:

  • Bug work — when the selected item carries the bug label the loop reuses the same phases, relabelled: coding shows as bug_fixing, testing as regression_gate, requirement_complete as bug_verified. The semantics that come with the relabel are the bug contract — reproduce the failure first, root-cause it, and land a regression test that failed pre-fix and passes post-fix. The tooltip names both the overlay label and the underlying phase.
  • revalidatingplanning entered across a cap/area seam runs the freshness re-check first (see Per-Area Re-Validation) and shows as revalidating until the pass is recorded.
Behavior Level 2 Level 3
Branch creation You create via /start-work Auto-creates feature/issue-NN-title
Merge strategy Creates PR for review Direct merge to master
Decisions Asks for architecture, scope, ambiguity Same
Continue prompt “Continue to next task?” Same
File operations Auto-approved Auto-approved
Destructive git Blocked (force push, reset –hard) Blocked (force push, reset –hard)

Before advancing past key phases (code_complete, tests_passed, requirement_done), a hook injects a verification prompt forcing Claude to re-read the original acceptance criteria and confirm each one is genuinely satisfied — not just that code exists.

A PostToolUse hook tracks how many times each file is edited during a phase. If any file exceeds the threshold (default: 5 edits), a warning is injected. This catches infinite edit cycles where Claude keeps tweaking the same file without making progress.

Distinct from the raw retry caps, the state machine watches for stagnation on the coding ↔ testing and coding ↔ doc_drift_check loops (#267). On every tests_failed / drift_blocked it computes two signatures:

  • a diff signature — a hash of git diff HEAD plus the untracked-file list (captures whether any code changed since the last attempt), and
  • a failure signature — a hash of the short failure string passed via --data '{"failure":"…"}' (the failing test name or first error line).

A per-chunk stall counter increments only when both are unchanged since the previous attempt — i.e. the same failure with no new diff. Any change to either (different error, or the diff moved) is treated as legitimate slow progress and resets the counter to zero. This is the key distinction the raw coding_cycles count can’t make: it halts on stagnation, not on attempts.

When the counter reaches max_no_progress (default 3), an enforced no_progress budget issue trips the circuit breaker into budget_exceeded. At Level 3 this is a mandatory human checkpoint — the loop will not spin on an unsolvable chunk burning budget; it stops and asks. budget_continue clears the guard and resumes; budget_abort cleans up. The counter also resets whenever a new chunk begins or the loop makes real headway (tests_passed, drift_clean).

Always pass the failure signature on tests_failed/drift_blocked; without it the detector falls back to the diff signature alone (still catches “no code changed”, just less precisely).

The circuit breaker also enforces a spend ceiling (#587). Token usage is tracked per chunk and per session; checkBudgets() compares the cumulative session total against max_session_tokens and, when crossed, emits an enforced cost_budget_exceeded issue that routes to budget_exceeded — the same mandatory Level-3 checkpoint as the other hard limits. This closes the gap where a runaway loop could burn budget while staying under every count-based cap.

  • Token count is the primary signal. The check reads token_usage.session_total when captured, else the running sum of chunk_snapshots, so it trips mid-session on accumulated spend rather than only at session end.
  • max_session_usd is optional/secondary — an estimated-dollar ceiling via token-report’s shared cost model. Prefer the token ceiling; the dollar estimate is best-effort.
  • Disabled by default. Both ceilings default to 0 (off), so existing sessions are unaffected unless you configure them under _workflow in settings.local.json.
  • The budget_exceeded checkpoint reports actual spend vs ceiling (e.g. cost_budget_exceeded: 1500 tokens exceeded 1000 tokens) and offers the usual budget_continue (extend) / budget_abort choice.

Two ways to stop — both reach the same state machine.

Slash command (from inside Claude Code):

/stop-warp-drive # Graceful: WIP commit, abort report, cleanup
/stop-warp-drive reset # Hard: delete state file immediately

Terminal (from any shell):

Terminal window
warp stop # Graceful abort
warp stop --hard # Hard reset

Graceful stop does:

  1. Transitions to aborted phase
  2. Creates a WIP commit for uncommitted changes
  3. Files an abort report as a GitHub Issue
  4. Cleans up the state file

Hard reset does:

  1. Deletes the state file
  2. That’s it — uncommitted work stays in the working tree

Warp-drive auto-resumes. When you start a new Claude Code session in a project that has a .warp-drive-state.json, the SessionStart hook injects the current phase context and Claude picks up where it left off.

If the state is stale (the original Claude process has ended), the session still resumes — it just notes the staleness.

When you try to exit Claude Code (/exit, Ctrl+D, etc.) during an active warp-drive session:

  • Report pending? Exit is blocked. File the chunk report first.
  • Other active phase? Exit is allowed with an advisory: “Consider running session end protocol before stopping.”

For autonomous sessions where you’re away from the terminal.

Slash command:

/rdb on

Terminal (the -rdb startup flag on the claude shell wrapper enables it when you launch a session):

Terminal window
claude -rdb

This enables the Remote Decision Bridge. All decisions go to Telegram instead of the terminal, and every phase transition sends a notification.

  • Phase transition notifications: [warp-drive] coding -- chunk 2/5 for #42
  • Decision prompts when Claude needs input
  • Completion notice: [warp-drive] session ending -- 4 commits, 3 chunks

If warp-drive needs your input (terminal or Telegram) and you don’t respond within 10 minutes:

  1. It files a GitHub Issue labeled todo, assigned to you, describing the decision needed
  2. Skips the blocked item and continues to the next chunk
  3. If nothing else to do, transitions to session ending

You’ll find the TODO in your issue list when you’re back.

Tell Claude you’re stepping away — it treats that as /rdb on:

“I’m going to grab lunch, keep going”


When a project has a dev.json at its root, warp-drive automatically manages the dev environment throughout the coding loop.

Phase Action Script
prerequisites Full dev-up: start server, run migrations, seed data, provision users dev-up.sh "$(pwd)" --verbose
coding Seed coverage check (advisory — warns if schema changed without seed data) check-seed-coverage.sh "$(pwd)"
chunk_complete Fast health check before next chunk dev-up.sh "$(pwd)" --check
chunk_complete (recovery) Full dev-up if health check fails dev-up.sh "$(pwd)" --verbose

If the dev server goes down during coding and can’t be recovered after 2 attempts, warp-drive treats it as a blockable event and escalates (see Recovery).

Terminal (Terminal only — copying the template is a plain shell step; once dev.json exists, /dev-up in-session or dev-up from a terminal runs the lifecycle):

Terminal window
# Copy template and customize
cp ~/.claude/templates/dev.json ./dev.json
vi dev.json

The template has sensible defaults for SvelteKit + Cloudflare Workers projects. Customize the server.command, server.port, server.health, seed, and auth sections for your stack.

Warp-drive encourages additive seed data: when a chunk adds new database entities or lifecycle states, the check-seed-coverage.sh gate advises adding seed data in the same commit. This ensures dev is always testable with realistic data across all entity states.

Projects without dev.json skip all dev lifecycle steps — the integration is fully opt-in. Set _workflow.dev_health_check: false to disable even when dev.json exists.

See dev-lifecycle.md for the full IaC dev lifecycle reference.


The approved label is stamped by /groom against a snapshot of the repo — and a long multi-area run invalidates that premise by landing commits before later areas are reached. By area 3, areas 1–2 have moved the ground the approval stood on: an AC may already be satisfied by an earlier area’s commit, made redundant by just-added tooling, or now contradict landed code. When _workflow.revalidate_on_area_entry is on (the default), the loop closes this gap with a lightweight freshness re-check on area entry instead of trusting the stamp blindly.

When it fires. At work_selected, the state machine compares the just-finished item’s cap/area metadata against the newly selected item’s — the same seamBetween signal seam-aware segmentation uses (#960). Only a crossing (different cap or different area: label, known on both sides) arms the sub-mode; the first item of a run never re-validates (it is freshly groomed), missing metadata never seams, and a same-area selection proceeds untouched. While armed, planning displays as revalidating in warp status.

The movement gate. The pass starts deterministic and cheap — scripts/warp-drive/revalidate.js gate <issue> reads the <!-- approved-at: <short-sha> --> marker /groom stamps into the issue body and compares it against HEAD:

  • No commits since the marker — the approval is fresh: the pass records a logged near-no-op (record --no-drift) and planning proceeds with zero further overhead.
  • Commits landed since (or the marker is missing/unverifiable — issues approved before the marker convention are treated as movement-unknown, failure-safe): the gate reports the commit count and a changed-file triage list, and the lightweight re-check runs.

The re-check. The loop triages the changed files first — movement that doesn’t touch the item’s area is still a no-drift outcome. Each genuinely drifted AC is classified by the reversibility engine (#878, decision-engine.js classify):

  • Two-way (reversible) — e.g. an AC already satisfied by a landed commit, or one made redundant: auto-adjusted (checkbox flip plus a one-line note comment naming the satisfying commit) and recorded.
  • One-way (irreversible) — a scope change, an AC that now contradicts landed code, a new cross-area dependency: surfaced, never auto-walked — a decision checkpoint at Level 2/3; under Level 4 the decision-timeout policy (#875) applies (conforming human-only TODO, defer the item, or stop cleanly if it blocks everything).

This is a freshness re-check, not a second grooming authority: it never rewrites issue bodies wholesale, never re-derives the cluster, and /groom remains the single upstream source of truth for cluster coherence (Prime Directive). It also composes with kickoff scope (#992): it re-validates only the already-selected item — it never adds, reorders, or widens work.

Observability. record writes each pass into session state (state.revalidation — armed boundary, entries, last verdict) and pushes a phase-history line, so warp status shows the last pass and warp log / warp status --log render the summary, e.g. revalidating -- area billing: 1 auto-adjusted, 0 blocker(s). The state dies with the session — no PM files.

All settings live in .claude/settings.local.json under _workflow. Edit directly or view with warp config.

Setting Default Purpose
max_acs_per_commit 3 Max acceptance criteria per chunk
test_before_commit true Run tests before each commit
auto_merge true L3: auto-merge to master
max_phase_minutes 30 Timeout per phase before budget warning
max_coding_cycles 3 Max code/test retries per chunk
max_retries_per_chunk 5 Max total retries before escalation
max_no_progress 3 No-progress stall halt: consecutive identical failed attempts (same failure, no new diff) before routing to budget_exceeded. See No-Progress / Stall Detection
max_auto_decisions 10 L4 decision budget (#876): max auto-decisions per session before a mandatory human digest checkpoint at budget_exceeded (never auto-continued past). 0 disables it. Only bites at Level 4 — below L4 the engine never auto-decides. Surfaced in warp status / warp config. See the reversibility decision engine.
max_edits_per_file 5 Doom loop detection threshold
pre_exit_verification true Self-check before phase advance
decision_timeout_minutes 10 Minutes before filing TODO issue. At Level 4, governs the decision-timeout policy one-way-door wait; 0 = never wait (work around or stop)
heartbeat_on_commit true RDB notify on each commit
reconcile_per_chunk true Auto-correct progress counters
flightplan_tick_on_item_boundary true Tick the finished requirement off the run’s flight plan on entering awaiting_continue, rather than trusting the prose check-off (#1757); idempotent, best-effort
harness_defect_filing true On entering chunk_complete and session_ending, file unreported harness_defects[] outward as de-duplicated harness-defect bug reports (#1836); best-effort, reported in the transition output as harness_defect_filing, never blocks
harness_defect_escalate_after 3 Recurrence threshold (#1838): after filing, any bigbrain harness-defect issue whose Recurrences: count reaches this is bumped to p2-high with a comment saying why (idempotent — marker comment + existing p1/p2 skip). 0 disables. Project-owned defects are never escalated
dev_health_check true Run dev-up health check between chunks
dev_seed_check true Run seed coverage advisory during coding
max_total_chunks 20 Circuit breaker: max chunks per session (hard limit)
max_session_minutes 480 Circuit breaker: max session duration in minutes (hard limit)
max_session_tokens 0 (disabled) Circuit breaker: max cumulative session tokens before routing to budget_exceeded. 0 disables the check, so existing sessions are unaffected unless configured. Token count is the reliable spend signal; trips mid-session on the accumulated chunk total. See Cost Budget.
max_session_usd 0 (disabled) Circuit breaker: optional/secondary estimated-dollar ceiling (via token-report’s cost model). 0 disables it. Use max_session_tokens as the primary signal.
segment_after_tokens 120000 (enabled) Session segmentation (L3+): checkpoint + resume in a fresh context once this many tokens accrue since the last checkpoint (per-segment, not cumulative). Default ON since #959 — 120k keeps segments comfortably below the ~150k high-cost context regime. Set an explicit 0 to disable. Soft threshold — hands work to a fresh session, does not halt. See Session segmentation.
segment_after_chunks 0 (disabled) Session segmentation (L3+): same, triggered by chunks completed since the last checkpoint. 0 disables it.
segment_at_seam_tokens 40000 (enabled) Seam-aware segmentation (L3+, #960): when the next queued item crosses a cap/area boundary from the just-finished one, segment at this lower per-segment token bar instead of waiting for segment_after_tokens. Hybrid — a seam lowers the bar, never forces a segment. 0 disables the seam trigger; the full segmentation opt-out keeps it inert.
revalidate_on_area_entry true Per-area approval re-validation (#1008): selecting an item across a cap/area seam arms a revalidating sub-mode of planning — a movement gate compares HEAD against the item’s <!-- approved-at: sha --> marker, and only on movement does a lightweight AC re-check run (reversible drift auto-adjusted, irreversible surfaced). false restores trust-approval-blindly semantics. See Per-Area Re-Validation.
phase_timeout_enforcement warn How max_phase_minutes breaches are handled: warn (advisory only), block (reject the offending transition until elapsed time recovers), or abort (route through budget_exceeded and auto-abort)
doc_drift_gate false When true, the slash command fires commit_with_doc_gate after each commit, routing through the doc_drift_check phase before reporting. Mechanical drift is auto-fixed via /doc-sync; high-severity audit findings route the loop back to coding. See Doc Drift Gate.
baseline_docs_check true At prerequisites, detect missing scaffolding docs on greenfield projects and file an unapproved req per gap (documentation,baseline-doc). Never authors docs (CAP-15 non-goal); idempotent; non-blocking. See Baseline Docs Check.
baseline_docs ["readme","runbook","claude"] Which baseline docs are required.
baseline_docs_max_commits 25 Greenfield gate: skip the check on repos with more commits than this. 0 = always run.
baseline_docs_auto_approve false When true, filed tracking issues also get the approved label, so warp-drive writes the doc itself in a later cycle (crosses the human-authored-narrative boundary).

phase_timeout_enforcement is the only budget threshold whose enforcement mode is configurable — retry_exceeded, coding_cycles_exceeded, no_progress, total_chunks_exceeded, and session_timeout are always enforced (they always route through budget_exceeded). Set phase_timeout_enforcement to warn (the default) for ordinary use and to block or abort only when you actively want phase-level timeouts to halt the loop.

For the full budget system, circuit breaker behavior, token capture pipeline, cost estimation, and reporting tools, see Token Monitoring & Budget System.

You can configure custom shell commands that must pass before phase transitions:

{
"_workflow": {
"verification_gates": {
"code_complete": ["make lint"],
"tests_passed": ["make type-check"]
}
}
}

If a gate command fails, the transition is blocked and Claude must fix the issue before advancing.

When _workflow.doc_drift_gate=true, the loop routes through a new doc_drift_check phase between committing and reporting:

committing → (commit_with_doc_gate) → doc_drift_check
↙ ↘
drift_clean drift_blocked
↓ ↓
reporting coding
(proceed normally) (fix high-severity drift)

Inside the gate:

  1. ~/.claude/scripts/doc-keeper/sync.js --root . --apply runs and applies every fix_kind: mechanical finding (component counts, label naming, etc.).
  2. ~/.claude/scripts/doc-keeper/audit.js --root . --fail-on-drift re-audits.
  3. If audit exits zero (no high-severity drift), the loop emits drift_clean and proceeds to reporting. Any sync edits are staged into a follow-up commit (or amended into the chunk commit, depending on policy).
  4. If audit exits non-zero, the loop emits drift_blocked and returns to coding so Claude can address the high-severity findings before retrying the chunk.

The gate is opt-in (doc_drift_gate=false by default) for backward compatibility. When it’s off, the standard committedreporting transition still runs.

The hook hooks/doc-drift-warning.sh runs after every successful git commit — even when the doc_drift_gate phase above is disabled — and fires when a commit touches tooling source paths (skills/, commands/, agents/, hooks/, bin/, scripts/, Makefile, registry/) without touching any *.md. It has two policy modes (#757), read from .claude/settings.local.json:

  • Advisory (default) — warns and lets the commit stand (exit 0).
  • Blocking (_workflow.doc_drift_blocking=true) — exits 2 so Claude Code surfaces a blocking error the agent must resolve (amend the commit with the doc update) before continuing.
  • Suppressed (_workflow.suppress_doc_drift_warning=true) — silent no-op.

The gate is PR-independent: the hook fires at commit time on any branch, and the CI backstop runs make docs-check on push: branches:[master] (docs-deploy.yml) — neither depends on a PR event, so direct-merge (L3) work is gated identically to PR-routed work. Blocking mode is the local teeth; the push-stage CI gate is the non-bypassable backstop.

The Doc Drift Gate above keeps existing docs accurate. The baseline-docs check addresses the complementary gap: a greenfield project that never gets baseline scaffolding docs in the first place (a real miss observed on a fresh MVP scaffold — an operations runbook and project CLAUDE.md were never produced).

At prerequisites, scripts/warp-drive/baseline-docs-check.sh checks for a README, an operations runbook (RUNBOOK.md, runbooks/*.md, or docs/runbook*.md), and a CLAUDE.md. For each missing doc it files a GitHub issue labelled req,documentation,baseline-doc,p3-mediumwithout approved, so a human gates the writing.

Design boundaries (why it only surfaces the gap):

  • Never authors narrative. Consistent with the CAP-15 non-goal — README/CLAUDE.md/runbook prose stays human-authored. The loop files a tracking issue; a human (or, with baseline_docs_auto_approve=true, a later cycle) writes it.
  • Idempotent. An open baseline-doc issue for a given doc is never refiled, so re-running prerequisites across sessions never duplicates.
  • Non-blocking. The check emits JSON and the loop continues regardless — it is advisory, never a gate.
  • Greenfield-scoped. The baseline_docs_max_commits gate (default 25) skips mature repos, which already have these docs or have deliberately chosen otherwise. Set it to 0 to always run.

Configure via the baseline_docs* keys in Configuration. Disable entirely with baseline_docs_check=false.

Control Claude’s reasoning effort per phase (the “reasoning sandwich” pattern — high reasoning for planning/verification, standard for implementation):

{
"_workflow": {
"reasoning_budget": {
"planning": "high",
"coding": "standard",
"testing": "high"
}
}
}

See Token Monitoring & Budget System for the full per-phase default table and how reasoning levels are applied.


Artifact Where Labels
Commits Feature branch (conventional commit format)
Chunk reports GitHub Issues warp-drive
Session summary GitHub Issue session-summary, warp-drive
Lessons learned GitHub Issues lesson, warp-drive
Decisions made GitHub Issues decision, warp-drive
Deferred bugs GitHub Issues bug-deferred, warp-drive
TODO (timeouts) GitHub Issues todo, warp-drive
Abort reports GitHub Issues warp-drive, session-summary
PR (Level 2) GitHub PR via gh pr create

GitHub labels are created automatically on first run (warp-drive, lesson, decision, risk, bug-deferred, session-summary).


If a session is lost (crash, worktree deletion, etc.), use the disaster recovery CLI (Terminal only — these recovery scripts have no slash form and are meant to run when no session is alive):

Terminal window
# Check current state
~/.claude/scripts/lib/cdr.sh status
# Re-initialize tooling (clone from remote, re-run cdi)
~/.claude/scripts/lib/cdr.sh recover /path/to/project
# Emergency state reset (deletes warp-drive state file)
~/.claude/scripts/lib/cdr.sh reset /path/to/project

Warp-drive records commit hashes in the state file (commit_hashes[]). If the state file survives but the worktree is lost, cherry-pick commits from the remote:

Terminal window
git fetch origin
git cherry-pick <hash1> <hash2> ...

Run the preflight checker to verify all dependencies are installed (Terminal only):

Terminal window
~/.claude/scripts/lib/preflight-check.sh

This checks: jq, node, git, gh auth, hook file existence, and core scripts.


Run /automation level 2 inside Claude Code first. This sets up permissions in .claude/settings.local.json.

Run /start-work to create a feature branch (Level 2), or let Level 3 auto-create one.

Warp-drive won’t start — “Active session exists”

Section titled “Warp-drive won’t start — “Active session exists””

A previous session didn’t clean up. Check if it’s stale:

Terminal window
warp status

If stale, reset it:

Terminal window
warp stop --hard

Commit blocked — “only allowed during committing phase”

Section titled “Commit blocked — “only allowed during committing phase””

The gate hook is working correctly. Claude tried to commit outside the committing phase. This usually means a phase was skipped. Check the state:

Terminal window
warp log

Can’t exit Claude Code — “report is missing”

Section titled “Can’t exit Claude Code — “report is missing””

The stop hook blocks exit when a commit was made but no chunk report was filed. Tell Claude to file the report, or if you need to force exit, kill the terminal.

Claude keeps editing the same file. The edit tracker caught it. This usually means the approach isn’t working. Claude should try an alternative strategy or escalate.

Terminal window
warp stop --hard

This deletes the state file. Your code changes are still in the working tree. Start a new session when ready.

Add ~/.claude/bin to your PATH:

Terminal window
echo 'export PATH="$HOME/.claude/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

Moved to reference — see Warp-Drive State Reference for the state file, state-machine CLI, and transition table.