Configuration¶
Full reference for the Stagecoach configuration system: precedence order, file format, environment variables, git-config keys, built-in defaults, and paths. Matches the shipped config init template and the Go source in internal/config/.
Precedence¶
CLI flags > STAGECOACH_* env vars > repo git config (stagecoach.*) >
repo-local .stagecoach.toml > global config file > provider defaults > built-in defaults
From lowest to highest:
- Built-in defaults — hardcoded in
config.Defaults(Layer 1). - Provider defaults — the manifest's
default_model,provider_flag, etc. (Layer 2). - Global config file —
$XDG_CONFIG_HOME/stagecoach/config.toml(Layer 3). - Repo-local
.stagecoach.toml—./.stagecoach.tomlin the repo root (Layer 4). - Repo git config —
stagecoach.*keys in.git/config(Layer 5). STAGECOACH_*env vars — environment variables (Layer 6).- CLI flags — command-line arguments (Layer 7 — highest).
When a [provider.<name>] section appears in a config file, its fields are merged onto the built-in manifest of the same name (field-by-field: present values override, absent values inherit).
session_modeoverride.session_modeis one such overridable field. An explicitsession_mode = ""on a provider that ships"append"(pi) disables the multi-turn fallback for that provider (the run proceeds one-shot → rescue, unchanged); omitting the key inherits the built-in"append". Settingsession_mode = "append"on a provider that ships""is a user override at their own verification risk — the shipped default stays""until a reproducible append-turn rendering is confirmed (see providers.md and).
Config file paths¶
| Scope | Path | Notes |
|---|---|---|
| Global | $XDG_CONFIG_HOME/stagecoach/config.toml (default ~/.config/stagecoach/config.toml) |
Written by stagecoach config init; read as Layer 3. |
| Repo-local | ./.stagecoach.toml |
Gitignored; read as Layer 4; overrides global. Written by config init --local. |
Use stagecoach config path to print the resolved config path (override-aware: honors --config / STAGECOACH_CONFIG, else the global path).
Bootstrap (config init)¶
stagecoach config init writes a populated, working config to the global path by default. It:
- Runs cascading provider detection (highest-priority installed built-in, in order: pi, opencode, cursor, agy, codex, claude).
- Writes
[defaults] provider = "<detected>"and that provider's per-role model defaults UNCOMMENTED (from the table) — EXCEPT for pi, whose per-role models are left EMPTY (pi is a multi-backend provider; set the model with an inference-provider prefix, e.g.model = "anthropic/claude-haiku",). Pi's shipped per-role models are blank so you supply your own backend/model. - Writes other installed providers as commented-out
[role.*]blocks (one-line uncomment to route a role to a different agent). - If no agent is detected, defaults to
"pi"with an annotation.
The written path is always printed on success.
| Flag | Description |
|---|---|
--provider <name> |
Target a specific built-in provider instead of auto-detecting. Unknown names exit 1. |
--force |
Overwrite an existing config file. |
--template |
Write the inert all-commented reference config (v1 behavior) instead of a populated bootstrap. |
--local |
Write to the repo-local ./.stagecoach.toml instead of the global config (it overrides the global file; mutually exclusive with --config). Composes with --force, --template, --provider, and --interactive. |
If a config file already exists, it is NOT overwritten unless --force is passed (exit code 1). Parent directories are created as needed.
With --force and no --provider, the regenerated template is re-targeted to the preserved [defaults] provider (rather than auto-detecting pi), keeping the generated [role.*] blocks consistent with the preserved default. An explicit --provider <name> always overrides this.
config init --interactive runs a TTY-gated wizard: it lists detected providers (default highlighted), shows each role's curated default for accept-or-edit, and — for multi-backend providers (pi, opencode) — prompts for the inference/model prefix on edited models (/) rather than guessing. It writes the same file as plain config init. Non-TTY stdin exits 1 pointing at plain config init (which stays non-interactive for post-install/first-run use,). Composes with --force (overwrites) and --provider <name> (pre-selects); mutually exclusive with --template.
config init --local writes to the repo-local ./.stagecoach.toml (Layer 4) instead of the global path. The generated file overrides the global config and is overridden by repo git config (stagecoach.*), STAGECOACH_* env vars, and CLI flags; its header scope is rewritten to repo-local framing so it does not claim to be the global file. Mutually exclusive with --config. Composes with --template (inert reference into the repo file), --force (refreshes an existing .stagecoach.toml, preserving active settings and backing up the prior file), --provider, and --interactive.
Schema versioning (config upgrade)¶
stagecoach config upgrade rewrites an existing config's top-level config_version line to the current schema version (3) in place — For multi-backend providers, the former default_provider is folded into a slash-prefix on the model and the key is deleted. Every other line is preserved. Idempotent: running it twice leaves the file unchanged. No flags.
$ stagecoach config upgrade
# Already at version 3 → "Config at <path> is already at version 3 (no changes)."
# Upgraded from v1 → "Upgraded config at <path> to version 3."
# No file → "no config file at <path> (run 'stagecoach config init' first)" (exit 1)
# Not valid TOML → "config <path> is not valid TOML: <err>" (exit 1, file untouched)
At load time, if config_version is missing or older, stagecoach prints an advisory to stderr pointing at config upgrade (never config init --force — that is a re-bootstrap, not an upgrade, per). If config_version is newer than the binary supports, the advisory says only to upgrade stagecoach (regenerating would discard a schema the binary cannot read). The current schema (version 3) includes per-role models, reasoning levels, the inference-provider model-prefix, multi-commit decomposition, and binary filtering.
Note
Point discovery at a specific file with --config <path> (or the STAGECOACH_CONFIG env var). It overrides global and repo-local file discovery and is honored by every command — including the default commit action and the config init, config path, and config upgrade subcommands (e.g. stagecoach --config X config upgrade upgrades file X; config path prints the resolved path) — so a provider declared under [provider.<name>] in that file is usable with --provider <name> directly. A missing explicit path (typo'd --config or STAGECOACH_CONFIG) fails fast with exit 1; only the discovery default tolerates a missing global file.
File format¶
The config file uses TOML with several section groups. By default, config init writes a populated config: an active [defaults] provider = "<detected>" (every role inherits it), the per-role [role.*] blocks COMMENTED with the shipped defaults (uncomment one to pin a role beyond the default), and [generation] with token_limit = 50000 active. Use config init --template to get the inert all-commented reference.
Populated config (default config init output):
config_version = 3
[defaults]
provider = "claude"
reasoning = "off" # off|low|medium|high; off by default for every role
# model = ""
# timeout = "120s" # global fallback for every role; the planner role defaults to 480s
# auto_stage_all = true
# verbose = false
# --- per-role models for the default provider "claude" ---
# [role.planner]
# model = "haiku"
# timeout = "600s" # per-role generation timeout; overrides the planner's 480s built-in
# [role.stager]
# model = "sonnet"
# [role.message]
# model = "haiku"
# [role.arbiter]
# model = "haiku"
[generation]
# max_diff_bytes = 300000 # ignored when token_limit is set
# max_md_lines = 100 # ignored when token_limit is set
token_limit = 50000 # holistic token budget; the populated config ships 50000 active — set 0 (or delete) for no cap
# diff_context = 1 # 0 = changed-lines-only, 1 = one anchor (default), 3 = git default; ; valid range 0–3 — out-of-range rejected at config load
# multi_turn_fallback = true # lossless multi-turn fallback on one-shot exhaustion; set false to DISABLE (now honored via file/git-config — see "Multi-turn fallback" below)
# multi_turn_chunk_tokens = 32000 # per-turn chunk budget in tokens; does NOT interact with token_limit
# work_desc_read_rounds = 5 # max READ rounds in work-description mode; flag/env activate the mode, not this knob
# exclude = [] # UNIONS across layers — see "Exclusion globs" below
# format = "auto" # <base>[+body]: auto|conventional|gitmoji|plain, each optionally +body; unknown = hard error (exit 1)
# locale = "" # free-form language name or BCP-47 tag; never validated
# template = "" # wrap every message; must contain literal $msg, e.g. "$msg (#205)"
# hook_timeout = "10m" # per-hook execution timeout; file + default only
# no_verify = false # skip pre-commit and commit-msg hooks (; mirrors `git commit --no-verify`); CANNOT disable-via-file is N/A (default is already false)
# no_parent_watchdog = false # opt out of the parent-death lock watchdog — set true if you launch via nohup/setsid/systemd-run
# [upgrade] # self-update; GLOBAL config only — no per-repo meaning
# channel = "stable" # stable | prerelease (admits -rc/-beta tags; = --prerelease)
# source_repo = "dabstractor/stagecoach" # release source; override for a fork. Compile-time default.
# ...
Inert template (config init --template): every line is commented out EXCEPT the [generation] table header (kept active so uncommenting a single generation key lands in the right table — it holds no keys, so the file is still functionally inert / IsInert is true). [defaults], [provider.*], and [role.*] sections are fully commented — documents every available option without changing any defaults.
Built-in defaults¶
These are the values when no config file, env var, git-config key, or flag sets them:
| Option | Default | Source |
|---|---|---|
provider |
"" (auto-detect) |
config.Defaults |
model |
"" (manifest default_model) |
config.Defaults |
timeout |
120s |
config.Defaults |
auto_stage_all |
true |
config.Defaults |
verbose |
false |
config.Defaults |
max_diff_bytes |
300000 |
config.Defaults |
max_md_lines |
100 |
config.Defaults |
token_limit |
0 |
config.Defaults (— unset ⇒ legacy caps) |
diff_context |
1 |
config.Defaults (— -U1; range 0–3, out-of-range rejected at config load) |
max_duplicate_retries |
3 |
config.Defaults |
multi_turn_fallback |
true |
config.Defaults |
multi_turn_chunk_tokens |
32000 |
config.Defaults |
work_desc_read_rounds |
5 |
config.Defaults |
subject_target_chars |
50 |
config.Defaults |
output |
"raw" |
provider manifest |
strip_code_fence |
true |
provider manifest |
format |
"auto" |
config.Defaults |
locale |
"" |
config.Defaults |
template |
"" |
config.Defaults |
push |
false |
config.Defaults |
hook_timeout |
10m |
config.Defaults (— per-hook execution timeout; file + default only) |
no_verify |
false |
config.Defaults (— skip pre-commit/commit-msg hooks; mirrors git commit --no-verify) |
no_parent_watchdog |
false |
config.Defaults (— parent-death watchdog runs by default) |
channel |
"stable" |
config.Defaults (— [upgrade], global-only; "prerelease" admits -rc/-beta tags) |
source_repo |
"dabstractor/stagecoach" |
config.Defaults (— release source; override for a fork) |
NoColor is TTY-aware at runtime (set by the UI layer); it is not a file field and has no config-file key.
[upgrade]is global-only. The[upgrade]table is read from the GLOBAL config file only — a per-repo.stagecoach.toml[upgrade]is ignored (a--verbosenote is printed bystagecoach upgrade).config.Loadnever propagates[upgrade]into the resolved config; thestagecoach upgradecommand reads it via the dedicatedconfig.LoadUpgradeConfigreader.CurrentConfigVersionis unchanged (3):[upgrade]is additive and never emits a config advisory.Hook execution knobs. Two
[generation]knobs control the hook-execution surface (pre-commit / prepare-commit-msg / commit-msg / post-commit):
hook_timeout(default10m) — bounds each hook invocation so a wedged hook cannot hang a commit. A duration string (e.g."30s","10m"); malformed values fail at config load. File + default only (no env var, no flag, no git-config key) — set it in a config file.no_verify(defaultfalse) — the--no-verifybypass: when true, skipspre-commitandcommit-msghooks (prepare-commit-msgandpost-commitstill run). It resolves through the full 5-layer precedence (--no-verify/STAGECOACH_NO_VERIFY/stagecoach.noVerify/[generation].no_verify). The[generation].no_verifyfile key uses the same only-true-propagates limitation aspush: a file settingno_verify = falseis a no-op (false is already the default); use the flag/env layers to set it false explicitly. (Note:auto_stage_allandmulti_turn_fallbackare*booland do NOT have this limitation — they are default-true, so a file/git-configfalseis honored;no_verify/pushare default-false, so only-true-propagates is harmless for them.)
The output and strip_code_fence settings apply to parsing of agent output. Setting output = "json" makes Stagecoach parse the agent's stdout as JSON (extracting the json_field value) across all providers. These [generation] values are an opt-in override: when [generation] (and git-config) omit them, the per-provider [provider.<name>] value is honored, falling back to the manifest defaults (output = "raw", strip_code_fence = true). Set output = "json" here only to force JSON parsing across ALL providers.
Token budget & diff context. Two
[generation]knobs size and shape the diff payload:
token_limit(default0= unset) — a holistic token budget over the whole agent payload (system prompt + style examples + the concatenated diff). When set (e.g.120000), Stagecoach reserves room for the prompt/examples and truncates the diff to fit using the ≈4 chars/token estimate; after truncation it assembles the actual full prompt, re-measures it, and re-trims until it fits — a closed-loop guarantee that the payload never exceedstoken_limit. For extremely small limits (below the irreducible prompt floor — system prompt + numstat skeleton + framing, which varies per run), Stagecoach rejects the limit with a clear error rather than silently violating the guarantee. The payload always fits your model's context window without Stagecoach maintaining a per-model context registry. A non-zerotoken_limitsupersedes the legacy per-section capsmax_diff_bytesandmax_md_linesfor that run; the two modes are mutually exclusive. When0/unset, the legacy caps apply unchanged.diff_context(default1) — unchanged context lines surrounding each diff hunk:0= changed lines only (maximal savings),1= one anchor line (default),3= git's default. Applies in every diff path (staged, multi-commit snapshot, per-concept tree diff). Valid range is 0–3; an out-of-range value is rejected at config load with a clear error.
Where token_limit truncates a too-large payload, the multi-turn fallback instead delivers it in request-sized pieces — the two never compose for a single message.
Multi-turn fallback. Two
[generation]knobs control the lossless multi-turn fallback path, which activates only after the one-shot retry loop exhausts on a large diff:
multi_turn_fallback(defaulttrue) — enables the fallback. This is a*boolfield (precedence-aware), so you can disable it by settingmulti_turn_fallback = falsein a config file — thefalseis honored end-to-end (afalsesurvives the materialize→overlay chain instead of being silently dropped). Settable via a config file (multi_turn_fallback = false) or theSTAGECOACH_MULTI_TURN_FALLBACK=falseenv var; there is no CLI flag or git-config key for it, so to disable multi-turn persistently use the config file or env var (or setsession_mode = ""on the provider — see providers.md). The shipped pi default is"append".multi_turn_chunk_tokens(default32000) — the per-request chunk size (tokens est.) the large diff is split into for multi-turn priming. This does NOT interact withtoken_limit:token_limittruncates the one-shot payload, while multi-turn deliberately uses the untruncated payload, delivered in request-sized pieces — the two never compose for a single message.
Environment variables¶
All STAGECOACH_* variables override the config file and are overridden by CLI flags:
| Variable | Mirrors flag | Description | Example |
|---|---|---|---|
STAGECOACH_PROVIDER |
--provider |
Default provider/agent | STAGECOACH_PROVIDER=claude stagecoach |
STAGECOACH_MODEL |
--model |
Model override | STAGECOACH_MODEL=sonnet stagecoach |
STAGECOACH_TIMEOUT |
--timeout |
Global generation timeout — the fallback for every role; the planner role defaults to 480s and is NOT changed by this | STAGECOACH_TIMEOUT=60s stagecoach |
STAGECOACH_CONFIG |
--config |
Config file path | STAGECOACH_CONFIG=./alt.toml stagecoach |
STAGECOACH_VERBOSE |
--verbose |
Print resolved command and output. Accepts true/false/1/0. (2 is documented as a future payload-contents level but not yet implemented — it is rejected with a clear message.) |
STAGECOACH_VERBOSE=true stagecoach |
STAGECOACH_NO_COLOR |
--no-color |
Disable color | STAGECOACH_NO_COLOR=true stagecoach |
NO_COLOR |
--no-color |
Universal color-disable (honored when set) | NO_COLOR=1 stagecoach |
STAGECOACH_COMMITS |
--commits |
Force N commits (0=auto, 1≡single) | STAGECOACH_COMMITS=3 stagecoach |
STAGECOACH_PLANNER_PROVIDER |
--planner-provider |
Per-role: planner provider | STAGECOACH_PLANNER_PROVIDER=claude stagecoach |
STAGECOACH_PLANNER_MODEL |
--planner-model |
Per-role: planner model | STAGECOACH_PLANNER_MODEL=opus stagecoach |
STAGECOACH_STAGER_PROVIDER |
--stager-provider |
Per-role: stager provider | STAGECOACH_STAGER_PROVIDER=pi stagecoach |
STAGECOACH_STAGER_MODEL |
--stager-model |
Per-role: stager model | STAGECOACH_STAGER_MODEL=gpt-5.4-mini stagecoach |
STAGECOACH_MESSAGE_PROVIDER |
--message-provider |
Per-role: message provider (env + config only) | STAGECOACH_MESSAGE_PROVIDER=claude stagecoach |
STAGECOACH_MESSAGE_MODEL |
--message-model |
Per-role: message model (env + config only) | STAGECOACH_MESSAGE_MODEL=haiku stagecoach |
STAGECOACH_ARBITER_PROVIDER |
--arbiter-provider |
Per-role: arbiter provider | STAGECOACH_ARBITER_PROVIDER=claude stagecoach |
STAGECOACH_ARBITER_MODEL |
--arbiter-model |
Per-role: arbiter model | STAGECOACH_ARBITER_MODEL=sonnet stagecoach |
STAGECOACH_REASONING |
--reasoning |
Global reasoning effort: off|low|medium|high | STAGECOACH_REASONING=high stagecoach |
STAGECOACH_PLANNER_REASONING |
--planner-reasoning |
Per-role: planner reasoning | STAGECOACH_PLANNER_REASONING=high stagecoach |
STAGECOACH_STAGER_REASONING |
--stager-reasoning |
Per-role: stager reasoning | STAGECOACH_STAGER_REASONING=low stagecoach |
STAGECOACH_MESSAGE_REASONING |
--message-reasoning |
Per-role: message reasoning | STAGECOACH_MESSAGE_REASONING=low stagecoach |
STAGECOACH_ARBITER_REASONING |
--arbiter-reasoning |
Per-role: arbiter reasoning | STAGECOACH_ARBITER_REASONING=low stagecoach |
STAGECOACH_PLANNER_TIMEOUT |
--planner-timeout |
Per-role: planner generation timeout (; planner built-in 480s) | STAGECOACH_PLANNER_TIMEOUT=600s stagecoach |
STAGECOACH_STAGER_TIMEOUT |
--stager-timeout |
Per-role: stager generation timeout (; inherits 120s) | STAGECOACH_STAGER_TIMEOUT=300s stagecoach |
STAGECOACH_MESSAGE_TIMEOUT |
--message-timeout |
Per-role: message generation timeout (; the single-commit path's only role; inherits 120s) | STAGECOACH_MESSAGE_TIMEOUT=120s stagecoach |
STAGECOACH_ARBITER_TIMEOUT |
--arbiter-timeout |
Per-role: arbiter generation timeout (; inherits 120s) | STAGECOACH_ARBITER_TIMEOUT=120s stagecoach |
STAGECOACH_FORMAT |
--format |
Message format: <base>[+body] — auto|conventional|gitmoji|plain; append +body to force a subject+body; unknown = hard error (exit 1) |
STAGECOACH_FORMAT=conventional+body stagecoach |
STAGECOACH_LOCALE |
--locale |
Message language (free-form; never validated) | STAGECOACH_LOCALE=ja stagecoach |
STAGECOACH_TEMPLATE |
--template |
Message template; $msg = generated message; must contain $msg (hard error) |
STAGECOACH_TEMPLATE='$msg (#205)' stagecoach |
STAGECOACH_PUSH |
--push |
Run git push after a fully-successful run (true = push; false = disable); on failure commits stand, exit 1 |
STAGECOACH_PUSH=1 stagecoach |
STAGECOACH_AUTO_STAGE_ALL |
--no-auto-stage (inverse) |
Auto-stage all when nothing staged (true = enable, false = disable) | STAGECOACH_AUTO_STAGE_ALL=false stagecoach |
STAGECOACH_MULTI_TURN_FALLBACK |
(no flag) | Enable lossless multi-turn fallback on large diffs (true = enable, false = disable) | STAGECOACH_MULTI_TURN_FALLBACK=false stagecoach |
STAGECOACH_NO_PARENT_WATCHDOG |
(no flag) | Opt out of the parent-death lock watchdog. Presence-semantic with a DIRECT set: =1/true disables it; =false is an explicit escape hatch. SIGHUP handling and lock status are unaffected (always on). |
STAGECOACH_NO_PARENT_WATCHDOG=1 stagecoach |
Git-config keys¶
These keys live in .git/config (set with git config --local or git config --global):
| Key | Type | Reads with | Description |
|---|---|---|---|
stagecoach.provider |
string | git config --get stagecoach.provider |
Default provider |
stagecoach.model |
string | git config --get stagecoach.model |
Model override |
stagecoach.timeout |
string | git config --get stagecoach.timeout |
Generation timeout (duration string) |
stagecoach.role.<role>.timeout |
string | git config --get stagecoach.role.<role>.timeout |
Per-role generation timeout; <role> ∈ planner|stager|message|arbiter. Duration string ("600s" or bare 600); unset ⇒ inherit global stagecoach.timeout. |
stagecoach.autoStageAll |
bool | git config --get --bool stagecoach.autoStageAll |
Auto-stage all when nothing staged |
stagecoach.output |
string | git config --get stagecoach.output |
Agent output mode: raw | json (overrides per-provider default) |
stagecoach.stripCodeFence |
bool | git config --get --bool stagecoach.stripCodeFence |
Strip ``` fences from agent output (overrides per-provider default) |
stagecoach.tokenLimit |
int | git config --get stagecoach.tokenLimit |
Holistic token budget for the whole payload; 0 = unset ⇒ legacy max_diff_bytes/max_md_lines caps. Supersedes both legacy caps when >0 (mutually exclusive). |
stagecoach.diffContext |
int | git config --get stagecoach.diffContext |
Unchanged context lines per hunk: 0 = changed-lines-only, 1 = one anchor line (default), 3 = git default. An explicit 0 is honored (changed-lines-only is a first-class value). |
stagecoach.format |
string | git config --get stagecoach.format |
Message format: <base>[+body] — auto | conventional | gitmoji | plain. Append +body to force a subject+body. Unknown = hard error (exit 1). |
stagecoach.locale |
string | git config --get stagecoach.locale |
Message language (free-form name or BCP-47 tag; never validated). |
stagecoach.template |
string | git config --get stagecoach.template |
Message template; the literal $msg is replaced with the generated message. Must contain $msg (hard error, exit 1). |
stagecoach.push |
bool | git config --get --bool stagecoach.push |
Run git push after a fully-successful run. On failure the commits stand — git's stderr is shown verbatim, "commits created; push failed" prints, exit 1. |
stagecoach.noParentWatchdog |
bool | git config --get --bool stagecoach.noParentWatchdog |
Opt out of the parent-death lock watchdog. Default false (the watchdog runs by default); set true for intentional-detach workflows (nohup/setsid/systemd-run). |
Note
The git-config layer has per-role timeout keys (stagecoach.role.<role>.timeout), but no per-role provider/model/reasoning keys — those are CLI flags (--planner-provider, etc.), env vars (STAGECOACH_PLANNER_*), and config-file [role.*] blocks only. There is no stagecoach.commits and no stagecoach.max_commits (decompose settings --commits/--single/--no-decompose are flag/env only; --max-commits also reads from the [generation] config-file section). There is also no stagecoach.exclude git-config key and no STAGECOACH_EXCLUDE env var (deliberate — see Exclusion globs below); exclusions are config-file + --exclude/-x only.
Decompose config keys¶
| Setting | Flag | Env var | Config file | Default | Notes |
|---|---|---|---|---|---|
| Commit count | --commits <N> |
STAGECOACH_COMMITS |
— | 0 (auto) |
0=auto-decompose; ≥2=force N; 1≡--single |
| Single-commit | --single / --no-decompose |
— | — | false |
Bypass decompose → v1 single-commit |
| Max commits | --max-commits <N> |
— | [generation].max_commits |
12 |
Safety cap on auto-decompose count |
Per-role provider/model overrides (flag > env > [role.<role>] config > [defaults] > built-in): see providers.md for the compiled-in defaults per provider. Every role (including message) exposes --<role>-provider/--<role>-model/--<role>-reasoning/--<role>-timeout (/). Each role also resolves its own generation timeout: the planner defaults to 480s (the heavy role that reasons over the full frozen diff), while stager/message/arbiter inherit the global 120s. Override a role with --<role>-timeout / STAGECOACH_<ROLE>_TIMEOUT / [role.<role>].timeout / stagecoach.role.<role>.timeout (precedence: flag > env > [role.<role>] > built-in > [defaults]). Note the global --timeout does not change the planner — it has a 480s built-in that wins; set --planner-timeout to override it.
Important
Precedence gotcha: because per-role config beats the global [defaults], a [role.<role>] entry silently shadows an explicit --model/--provider (which set the GLOBAL default only) for that role. This is correct per but an easy footgun — e.g. a [role.message] model = "…" config means stagecoach --model X uses the config's model for the commit. Use --message-model (or --message-provider) to override the message role specifically, or run with --verbose to see a DEBUG: note: --model shadowed by [role.message].model; use --message-model to override hint when shadowing is active. See cli.md for the full precedence note.
Exclusion globs ([generation].exclude)¶
[generation].exclude (config file, both global and repo-local) and the repeatable --exclude <glob> / -x <glob> CLI flag exclude matching files' diff content from the agent payload — a placeholder line stands in for the diff; the file is still captured and committed normally. Patterns are gitignore-style globs.
Important
This is the one setting in the whole precedence system that UNIONS instead of overriding. Every other list-valued key (e.g. [generation].binary_extensions) REPLACES across layers — a higher layer's list wins outright. exclude instead accumulates: the resolved set is the global file's globs, followed by the repo file's globs, followed by every --exclude/-x occurrence, in that order. A repo cannot use its local config to un-exclude a glob a user set globally.
There is deliberately no STAGECOACH_EXCLUDE environment variable and no stagecoach.exclude git-config key — a colon/comma-joined env list is a well-known quoting trap for glob patterns containing those characters. Use the config file for persistent excludes and --exclude/-x for ad-hoc ones.
.stagecoachignore¶
A repo can place a .stagecoachignore file at its root (alongside .stagecoach.toml) containing one gitignore-style glob per line (,). Blank lines and # comment lines are ignored. The globs are unioned with [generation].exclude and --exclude/-x (see Exclusion globs above).
Warning
Negation (!) is NOT supported. Git pathspec excludes have no re-include mechanism — a ! line is silently skipped with a --verbose warning. This is intentional: the translated :(exclude,glob) pathspecs cannot un-exclude.
A missing .stagecoachignore is a no-op (no warning, no error).
Lock file location¶
The per-repo run lock is stored outside the repository to avoid polluting git status, being committable, or being ambiguous across worktrees. The lock file location resolves in this order:
$XDG_RUNTIME_DIR/stagecoach/locks/<hash>.lock— whenXDG_RUNTIME_DIRis set and absolute$XDG_CACHE_HOME/stagecoach/locks/<hash>.lock— whenXDG_CACHE_HOMEis set and absolute~/.cache/stagecoach/locks/<hash>.lock— fallback viaos.UserHomeDir
Where <hash> is the sha256 hex digest of the repo's canonical absolute path (resolved via filepath.EvalSymlinks to handle symlinked paths). Relative XDG values are ignored (only absolute paths are honored). If no resolution path exists, stagecoach exits with an error — it never falls back to the current working directory or the repo itself.
To inspect the current repo's lock holder (path, pid/host, liveness, orphan status), run stagecoach lock status; see CLI reference — lock status.
Exclusions are payload-only: excluded files are hidden from what the agent sees but are still captured and committed normally.
Example: