Appearance
Automation Level Behavior Guide
Reference for how commands and skills should adjust interaction ceremony based on the active automation level. The big-picture description of each level (and how to switch) is in project-orchestration-handbook.md §7.1; this doc is the per-decision behavior table. Warp-drive's level-specific behavior is in the handbook §7.2 and warp-drive.md.
Detection
Read .claude/settings.local.json in the project directory. Look for _automation.active_level (1, 2, or 3). If not found or the file doesn't exist, default to Level 1.
Level Principles
Level 1 — Supervised (default)
- Ask before every decision
- Show all options with explanations
- Wait for explicit confirmation before acting
- This is identical to pre-automation behavior
Level 2 — Trusted Dev
- Auto-proceed on safe, inferrable, or reversible decisions
- Still confirm destructive operations (force push, delete, overwrite)
- Still ask for subjective content (summaries, descriptions, priorities)
- Show each auto-decision inline as it happens
Level 3 — Autonomous
- Minimize prompts — only stop for destructive ops or subjective content
- Auto-infer from git context, branch names, file paths, commit history
- Batch auto-decisions into a summary rather than inline notifications
- Ask for: rebase conflicts, subjective writing, destructive operations
Transparency Format
Commands must show what was auto-decided so the user stays informed.
Level 2 — inline per decision:
> Auto (L2): Branch type set to `tooling/` (inferred from .claude/ changes)
> Auto (L2): Stashed 2 uncommitted filesLevel 3 — batched summary:
> Auto (L3) decisions:
> - Branch type: `tooling/` (inferred from .claude/ changes)
> - Stashed 2 uncommitted files
> - Branch name: `tooling/automation-behavior`Decision Classification
| Category | L1 | L2 | L3 |
|---|---|---|---|
| Safe + inferrable | Ask | Auto, show | Auto, show |
| Reversible action | Ask | Auto, show | Auto, show |
| Subjective content | Ask | Ask | Ask |
| Destructive operation | Ask | Ask | Ask |
| Push to remote | Ask | Confirm | Auto (except main/master) |
| Conflict resolution | Ask | Ask | Ask |
| Requirement/AC tracking | Ask | Auto-update, show | Auto-update, show |
| Documentation housekeeping | Ask | Auto-update, show | Auto-update silently |
| Warp-drive continue to next task | Ask | Ask | Auto-continue, log |
| Warp-drive plan review | Ask | Ask | Auto-proceed, log |
Autonomous profile — auto-approved vs always-blocked (#420)
Level 3 (the autonomous profile, profiles/autonomous.json) is the only level designed to run unattended — including overnight. Because nothing prompts, "what exactly is this allowed to do while I'm asleep?" needs a documented answer. This is it.
When to use it vs. interactive defaults. Use L3 only for a scoped, version-controlled repo where every change is recoverable from git and you have reviewed the work queue — e.g. an area:-scoped warp-drive batch of approved requirements. Stay at L1/L2 (interactive) for exploratory work, anything touching production credentials or external services by hand, or a repo whose state you are not ready to have advanced without review. L3 trades the per-action prompt for an after-the-fact audit trail (below) — that trade only pays off when the queue is trusted and the blast radius is git.
Auto-approved at L3 (no prompt): reads, search, edits/writes, Bash, the dev/test/commit/push/merge skills, Playwright, WebFetch/WebSearch, and push/merge to main/master (subject to the promotion ceiling — a pr ceiling still caps L3 at a pull request).
Always-blocked at L3 (denied even though everything else is auto-approved) — three layers, each entry carries a rationale in profiles/autonomous.json → _meta.deny_rationale:
| Layer | Blocks | Why |
|---|---|---|
| Destructive-git floor | git push --force, git push origin --force, git reset --hard, git branch -D, rm -rf | Irrecoverable history/working-tree loss. This floor is a deliberate guardrail and is never loosened — see the autonomous-deny-destructive-git guardrail. |
| Secrets / credential reads | .env / .env.*, *credential*, ~/.ssh, ~/.aws, ~/.config/gcloud, ~/.kube, ~/.npmrc, ~/.netrc (via both Read and shell cat) | An unattended run has no legitimate reason to read raw secret stores; blocking them shrinks the exfil surface. |
| Network-exfil shapes | curl -T / --upload-file, curl -d @file, pipe-into-curl/wget, wget --post-file | The obvious "send a local file to a remote host" shapes. |
Honest limitation. The secrets/exfil blocks are defense-in-depth, not a sandbox. Bash is broadly allowed, so a sufficiently creative shell command can still read a file or reach the network — the deny patterns only catch the obvious shapes. The real backstop for an unattended run is the audit log, not the deny-list.
Audit log
When _automation.active_level == 3, the autonomous-audit-log.sh PreToolUse hook records every auto-approved tool call — tool, target, timestamp, session, cwd — as one JSONL line per call:
- Location:
~/.claude/logs/autonomous-audit/<session-id>.jsonl(override with$BOB_AUTONOMOUS_AUDIT_DIR). - Per session: one file per Claude Code session, so an overnight run is one reviewable trail.
- Recorder, never a gate: it always exits 0 and never blocks — it only observes.
- Strict no-op below L3: at L1/L2 (and outside BoB projects) it reads one settings field and exits, so it cannot affect interactive or Trusted-Dev runs.
To review an unattended run afterwards: jq -r '"\(.ts) \(.tool) \(.target)"' ~/.claude/logs/autonomous-audit/<session>.jsonl.
So — is it safe to leave running overnight? For a scoped, git-backed repo with a reviewed queue: yes — irrecoverable git ops, secret reads, and obvious exfil are blocked, and everything auto-approved is captured in the audit log for morning review. It is not a substitute for a sandbox on an untrusted workload, and the promotion ceiling still governs how far work is actually promoted.
Promotion ceiling (#448)
The automation level governs how much autonomy the agent has. It used to also decide how far work is promoted — the two were fused: Level 2 ≡ "open a PR", Level 3 ≡ "merge to master". That made "run fully autonomously, but never push past a PR" impossible to express.
A project's promotion ceiling (dev.json → promotion.ceiling, default external) decouples those axes. The merge/deploy decision becomes f(automation_level, ceiling), computed by the shared library scripts/promotion/promotion.js and enforced everywhere — warp-drive's merging phase, /finish-work, and the promotion-ceiling-guard PreToolUse hook. View or set it with /promotion.
Git action (merge) — f(level, ceiling):
| Ceiling ↓ \ Level → | L1 | L2 | L3 |
|---|---|---|---|
pr | PR | PR | PR (capped — never auto-merges to main) |
external (default) | PR | PR | direct merge to main |
test | PR | PR | direct merge to main |
prod | PR | PR | direct merge to main |
Deploy action — f(level, ceiling):
| Ceiling ↓ \ Level → | L1 | L2 | L3 |
|---|---|---|---|
pr | none | none | none |
external (default) | none | none | none (git only — deployment is out-of-band) |
test | none | none | deploy to test |
prod | none | none | deploy to prod |
The default external reproduces the legacy fused behavior exactly (L3 → direct merge + no deploy, L2 → PR), so a project with no promotion block is unchanged. Setting a ceiling only ever narrows (pr) or extends (test/prod) from that baseline. Deploy adapters that execute the test/prod deploy land in #450; until then a non-external deploy action is a recorded intent, not yet an executed deploy.