Docs / Reference / ade.toml
ade.toml
Generated 27/10/2026 from the kit
One file configures a project: providers and models per role, the test command, protected paths and rigour per path. This is the template ade init writes, comments and all; edit it, then rerun ade init.
# Atlas ADE configuration. `ade init` writes this once; edit it, then rerun `ade init` to # regenerate .claude/settings.json, .claude/agents/ and AGENTS.md from it.
[git]#
[git] main = "main" # ade pushes to this remote, and only trunk, the ade-records branch and tags matching push_tags: no force, no deletes, # fast-forward only (`ade push`). A repo without this remote is simply not pushed. remote = "origin" # "land": every landing pushes once it has merged; a refused push is printed and the card stays landed. # "manual": only `ade push` pushes. push = "land" push_tags = ["checkpoint/*"] # Branch mirrors as "src:dst" (["master:main"]): dst follows src, fast-forward only, never forced or deleted. With a # GitHub remote, ade init writes .github/workflows/ade-mirror.yml so GitHub moves dst after each push of src (no # machine pushes dst); elsewhere ade push sends it. A first init copies these from remote.<remote>.push refspecs. mirror = []
[tests]#
[tests]
# {tests} becomes the test files the graph selects, in one run; an empty selection runs the bare command.
# {test} instead runs the command once per selected file, for tests that are scripts (`npx tsx {test}`).
# {python} is the interpreter running ade, so the same line works on Windows, macOS and Linux.
command = "{python} -m unittest {tests}"
# Commands run first for every change that touches code, such as a typecheck (`npm run typecheck`).
always = []
# Used when a touched path is unknown to the graph or no test reaches it. Empty = whole suite.
critical = []
timeout = 600
# A failure whose output matches this gets one isolated retry; anything else fails at once.
timing = "timed? ?out|deadline exceeded|ETIMEDOUT|ECONNRESET|temporarily unavailable"[lanes]#
[lanes] # Gitignored folders a lane needs but a worktree does not check out, such as dependencies (["node_modules"], # [".venv"]). Each lane links them from the main checkout (a junction on Windows) instead of installing again. share = []
[rigour]#
[rigour] # Paths whose output can cause harm: every test that reaches them runs. Everything else is orchestration-grade. proof_grade = [] # Prose paths that land without review. no_review = ["*.md", "docs/*"]
[graph]#
[graph] out = "graph.json" exclude = [] tests = ["tests/*", "test_*.py", "*/test_*.py", "*.test.*", "*.spec.*", "*/__tests__/*"] # Scripts whose `ade <verb>` lines are bound to those verbs in the graph and checked at land, so a renamed verb or a # new required argument cannot leave a script behind. scripts = ["*.sh", "*.ps1", "*.cmd", "tools/*", "scripts/*", ".github/workflows/*"] # What every added or changed code file should carry: "tests" (a test reaches it through imports) and "header" # (the five-line header). `require` stops land until it is there; `note` lands and records what is missing. # Tests are minimal: one robust invariant where it guards something lasting, never a literal # example, so a missing test is noted, not blocked. Projects started on ade require the header. require = [] note = ["tests"] untested_ok = []
[guards]#
[guards]
# The bash guard (hook 2) refuses only these classes, in every permission mode. Secret files are never
# printed by a reader; pass them by path. Writers are refused on [protected] paths.
secrets = [".env", ".env.*", "*.pem", "*.key", "id_rsa*", "id_ed25519*", "*.p12"]
secret_ok = [".env.example", ".env.sample", ".env.template"]
readers = ["cat", "less", "more", "head", "tail", "bat", "grep", "rg", "egrep", "awk", "sed", "cut", "sort",
"xxd", "od", "hexdump", "strings", "base64", "nl", "type", "Get-Content", "gc", "jq", "yq"]
writers = ["tee", "cp", "mv", "rm", "truncate", "ln", "install", "dd"][protected]#
[protected] # Edits by an agent are refused here; change them by hand, then rerun `ade init`. paths = ["ade.toml", ".claude/settings.json", ".claude/settings.local.json", ".claude/agents/*", "AGENTS.md", ".git/*"]
[budgets]#
[budgets]
core_lines = 20000
hooks = 5
refusal_codes = 50
handover_tokens = 20000
brief_tokens = 250
core = ["*.py"]
core_exclude = ["tests/*", "test_*.py", "*/test_*.py"]
# Per-component line budgets: name = { globs, lines }. Every core file must match exactly one row;
# `ade budget` reports the rest as "unassigned" or "overlapping", each with a budget of 0. Empty = total only.[budgets.components]#
[budgets.components]
# Providers are adapters: argv templates with {prompt} and {model}. `fake` is built in,
# deterministic and spends no usage; set ADE_PROVIDER=fake for tests and rehearsals.[providers.claude]#
[providers.claude]
# {mode} is [permissions] lane_mode: lanes are isolated worktrees, and hooks guard edits and shell in every mode.
# --output-format json carries the call's own usage report (tokens, cache, cost, turns); ade keeps it locally.
cmd = ["claude", "-p", "{prompt}", "--model", "{model}", "--permission-mode", "{mode}", "--output-format", "json"]
timeout = 1800
# Dropped from the environment of each claude call: what a host Claude Code session (one that runs ade) sets, so
# the child runs as your own login, not as a child of that session. ANTHROPIC_BASE_URL is here because a host
# points it at its own proxy; if you set it yourself on purpose, take it out of this list.
scrub_env = ["CLAUDECODE", "CLAUDE_CODE_*", "CLAUDE_AGENT_SDK_*", "CLAUDE_PID", "CLAUDE_EFFORT", "CLAUDE_PLUGIN_DATA",
"ANTHROPIC_BASE_URL"][providers.codex]#
[providers.codex]
# --json prints events with token counts; a rate_limits window, when one appears, feeds the week meter.
# Neither engine reported a week window in the first real runs (27 Oct), so meters read "week not reported" and show
# the kit's own 7-day spend instead. codex reports tokens but no cost; claude reports both.
cmd = ["codex", "exec", "--json", "-m", "{model}", "{prompt}"]
timeout = 1800[conduct]#
[conduct] # `ade conduct` routes each stop of the loop by ade/route.toml. A card that has woken a model this many times parks; # a route wait gives up after this long and parks. wakes = 2 wait = "30m"
[watch]#
[watch] # Minutes a lane log may stay silent before the watcher reads it as stalled (`ade wait --lane`, `ade watch`). stall = 10
[q]#
[q] # The output store (.ade/q.sqlite): ade's own long output, and anything run as `ade q run -- CMD`, is kept whole # there and shown as its head, its tail, error-looking lines and a handle to page through it (`ade q show q17`). # Text within both caps prints whole. cap_lines = 200 cap_bytes = 20000 head = 40 tail = 40 page_lines = 200 sql_rows = 200 keep_days = 14 # Claude Code's own cap on Bash output, written into .claude/settings.json env as BASH_MAX_OUTPUT_LENGTH when above # 0 and the file sets none. 0 leaves the platform's default, which already keeps very long output out of context. bash_chars = 0
[cockpit]#
[cockpit] # "cli": the terminal board is the whole cockpit. "orca": also mirror each lane into Orca as a worktree with a # terminal running `ade watch <card>`, and each stop as the worktree's status comment. "auto": orca when an Orca is # running, cli when not. Orca never drives the loop; `ade cockpit --mode` overrides this for one run. mode = "cli"
[usage]#
[usage]
# Your weekly share per provider: the fraction of each account's week the kit may use. The cockpit and status
# line show each meter against it once that provider reports a week window; until then, "week not reported" and the kit's 7-day spend.
share = { claude = 1.0, codex = 1.0 }
# The roster: one source for .claude/agents/ade-<role>.md and AGENTS.md. Models are family
# aliases (opus, sonnet, gpt-6-sol) so each provider resolves its latest release.[roles.pilot]#
[roles.pilot]
provider = "claude"
what = "Flies the board: picks cards, answers needs-judgement stops, decides, lands."
models = { claude = "opus", codex = "gpt-6-sol" }
tools = "Read, Grep, Glob, Bash"[roles.builder]#
[roles.builder]
provider = "claude"
what = "Builds one card in its lane: the smallest change that meets the card's done line."
models = { claude = "sonnet", codex = "gpt-6-sol" }
tools = "Read, Edit, Write, Grep, Glob, Bash"[roles.reviewer]#
[roles.reviewer]
provider = "claude"
what = "Reviews one lane diff; answers APPROVE on the first line, or returns a patch."
models = { claude = "sonnet", codex = "gpt-6-sol" }
tools = "Read, Grep, Glob"
# Every rule carries its reason; settings.json cannot hold comments, so the reason lives here.[permissions]#
[permissions] # The mode `ade init` writes for your own sessions when none is set; bypassPermissions is allowed (one line away). mode = "acceptEdits" # The mode lane builders and reviewers run in. lane_mode = "bypassPermissions"
[permissions.allow]#
[permissions.allow] "Bash(ade *)" = "the kit's own verbs; each writes its own record and refuses with a cure" "Bash(ade push*)" = "ade's own push: trunk, ade-records and [git] push_tags only, fast-forward only, never forced; raw git push stays unallowed" "Bash(git status*)" = "read-only repository state" "Bash(git diff*)" = "read-only; reviewers and builders read their own diff" "Bash(git log*)" = "read-only history" "Bash(python3 -m unittest*)" = "the configured test command; edit with [tests].command"
[permissions.deny]#
[permissions.deny] "Read(./.env)" = "secrets are passed by path, never read" "Read(./.env.*)" = "secrets are passed by path, never read" "Bash(git push --force*)" = "history is never rewritten" "Bash(rm -rf *)" = "destructive; lanes are removed by `ade land`"