THE PRODUCT FACE
War Room
War Room is the operator surface and the product face: where you convene, steer, pause, and read the record. Every view, every control, every transport. The browser UI and the macOS desktop shell share one backend.
The views
Twelve views in one War Room. Each view names its controls and exactly what they do. Nothing here hides a second backend: the browser tab and the desktop shell are two windows into the same services.
Deliberate
Streams a multi-seat session over /ws/deliberate.
Between rounds, when the server reports awaiting_input, the operator can send an intervention into the proceeding. The stream speaks in typed events, so the UI always knows which act it is in:
Every control on the convene desk, and everything you can do at the pause, is spelled out in the two sections below.
Direct Fire
One seat, one shot, no rounds.
A single model answers a single prompt with a chosen lens: contrarian tears down the framing, munger inverts the assumptions, kiss strips to essentials, specops hunts the adversarial signal, and the experimental premortem assumes the plan already failed and works backward. CLI parity: --contrarian, --munger, --kiss-review, --specops, --premortem. The stream skips straight from session_started to synthesis: no rounds, no convergence, no pause.
History
The record of proceedings.
Saved sessions, their synthesis, their lineage, and their exports. What the council said stays findable.
Outbox
Reads signed Gateway directives.
This is where signed directives surface. An empty Outbox means there is no signed directive from the configured Gateway. It is not an error; nothing has been signed yet.
Watch
Sentinels, fires, and the watch plane.
Shows registered Sentinels, their fires, and watch-plane status. It stays readable while action production is disabled, because observing is not acting.
Discover
Scans exact transports, nothing more.
Three probes: non-empty API-key environment variables (names only, never values, no billable call), supported local CLI binaries, and reachable local runtimes. Unavailable rows stay visible for setup guidance but are disabled in selectors. Rescan refreshes the browser-cached result. There is no background polling.
Cabinets
Reads and edits local cabinet YAML.
The server is the YAML-validity authority, and it refuses to overwrite built-ins. Cabinet keys must match ^[a-z0-9][a-z0-9_-]{0,63}$.
Patterns
Your intervention habits, counted.
Every pause decision you make is logged, and this view plays it back: counts per action (Continue, End early, SpecOps, Munger, Contrarian, KISS, Inject context, Swap seat) over a chosen window, default 30 days, plus the full paginated intervention log from /api/interventions with copyable session ids. The council audits you a little, too.
Drift
Compares normal versus blind reruns.
Re-runs normal sessions in blind mode and measures how much precedent injection actually shaped the ruling. Weekly drift history feeds the dashboard card and Meta-review. Notifications are delivered via COUNCIL_WEBHOOK_* environment variables.
Meta-review
The self-audit loop.
Reads the weekly drift history plus your intervention history and recommends one parameter to tune. One, not ten: a self-audit that produces a single next move is one you might actually run.
Librarian
An optional tab, never a dependency.
Proxies a separately configured local Librarian service. If that service is offline, Council and Gateway carry on untouched. Memory is a guest here, not a load-bearing wall.
Settings
The bases and the token.
API base, WS base, Gateway base, Librarian base, auth token, and Council root, all stored in local storage. Prefer loopback: a non-loopback endpoint can leak the token.
Deliberate or Direct Fire
Two ways to ask, one honest split. Deliberate convenes a cabinet: assigned seats, counted rounds, convergence scoring, an operator pause, and a chair ruling filed at the end. Direct Fire sends one prompt to one model through one lens and returns one answer.
Choose Deliberate when
The question deserves disagreement: a decision with stakes, a design worth attacking from several blind spots, anything where you want positions challenged across rounds and a ruling with the dissent still attached. It costs more seats, more tokens, and more of your attention at the pause.
Choose Direct Fire when
You want one mind and one move: tear down this framing, invert these assumptions, strip this to essentials, run a premortem. No rounds, no convergence, no pause, one saved result. If the answer surprises you, promote the question to a full proceeding.
Convene: every control on the desk
This is the full Deliberate start panel, control by control. Nothing on it is decorative.
best, sovereign, or strict_sovereign.X-Sensitivity-Level. The start is blocked if the cabinet references a Direct-only transport.local_code_only cabinets. Details on the Council page.worker_provenance JSON blob.The pause: the round ends, you speak
With the default pause on, the stream stops at awaiting_input after each round, which means the operator gets a word in before round 2 even exists. The panel shows the convergence score of the round just finished and waits for your call. These are the controls, exactly as they appear.
Why the pause is load-bearing
The pause is the Reality slot expressed as UI. Convergence is scored every round, an intervention predictor may hint when an escalation looks warranted, and nothing proceeds past a paused round until you choose. Turning the pause off is a choice too, and it is yours.
The transport matrix
A seat is bound to exactly one transport, and that binding is never silently switched. These are the built-in transport IDs and what each one actually talks to.
grok CLI.XAI_API_KEY.ANTHROPIC_API_KEY.OPENAI_API_KEY.agy CLI.VERTEX_PROJECT, VERTEX_LOCATION set to global, and VERTEX_GEMINI_MODEL.NVIDIA_API_KEY.NOUS_API_KEY.DEEPSEEK_API_KEY, or a configured NIM or Nous model.Seat ID rules
- One seat ID equals one transport. The binding is never silently switched.
- Legacy IDs stay readable:
grok,grok_cli,hermes_cli,claude,gpt,gemini,agy_cli. - Custom slugs via
~/.config/council/providers.tomlcannot redefine reserved built-in IDs. - Gateway adapters exist only for
grok_api,claude_api,claude_code,openai_api,codex_cli,gemini_vertex,gemini_cli, andnvidia/nim. Everything else is Direct-only.
The REST surface
The whole spoken surface of the Council backend, plus the one WebSocket. Authentication is a bearer token when COUNCIL_AUTH_TOKEN is set; the WebSocket carries that token via a negotiated subprotocol, and Gateway may require X-Gateway-Auth.
Real receipts
Not mockups. These frames come from an actual proceeding on the actual UI.
Persistence caps
Storage has hard edges, and the edges are numbers. Chat patches use optimistic concurrency via If-Match, so two writers cannot silently trample each other.
Fine print, read before trusting
- A detected CLI binary is not proof its login is still valid. The first real seat call is.
- Discovery and routing YAML never prove model entitlement.
- The War Room blocks a Governed start when the cabinet references a Direct-only transport.
COUNCIL_DEV_NO_AUTH=1must never run on a network-accessible service.- Do not delete or rewrite historical sessions without a migration plan. New fields need serde defaults.