---
name: war-room
description: Interview the user to produce a WAR run configuration at .claude/war/config.json — per-role model and reasoning effort (e.g. a worker on ultrathink), the fix-worker and red-team model/effort tiers, the audit roster (1–5 distinct-lens seats, per-seat depth) + roster policy, round limit, the afk default, and the memory asks (prior-lesson prefetch topK; whether to commit distilled learnings to the repo). Presents presets (balanced/thorough/economy) then grills only the overrides the user wants. Writes the config and stops; /war auto-discovers and consumes it. Use when the user runs /war-room, wants to configure a WAR run, choose which models or how much effort WAR's agents use, or shape the audit roster WAR convenes.
---

# /war-room — configure a WAR run

You produce a **run config** for `/war` and nothing else: you write `.claude/war/config.json`, then stop. You never decompose a plan, spawn agents, or launch `/war`. The schema, defaults, presets, and validator are owned by [`../war/assets/war-config.mjs`](../war/assets/war-config.mjs) — that script is the source of truth; shell out to it, don't reinvent the schema. Full field reference: [`../war/references/schemas.md`](../war/references/schemas.md) (§ Run config).

## Flow
1. **Pick a starting point.** If `.claude/war/config.json` already exists, show it and offer to start from it. Otherwise present the three presets and ask which to start from:
   - **balanced** — the defaults: workers opus/`default`, docs-tier sonnet/`high`, fix-tier opus/`default`, auditors sonnet/`high`, refiner sonnet/`high`, recovery-tier opus/`high`, servitor sonnet/`xhigh`, red-team opus/`high`, snipe opus/`high`, trial-tier opus/`default`. Roster pool: five seats (`correctness`, `cascading-impact`, `plan-faithfulness`, `simplicity`, `performance`) at `deep`, seeded per task by `rosterPolicy: auto` — the Lead composes 1–5 seats per task from the lens catalog, each with an explicit per-seat depth and a one-line rationale (leaf tasks get one `neighbors` seat); `run.ace` on.
   - **thorough** — workers fable/`default`, docs-tier fable/`default`, fix-tier fable/`default`, auditors fable/`default`, refiner sonnet/`xhigh`, recovery-tier opus/`high`, servitor opus/`high`, red-team fable/`default`, snipe fable/`default`, trial-tier fable/`default`. Five-lens pool (`correctness`, `cascading-impact`, `plan-faithfulness`, `security`, `test-fidelity`) under `rosterPolicy: auto` — the Lead composes 1–5 seats per task from the catalog, each at its own depth with a one-line rationale; `run.ace` on.
   - **economy** — workers opus/`default`, docs-tier sonnet/`high`, fix-tier opus/`default`, auditors sonnet/`xhigh`, refiner sonnet/`high`, recovery-tier opus/`high`, servitor sonnet/`high`, red-team sonnet/`default`, snipe opus/`high`, trial-tier sonnet/`high`. Its own pinned four-lens pool (`correctness`, `cascading-impact`, `plan-faithfulness`, `security`) under `rosterPolicy: auto` (inherited — the Lead composes each task's seats from the catalog; this pool is the widening fallback), `roundLimit: 4`, `redteamRoundLimit: 2`, `run.ace` inherited on. Use when you want cheaper review seats and a shorter fix budget.
   - **balanced skill seats** — the defaults: machine-drafter opus/`default`, machine-derive opus/`default`, machine-apply opus/`default`, machine-revise fable/`default`, survey-miner sonnet/`high`, survey-reader sonnet/`high`, survey-cluster opus/`default`, survey-synth opus/`default`, survey-critic opus/`default`, lessons-investigate sonnet/`high`, lessons-edit opus/`default`, lessons-retype sonnet/`high`, lessons-keywords sonnet/`high`.
   - **thorough skill seats** — one model tier above balanced, capped at fable: machine-drafter fable/`default`, machine-derive fable/`default`, machine-apply fable/`default`, machine-revise fable/`default`, survey-miner opus/`default`, survey-reader opus/`default`, survey-cluster fable/`default`, survey-synth fable/`default`, survey-critic fable/`default`, lessons-investigate opus/`default`, lessons-edit fable/`default`, lessons-retype opus/`default`, lessons-keywords opus/`default`.
   - **economy skill seats** — a little below balanced: machine-drafter opus/`default`, machine-derive sonnet/`high`, machine-apply sonnet/`high`, machine-revise opus/`default`, survey-miner sonnet/`high`, survey-reader sonnet/`high`, survey-cluster opus/`default`, survey-synth opus/`default`, survey-critic sonnet/`high`, lessons-investigate sonnet/`high`, lessons-edit sonnet/`high`, lessons-retype sonnet/`high`, lessons-keywords sonnet/`high`.
   Tier tokens read ``role model/`effort` `` (`default` effort = the seat inherits the launching session's effort knob); presets order by model capability, and the ordering rule is [ADR 0050](../../docs/adr/0050-preset-tiers-order-by-model-capability.md). `war-config.test.mjs` parses every token in these six bullets against `presetConfig()`, so keep that shape when you edit them. Render the concrete JSON so the user sees exactly what they start from: `node ${CLAUDE_PLUGIN_ROOT}/skills/war/assets/war-config.mjs --preset <name>`.
2. **Offer overrides.** Ask "anything to change?" and grill **only** the dimensions the user names, one at a time. Allowed values (reject anything else — the validator will too):
   - `agents.<role>.model` ∈ `opus | sonnet | haiku | fable`. Roles: `worker` (the base tier — fix rounds and `--ace` inherit it unless split via `agents.worker.fix` / `agents.worker.ace`, and docs-only tasks use `agents.worker.docs`; see **worker tiers** below), `auditor`, `refiner`, `servitor`.
   - `agents.refiner.recovery` — separate `{ model, effort }` tier for uncertain Git mutations. It inherits the recovery default, independently of the routine refiner; see `war-config.mjs` and [refiner recovery](../war/references/refiner-recovery.md#uncertain-merge-reconciliation). Override it when configuring judgment-heavy in-phase maintenance.
   - `agents.auditor.rebuttal` — optional `{ model, effort }` tier for the seats of a split panel's rebuttal round. It replaces each rebuttal seat's tier, the seat's own roster `model`/`effort` included. No preset sets it: absent, each rebuttal seat keeps its usual tier. Write it only when the user names a value.
   - `agents.<role>.effort` ∈ `default | low | medium | high | xhigh | max`. `default` is not the lowest effort: the seat inherits the effort knob of the chat session that launched the run, and any other value overrides that knob for the one seat. Translate words: "ultrathink" → `max`, "think hard/harder" → `high`/`xhigh`, "normal"/"leave it" → `default`.
   - **Worker tiers** — refine which model runs which worker dispatch (same `model`/`effort` vocabulary; neither is a phase role):
     - `agents.worker.docs` (the `docs-tier` token in the preset bullets above) — the tier for **docs-only tasks**. The predicate is mechanical: a task is docs-tier **iff every `Files:` entry matches `*.md`**, evaluated against the **plan's file list, not the diff**. One non-`.md` file in the task → it falls back to the base worker tier.
     - `agents.worker.fix` (**preset-populated** — the `fix-tier` token in the preset bullets above) — the tier for **fix rounds and floor retries**, and for `--ace` when `agents.worker.ace` is unset, so follow-up work can run on a different model than the first pass, in either direction. Override it like any other tier; a hand-written config that drops the block simply lets fix-round and ace workers inherit the base worker.
     - `agents.worker.ace` (**not preset-populated** — ask about it right after the fix tier) — the tier for **`--ace` rounds and the terminal pass** (#2519), so ace can run on a different model than fix rounds. Absent ⇒ the fix tier, exactly as before the split. Write it only when the user names a value.
   - `agents.redteam.model` / `agents.redteam.effort` (same value vocabulary) — the model/effort read **fail-open by `/red-team`** for its probe and adversarial-confirm sub-agents. Not a phase role, but **every preset sets it** (the `red-team` token in the preset bullets above) so it is never blank; override it for the model you want `/red-team` to run against this repo. Only a *missing* config file makes `/red-team` fall back to the session.
   - `agents.snipe.model` / `agents.snipe.effort` (same value vocabulary) — the tier `/snipe` spawns its one-shot audit seats at (#1920). Not a phase role; preset-populated (the `snipe` token in the preset bullets above), explicit `null` = unset (falls back to the auditor tier). Override it for cheaper or heavier quick audits.
   - `agents.trial.model` / `agents.trial.effort` (same value vocabulary) — the tier `/war-strategy`'s plan interview spawns its fork-trial subagent at (D18), read **fail-open** like `agents.redteam`. Not a phase role and no agent card, but every preset re-pins it (the `trial-tier` token in the preset bullets above) so it is never blank; override it for a cheaper or heavier throwaway trial.
   - `agents.<skill>.<seat>` (same value vocabulary) — the subagent seats of `/war-machine` (`machine`), `/survey-corps` (`survey`) and `/lessons-learned` (`lessons`); the seat names are `SKILL_SEATS` in `war-config.mjs`, and each preset's values are its **skill seats** bullet above. Each skill reads them **fail-open** and gives each role's `agent()` its seat's model. Override a single seat, for example `agents.machine.drafter`, when the user names it.
   - `audit.roster` — an array of seat objects `{ lens, depth?, agentType?, model?, effort? }` (lenses distinct; a seat's `model`/`effort` use the role vocabulary and override the auditor tier for that seat only, except in a rebuttal round when `agents.auditor.rebuttal` is set; the seat bound, the depth enum and the depth default per `validateRoster` in `war-config.mjs` — never restate them here; `agentType` ∈ `SEAT_AGENT_TYPES`, the `war-config.mjs` export — the evidence member needs `run.evidence` on). Offer the evidence lens `artifact-evidence` (`EVIDENCE_LENS` in `war-config.mjs`, with `agentType` the evidence member of `SEAT_AGENT_TYPES`) only when `run.evidence` is on AND the presence check on the `run.evidence` row passed; otherwise never list it. It is never a widen nominee (`WIDEN_BARRED_LENSES`). Never write an evidence-type row into `audit.roster`: the config is reused across runs, its pool feeds every task, the widen fallback and the sweep panel, a five-seat pool plus the row fails `validateRoster`, and the `/war` Lead strips any such row at Decompose. `run.evidence` on is the opt-in record: accepting keeps it on and writes nothing else, declining turns `run.evidence` off, because the knob is the only record and it gates the worker clause too, and the `/war` Lead seats the lens only on tasks whose slice cites a `rea:` tag ([artifact-evidence-lead.md](../war/references/artifact-evidence-lead.md)). Present the roster as a **seat list** — one lens + depth per seat. Present the offer as one yes/no question after the seat list, never as a seat-list entry. `audit.rosterPolicy` ∈ `auto | all | solo`; `audit.autoEscalate` (bool). Legacy `covenSize`/`lenses`/`covenPolicy` keys fail validation (removed/renamed) — never write them.
   - `run.maxParallel` — the per-run agent ceiling (the pacing knob for rate-limited accounts). In the /war engine, one global counting semaphore caps agent dispatches in flight across the whole run at that many: workers, auditors, aces, fix workers and gate-audit seats share the one counter, so a wave of N tasks each running its own audit roster still holds at most N sub-agents at once. `/red-team` reads the same knob fail-open and applies it to the probe groups in its own scratch scaffold — that group slicing is a separate, ratified non-goal, outside this ceiling. **Default: leave it absent** — absent means today's unthrottled dispatch and no preset sets it; only pin a value when the operator names this dimension. An explicit `null` is unset too, the same as absent. Shape and validation live in `war-config.mjs` `validate()` — never restate them here.
   - `run.evidence` — the REA artifact-evidence opt-in, the run-side half of the evidence-seat gate: `validate()` admits an evidence-type seat row, the evidence member of `SEAT_AGENT_TYPES`, only while it is on, and the Lead also resolves REA presence before launch. **Default: leave it absent** — absent is off (today's byte-identical run) and no preset sets it; an explicit `null` is unset too, the same as absent. Shape and default per `war-config.mjs` `validate()` — never restated here. When the operator opts into `run.evidence`, read [references/rea-setup.md](references/rea-setup.md) and walk them through it (install, providers, the env rule per shell, verification). Then run the presence check and record the result for the roster row. `rea --version` shows the CLI; if it differs from the version the evidence-auditor MCP hook's header records (`Derived at rea-agents: `), print one loud line: re-derive the allowlist before relying on the evidence seat. REA is present when the tool registry carries the presence tool `mcp__rea__binary_session`, or when `zsh -c 'rea doctor --json'` / `bash -c 'rea doctor --json'` (a non-interactive shell on purpose) reports the providers the plan's artifacts need as ok (with no plan in hand, the providers the operator names; the `/war` Lead re-checks against the plan's rows at launch). If it passes, make the evidence-lens offer from the `audit.roster` row even when the operator did not name the roster. A failed check never blocks the config: the knob stays on, the seat is simply not offered.
   - `run.roundLimit` (integer ≥ 1); `run.absorbRounds` (integer ≥ 1 (default 6) — the per-task absorb budget, the ace ladder's own round meter, separate from `run.roundLimit`); `run.afk` (bool); `run.ace` (bool — the per-task ace ladder: pre-merge auto-fix of auditor-flagged nits; default `true`); `run.redteamRoundLimit` (integer ≥ 1; default `3`, the economy preset pins `2`) — `/red-team`'s cumulative grill-round budget, read fail-open by `/red-team`, never the phase engine; at the limit with an unadjudicated root finding open — or from round 2 with an unadjudicated needs-decision finding — the gate routes the plan back upstream (never while coverage is incomplete).
   - `interview.trialWallSeconds` / `interview.commandTimeoutSeconds` / `interview.excerptLines` — the fork-trial bounds `/war-strategy`'s plan interview reads fail-open (the wall cap per trial, the per-command timeout inside the sweep, the fenced-excerpt cap), never the phase engine. Each a positive integer; an explicit `null` is unset (the interview falls back to the default per key). No preset sets the section. Shape and defaults per `war-config.mjs` `validate()` and `DEFAULTS.interview` — never restated here.
   - `overrides.gate` / `workingBranch` / `landingBranch` / `learningsTarget` / `testPattern` — `null` lets `/war` auto-detect; a string pins it. `testPattern` is the **test-floor glob set** (space-separated single-star tokens, e.g. `'*.test.ts *.test.tsx'`): `null` = today's floor defaults (`/war` proposes one from the target's test conventions at Setup, pinned **together with the gate** — floor ⊆ gate — though under `--afk` that confirmation is not the final word on the *value*: a proposal the sanity floor rejects stays **pending** and is re-checked at each phase launch, adopted monotonically once every token matches and the value re-passes the validator's `overrides.testPattern` check); a string pins the target's glob set. Its **glob-safe charset is enforced by the validator** — quotes, `;`, backticks, `$`, and newlines are rejected (the value is embedded into an agent-executed shell command).
   - `overrides.ghUser` — the personal `gh` account `/war` asserts is active before every gh write batch (via `gh-preflight.sh`), for multi-account machines; `null` (default) skips the check. It stays in the **local** `.claude/war/config.json` and ships `null` — never commit a real handle.
   - `memory.topK` (integer ≥ 1, default `10`) — max prior-lesson blocks the Lead prefetches per seat. `memory.retrieval` (bool, default `true`) — set `false` to skip the prefetch entirely. (`memory.commitLearnings` is **not** an override here — it is the standing opt-in ask in step 3.)
   - `hooks.replyStandard` (bool, default `true`; `null` = unset, the default applies) — toggles the plugin's Reply Standard hook family under `hooks/reply-standard/`: the `UserPromptSubmit` card + `Stop` meter, and the WAR-seat pair — a `SubagentStart` seat-edition card + `SubagentStop` meter (matched to `work-audit-refine:war-*` agent types, rows to `subagent-meter.log`). Read fail-open by the hooks' own gate wrapper in the project the session runs in, never by the phase engine; only an explicit `false` disables.
   - `hooks.valeMarkdown` (bool, default `true`; `null` = unset, the default applies) — toggles the plugin's advisory Vale Markdown lint (`PostToolUse` on `Edit`/`Write`, `.md` files only, under `hooks/vale-md/`). Self-gated fail-open by the hook script itself, never by the phase engine; advisory only, and a machine without the `vale` binary silently no-ops; only an explicit `false` disables.
   - `hooks.valeStyle` (enum, default `workAuditRefine`; `null` = unset, the default applies) — vale-md profile selector: `house | workAuditRefine | google | microsoftFork | writeGood | proselint | alex | readability | redhat | custom`. `workAuditRefine` is the repo's tuned fork (house rules + a tuned Google cut); `google` is raw upstream Google. Every named style is vendored in the plugin (`hooks/vale-md/styles/`, no network); `custom` reads the project-side `.claude/war/vale/.vale.ini` and fail-opens to the default when that file is absent. Subordinate to `hooks.valeMarkdown` — when that is `false`, nothing runs. When the operator picks `custom`, read [references/vale-custom-interview.md](references/vale-custom-interview.md) and run that interview — its first act is a non-blocking repo pattern-mining subagent, then the grill, then the project-side profile write.
   When the user touches roster settings, read the canonical `audit.autoEscalate` row in [`../war/references/schemas.md`](../war/references/schemas.md) (§ Run config) — **`rosterPolicy: "solo"` alone does not pin one auditor**; that row gives the widen semantics and the `autoEscalate: false` pin.
3. **Ask the learnings-publication opt-in** — a **standing ask every run** (not an override the operator must name). Publication is **off by default**: distilled lessons stay in the local, untracked memory root unless the operator opts in here. Surface the value pitch, then record the answer:
   > Commit distilled, lint-scrubbed engineering lessons under `docs/learnings/` so they travel with the repo and compound across your team? Each rides its phase PR and is human-reviewed like code.
   Yes → set `memory.commitLearnings: true`. No or silence → leave it `false` (the default). Either way the fail-closed redaction lint guards what publishes, and local-root lessons are always captured regardless.
4. **Resolve provisioning** (`run.provision` — the commands that make a fresh worker worktree gate-ready). Ask the module what to do rather than deciding yourself — pass the assembled-so-far config to `resolveProvision` (exported by `war-config.mjs`); it returns `{ provision, source, scout }`:
   - **`scout: false` with a non-empty `provision`** — the operator pinned an explicit list (or it was carried from an existing config). Honor it **verbatim**; do **not** run the scout. Leave `run.provision` and `run.provisionSource` exactly as they are.
   - **`scout: false` with an empty `provision`** — `provisionAuto` is off. Leave `run.provision: []` / `run.provisionSource: "none"`; no scout, no steps.
   - **`scout: true`** — `provisionAuto` is on and no explicit list exists. Run the read-only setup-scout (`agents/war-setup-scout.md`) against the target repo via `Agent`. Present its **proposed `provision` list + `source` + `rationale`** to the operator and ask them to confirm or edit. Write the **CONFIRMED** list into `run.provision` and the scout's `source` into `run.provisionSource`. If the operator declines all steps, write `run.provision: []` / `run.provisionSource: "none"`.
   - **`--refresh-provision`** — when invoked with this flag, first **clear** any existing `run.provision` (set it to `[]` and `run.provisionSource: "none"`) so `resolveProvision` reports `scout: true`, then re-scout as above. This is the only way to re-derive a list that is already pinned (a non-empty list otherwise short-circuits the scout).
5. **Submodule overlap check.** Before showing the final config, inspect the plan file (if provided) for task target paths. If any plan target path overlaps with a `.gitmodules` submodule path in the repo (i.e. any target is under a declared submodule prefix), surface a **setup warning** for each overlap before proceeding:
   > ⚠ task `<task-id>` targets `<path>` inside submodule `<submodule-path>`; expect `/war`'s submodule router to propose a submodule task + paired gitlink-bump task for your approval at Decompose — see [`../war/references/submodule-flows.md`](../war/references/submodule-flows.md) (§ Submodule router). An undeclared submodule touch is still refused at merge.
   If no `.gitmodules` exists, or no overlaps are found, continue silently. This warning does not block config writing — it is informational so the operator can decide before investing a full run.
6. **Show the assembled config** as pretty JSON and ask for explicit confirmation.
7. **Validate, then write.** Validate through the module and only write on success (write to a temp file first so a failure never truncates an existing config):
   ```bash
   mkdir -p .claude/war
   printf '%s' '<assembled-json>' \
     | node ${CLAUDE_PLUGIN_ROOT}/skills/war/assets/war-config.mjs --stdin --fill-defaults \
     > .claude/war/config.json.tmp \
     && mv .claude/war/config.json.tmp .claude/war/config.json \
     || { echo "validation failed — config NOT written"; rm -f .claude/war/config.json.tmp; }
   ```
   If it failed, show the validator's errors, fix them with the user, and retry. Never write an invalid or unvalidated file. (`--fill-defaults` writes the complete resolved config, so the file is self-describing.)
8. **Stop.** Tell the user: *"Config written to `.claude/war/config.json` (profile: <profile>). Run `/war <plan>` to use it, or `/war <plan> --config <path>` to point at a different file."* Do not launch `/war`.

## Notes
- Conversation-only: no `Workflow`, no git writes beyond the single config file. The **only** `Agent` you may spawn is the read-only setup-scout (step 4), and only when `resolveProvision` reports `scout: true` (or `--refresh-provision` was passed); it is `Read/Grep/Glob`-only and changes nothing.
- If a `[Fact-Forcing Gate]` (GateGuard) blocks the write, present the facts it lists, then retry the identical command.
