Repository navigation
Expand file tree
/
Copy pathfleets.html
More file actions
193 lines (189 loc) · 33.6 KB
/
Copy pathfleets.html
File metadata and controls
193 lines (189 loc) · 33.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fleets and mass-spawn — atrium docs</title>
<meta name="description" content="atrium documentation: Fleets and mass-spawn. tmux for coding agents, zero third-party dependencies.">
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<header><div class="wrap bar">
<span class="dots"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span></span>
<span class="title"><a href="index.html"><b>atrium</b></a> — docs</span>
<nav>
<a href="index.html">home</a>
<a href="https://crates.io/crates/atrium">crates.io</a>
<a href="https://github.com/nativelite/atrium">github</a>
</nav>
</div></header>
<main class="wrap doc">
<div class="docnav"><a href="index.html">home</a><a href="architecture.html">architecture</a><a href="agent-status.html">agent status</a><a href="identity.html">identity</a><a href="fleets.html" class="active">fleets</a><a href="control-plane.html">control plane</a><a href="trust-and-security.html">trust & security</a><a href="worktrees.html">worktrees</a><a href="dev-fleet.html">dev fleet</a><a href="reaping.html">reaping</a><a href="session-recovery.html">session recovery</a></div>
<h1>Fleets and mass-spawn</h1>
<p>atrium can open a whole grid of agent panes from one command. Use <b>mass-spawn</b> to launch N copies of the <i>same</i> command in one window, or a <b>fleet</b> to bring up a squad of <i>different, named</i> agents, each already in-role. See the <a href="https://github.com/nativelite/atrium#readme">project README</a> for the wider tour.</p>
<h2>Mass-spawn</h2>
<p><code>-n <N></code> and <code>--grid <R>x<C></code> open a grid of agent panes in one window, instead of splitting N times by hand.</p>
<pre><code>atrium -n 4 claude # 2x2: four agents in a square
atrium -n 6 claude # 2x3
atrium -n 8 claude # 2x4
atrium --grid 2x3 claude # the same 2x3, shape given explicitly
atrium --identity work -n 4 claude # four panes, all under the `work` identity</code></pre>
<h3><code>-n <N></code></h3>
<p><code>-n</code> takes a <b>positive multiple of 2</b> (2, 4, 6, 8, and so on). An odd or zero N is a clear startup error (<code>-n must be a positive multiple of 2</code>), never a silent fall back to one pane.</p>
<p>The shape is balanced automatically: rows are the factor of N closest to <code>sqrt(N)</code>, cols are <code>N / rows</code>. The grid is therefore never taller than it is wide.</p>
<h3><code>--grid <R>x<C></code></h3>
<p><code>--grid</code> sets the dimensions yourself. The product must be at least 2.</p>
<h3>What every tile shares</h3>
<ul><li><b>Every tile runs the same command, each its own session.</b> Each agent pane gets its own <code>--session-id</code>, so the agent-aware chrome binds each one independently.</li><li><b>All under one identity.</b> If <code>--identity</code> is given, every pane runs under it. See <a href="identity.html">credential identity</a>.</li></ul>
<p>Everything after spawn is the ordinary tiled window: <code>hjkl</code> and arrows move focus, <code>z</code> zooms, <code>x</code> kills and re-tiles, and the status/identity chrome plus <code>q</code> quit all work exactly as for a hand-split grid.</p>
<p>For a saved roster of named agents, each with its own identity, working dir, and instructions, use a fleet.</p>
<h2>Fleets</h2>
<p>Mass-spawn opens N copies of <i>one</i> command. A <b>fleet</b> brings up a squad of <i>different, named</i> agents, each already in-role with its own identity, working directory, extra context dirs, and instructions, from one command.</p>
<pre><code>atrium fleet init crew # start this project's atrium.fleet.json from a template
atrium fleet up review-crew # bring up the whole squad, each on its identity
atrium fleet ls # list the fleet names in the file</code></pre>
<h3>Templates</h3>
<p><code>atrium fleet init <name> [--agents N]</code> writes <code>./atrium.fleet.json</code> from a template, so a fleet starts from a known-good shape rather than a blank file. It copies, never links — the project file is self-contained and reviewable — and refuses to overwrite one that exists. <code>atrium fleet ls --templates</code> is the menu.</p>
<p>Three <b>built-ins</b> are compiled into atrium, so they work on a fresh machine with no files anywhere:</p>
<table><thead><tr><th>Template</th><th>Roster</th><th>What it encodes</th></tr></thead><tbody><tr><td><code>solo</code></td><td>one claude, control plane on</td><td>a <code>PLAN.md</code> kept current, a checkpoint per item, a restart that picks up from disk</td></tr><tr><td><code>pair</code></td><td>a builder and a reviewer</td><td>one item per builder session; a reviewer that checks the brief, not the builder's story</td></tr><tr><td><code>crew</code></td><td>lead, builder, reviewer, integrator</td><td>the lead plans and sizes items, delegates one per fresh teammate, reaps on checkpoint, reviews in fresh sessions, restarts itself from <code>PLAN.md</code> and the board; builders in their own worktrees</td></tr></tbody></table>
<p>They are runnable statements of the rules the <code>atrium-coordinate</code> skill spells out in prose — the prompts are the standing orders, the kickoffs the openings. <code>--agents N</code> scales the builders in <code>pair</code> and <code>crew</code> (<code>builder-1</code>…<code>builder-N</code>, each in its own worktree group). Read what <code>init</code> wrote before <code>fleet up</code>: the lead's kickoff asks the human for the initiative on the bus if there is no <code>PLAN.md</code>.</p>
<p><b>Your own templates</b> are the fleets in the user-global <code>fleet.json</code> (<code>atrium config path</code> shows where it is). <code>atrium fleet init <name></code> copies one by name, as you wrote it; a fleet of yours with a built-in's name wins over the built-in, and <code>init</code> says so. That file is otherwise only a fallback for <code>fleet up</code>, so keeping your reusable rosters there costs nothing.</p>
<p>Deferred, deliberately: <code>"extends": "<template>"</code> in a project fleet (inheritance with overrides). Copy-<code>init</code> first; if you find yourself re-running it to pick up template changes, that is the signal.</p>
<h3>Where the fleet file lives</h3>
<p>A fleet lives in <b><code>atrium.fleet.json</code></b>, looked for in this order:</p>
<ol><li>The <b>current directory</b> first. Check it into the repo so a team shares the fleet.</li><li>A <b>user-global fallback</b>: <code>%APPDATA%\atrium\fleet.json</code> on Windows, <code>~/.config/atrium/fleet.json</code> elsewhere — or the full path in <code>ATRIUM_FLEET</code>, when you keep it somewhere else.</li></ol>
<p>The file is read <b>read-only</b>. atrium never writes it.</p>
<h2>The JSON format</h2>
<pre><code>{
"fleets": {
"review-crew": {
"grid": "2x2",
"identity": "work",
"agents": [
{
"name": "reviewer",
"cmd": ["claude"],
"identity": "wif:prod",
"cwd": "./review",
"add_dirs": ["../shared", "./specs"],
"prompt": "You review PRs for safety.",
"model": "opus",
"effort": "high"
},
{ "name": "builder", "cmd": ["claude"], "cwd": "./app" }
]
}
}
}</code></pre>
<h3>Fleet-level fields</h3>
<table><thead><tr><th>Field</th><th>Required</th><th>Meaning</th></tr></thead><tbody><tr><td><code>grid</code></td><td>optional</td><td>An <code>RxC</code> layout that must fit the agent count. Omit it and the window auto-grids from the number of agents.</td></tr><tr><td><code>identity</code></td><td>optional</td><td>A default <code>akey</code> identity for every agent. A per-agent <code>identity</code> overrides it.</td></tr><tr><td><code>allow_ctl</code></td><td>optional</td><td>Bring <a href="control-plane.html">the control plane</a> up, as <code>--allow-ctl</code> does.</td></tr><tr><td><code>mod</code></td><td>optional</td><td><code>false</code> keeps <a href="control-plane.html#the-mod-typed-tools-the-role-section-and-subagent-panes">the mod</a> out of this fleet's claude panes; absent or <code>true</code> injects it when <code>atrium mod install</code> has been run. The config's <code>"mod": false</code> fills an absent key.</td></tr><tr><td><code>subagents</code></td><td>optional</td><td>How the model's subagents run in claude panes: <code>panes</code> (visible panes beside the caller, the default), <code>native</code> (the engine's own, invisible) or <code>deny</code>.</td></tr><tr><td><code>respawn_at</code></td><td>optional</td><td>A context-window fill, in percent, at which a claude pane that is at its prompt gets one <code>decision_needed</code> posted to its parent on the <code>atrium</code> topic ("checkpoint and respawn it?"), with the parent woken. Never a respawn by itself. Needs the mod.</td></tr><tr><td><code>subagents_keep</code></td><td>optional</td><td><code>true</code> leaves a subagent pane open after its answer was collected (default: closed).</td></tr><tr><td><code>captions</code></td><td>optional</td><td><code>false</code> keeps the mod from spending a small model call on each pane's caption (the six-word summary of its own words); the caption from tool calls stays, free. Default <code>true</code>.</td></tr><tr><td><code>trust</code></td><td>optional</td><td>The trust posture the whole fleet runs at.</td></tr><tr><td><code>context</code></td><td>optional</td><td>Shared knowledge and memory backend for the fleet (see <a href="#shared-context">Shared context</a>).</td></tr><tr><td><code>topics</code></td><td>optional</td><td>The fleet's canonical bus-topic vocabulary (an array of strings): the topics its agents coordinate on. See <a href="control-plane.html">the control plane</a>.</td></tr><tr><td><code>worktrees</code></td><td>optional</td><td><code>true</code> gives <b>every</b> agent its own git worktree and branch (a full fan-out), so parallel workers never clobber one shared tree. See <a href="worktrees.html">worktrees</a>.</td></tr><tr><td><code>worktree_base</code></td><td>optional</td><td>Where those worktrees are created; defaults to a sibling of the repo so they never show up as untracked files inside it. See <a href="worktrees.html">worktrees</a>.</td></tr><tr><td><code>worktree_seed</code></td><td>optional</td><td>Untracked paths (an array of strings) linked from the main tree into each fresh worktree — config or build inputs git does not track. Best-effort (junction/symlink, copy fallback); never fails a launch. See <a href="worktrees.html">worktrees</a>.</td></tr><tr><td><code>build_jobs</code></td><td>optional</td><td>Total compiler jobs the whole fleet's builds share (a whole number; <code>0</code> = off). Default: one per core, bounded by RAM.</td></tr><tr><td><code>deny</code></td><td>optional</td><td>Commands no agent in the session may run, including workers a lead spawns later (an array of claude rules like <code>"Bash(git push --force*)"</code> or command prefixes like <code>"cargo test --workspace"</code>). Claude agents only.</td></tr><tr><td><code>claude_aliases</code></td><td>optional</td><td>Other commands that are claude (an array of strings like <code>["claude2"]</code>): a second account's shim, a wrapper, a renamed install. Their panes get every claude rule and <code>ctl spawn</code> accepts them. Adds to <code>ATRIUM_CLAUDE_ALIASES</code> for the session.</td></tr><tr><td><code>memory_mb</code></td><td>optional</td><td>A fixed ceiling, in MiB, on the memory everything the agents run may use (<code>0</code> = off). Default: a dynamic ceiling that tracks the machine's free memory. A hard kernel limit on Windows, and on Linux when launched under <code>systemd-run --user --scope -p Delegate=yes</code>; a soft guard otherwise.</td></tr></tbody></table>
<p><b><code>build_jobs</code></b> (optional) sizes the fleet's <b>shared compile pool</b>: the total number of compiler jobs all the agents' builds may run at once, however many agents start a build. <code>0</code> turns the pool off. Omit it and the pool gets one job per core, bounded by RAM (about 3 GiB per job), so the whole fleet compiles within the budget of one developer's <code>cargo build</code>. <code>ATRIUM_BUILD_JOBS</code> on the machine wins over the file. The banner's posture line states the budget ("16 compile jobs shared").</p>
<p>The pool is a jobserver: atrium sets <code>CARGO_MAKEFLAGS</code> in every pane, and cargo takes a token before each compiler job, so neither <code>-j</code> nor <code>CARGO_BUILD_JOBS</code> in an agent's command gets past it. Without it, seven agents running <code>cargo test --workspace</code> start seven machine-sized builds at once, which is how a fleet exhausted a 64 GB machine. Only cargo builds are pooled today; other compilers (<code>make</code>, <code>ninja</code>, <code>go</code>) are not. A build killed mid-compile loses its tokens; atrium refills the pool whenever no build is running.</p>
<p><b><code>memory_mb</code></b> (optional, Windows) caps the fleet's memory. Every pane runs inside atrium's session Job Object and atrium itself doesn't, so a job memory limit caps the agents without ever capping atrium. By default the ceiling is <b>dynamic</b>: what the panes use now plus the machine's free commit, minus a reserve (a tenth of the commit limit, at least 2 GiB), recomputed every 3 seconds. As other programs grow, the fleet's ceiling shrinks, so the fleet can use spare memory but can never be what exhausts the machine. <code>memory_mb</code> sets a fixed ceiling, which still never exceeds the dynamic one; <code>0</code> turns the guard off, and <code>ATRIUM_MEMORY_MB</code> wins over the file.</p>
<p>At 90% of the ceiling the guard stops the <b>largest build process</b> (cargo, a compiler, a linker or a build script, never an agent) and raises it on the bus and the bar. When the machine itself is already inside its reserve, the ceiling is pinned to what the panes use now, so builds in the session are stopped one per tick even if the memory hog is a program outside atrium: the session gives way first. The dynamic ceiling never drops below 1 GiB, so new panes can always start; a fixed <code>memory_mb</code> is honoured as given. Past the ceiling, allocations fail inside the panes instead of across the whole machine. The banner's posture line states the guard ("memory guard on").</p>
<p>On <b>Linux the cap can be hard.</b> Launch atrium in a cgroup it owns:</p>
<pre><code>systemd-run --user --scope -p Delegate=yes atrium fleet up build</code></pre>
<p>atrium then splits that scope into an <code>atrium/</code> leaf for itself and a <code>panes/</code> leaf for every pane, and sets <code>memory.high</code> (throttle at 90%) and <code>memory.max</code> (hard) on <code>panes/</code> from the same dynamic formula as Windows. Swap is closed to the panes while capped, because a cgroup's <code>memory.max</code> counts RAM only and a swapping build is exactly the freeze this prevents. Past the cap the kernel's cgroup OOM killer stops a process inside the panes — preferring builds, which carry a raised <code>oom_score_adj</code>. The banner says <code>memory guard on</code> without <code>(soft)</code> when this is in effect. It needs a systemd user manager with delegation (most desktop distributions and WSL with systemd enabled); a plain terminal doesn't give atrium a cgroup of its own, and then the guard is soft.</p>
<p>Without that — and always on <b>macOS</b> — the guard is <b>soft</b>, and the banner says <code>(soft)</code>. There is no Job Object, and a shell in a terminal doesn't own a cgroup it could cap, so atrium watches instead: it finds each pane's processes by session (every pane is a session leader), and when the machine's available memory drops into the reserve — or the panes reach a fixed <code>memory_mb</code> — it stops the largest build, binding the kill to the process's start time (a pidfd on Linux) so a reused pid is never hit. On Linux it also raises every build's <code>oom_score_adj</code>, so if the kernel's OOM killer fires it takes a build before atrium, the terminal or the desktop. Nothing stops an allocation between the 3-second ticks. macOS reads <code>kern.memorystatus_level</code> and is compiled but not yet run on a Mac.</p>
<p><b><code>deny</code></b> (optional) is a fail-safe list of commands agents may not run. Each entry is a claude permission rule (<code>"Bash(git push --force*)"</code>, <code>"WebFetch"</code>) or a bare command prefix (<code>"cargo test --workspace"</code> becomes <code>Bash(cargo test --workspace*)</code>). A fleet-level <code>deny</code> applies to <b>every</b> claude pane in the session, including workers a lead spawns later over ctl; an agent's own <code>deny</code> adds to its pane only; <code>ATRIUM_DENY</code> adds operator-wide rules. Every claude pane also carries two built-in rules refusing any command that names <code>CARGO_MAKEFLAGS</code>, so an agent can't strip the compile pool. The banner's posture line counts the session rules.</p>
<p>Keys a fleet leaves out can come from the user-global <code>config.json</code> (<code>fleet_defaults</code> for <code>trust</code>, <code>identity</code>, <code>allow_ctl</code>, <code>grid</code>; top-level <code>build_jobs</code>, <code>memory_mb</code>); the fleet's own value always wins, and the environment wins over both. See the README's <i>Config</i> section.</p>
<p><b><code>claude_aliases</code></b> (optional) names other commands that <i>are</i> claude. atrium decides whether a pane is claude by its command's stem, and everything claude-specific hangs on that answer: the trust posture flags, the deny list, the injected <code>--session-id</code> (status, transcripts, recovery), the ctl directive and worktree instructions folded into the system prompt, and <code>ctl spawn</code>'s agent allowlist. A second account is usually a shim — <code>claude2</code>, a <code>.cmd</code> or script that sets <code>CLAUDE_CONFIG_DIR</code> and runs <code>claude</code> — and without this key such a fleet launched with none of that, silently. Entries are commands or stems (<code>"claude2"</code>, <code>"claude2.cmd"</code>, a full path); <code>ATRIUM_CLAUDE_ALIASES</code> names them operator-wide. For a second account, name the alias in the user-global <code>config.json</code> with its <code>config_dir</code> instead: atrium then reads that account's transcripts and keeps its folder trust there, so its panes bind, show status and recover like any other.</p>
<p>The rules become claude's <code>--disallowedTools</code>, which was measured to hold even under <code>--dangerously-skip-permissions</code>, to win over a matching allow rule, and to be checked on each part of a compound command (<code>cd x && cargo test --workspace</code>). Two limits, stated plainly: rules match command <b>text</b>, so a deliberate wrapper (<code>bash -c "…"</code>) can still get past one, which makes this a guardrail that catches an agent rather than a sandbox; and codex has no equivalent flag, so the banner warns when a rule is aimed at a non-claude agent. The compile pool and the memory guard are the hard limits; <code>deny</code> is the tripwire in front of them.</p>
<p><b><code>identity</code></b> is resolved via <code>akey</code> and injected per pane exactly as <code>--identity</code> does. The pane shows the <code>·<name></code> tag (the <b>name</b> only, never a secret), and a resolve failure is flashed and the pane runs without it, never silently unauthenticated. See <a href="identity.html">credential identity</a>.</p>
<p><b><code>allow_ctl</code></b> matters because a fleet whose agents coordinate is dead without it, and silently so: the panes come up and publish into a bus that is not there. If the file is the complete definition of a spin-up, this belongs in it.</p>
<p><b><code>trust</code></b> takes one of <code>plan</code>, <code>accept</code>, <code>automode</code>, or <code>skip</code>. A fleet is launched to run hands-off, so it needs one; declaring it here keeps it with the roster it applies to and out of a flag you retype every launch. <code>--trust</code> on the command line wins when given. It is a <b>request, not an override</b>: if this atrium is itself running inside another atrium, the outer session's policy still caps it. See <a href="trust-and-security.html">trust and security</a>.</p>
<h3>Per-agent fields</h3>
<p>Each agent needs a <b><code>name</code></b> and a <b><code>cmd</code></b> (command plus args). Everything else is optional.</p>
<table><thead><tr><th>Field</th><th>Maps to</th><th>Meaning</th></tr></thead><tbody><tr><td><code>name</code></td><td>required</td><td>The agent's name in the roster.</td></tr><tr><td><code>cmd</code></td><td>required</td><td>Command and args, for example <code>["claude"]</code>.</td></tr><tr><td><code>identity</code></td><td><code>--identity</code></td><td>Overrides the fleet-level <code>identity</code> for this agent.</td></tr><tr><td><code>cwd</code></td><td>spawn directory</td><td>The agent is spawned here, so its <code>CLAUDE.md</code> auto-loads. Resolved relative to the fleet file's directory; absolute paths are used as-is.</td></tr><tr><td><code>add_dirs</code></td><td><code>claude --add-dir …</code></td><td>Extra trees the agent may read.</td></tr><tr><td><code>prompt</code></td><td><code>--append-system-prompt</code></td><td>Extra system-prompt text.</td></tr><tr><td><code>model</code></td><td><code>--model</code></td><td>The model to run.</td></tr><tr><td><code>effort</code></td><td><code>--effort</code></td><td>The reasoning effort.</td></tr><tr><td><code>can_spawn</code></td><td><code>ctl spawn</code> capability</td><td>May this agent create teammates? <b>Defaults to <code>false</code>.</b></td></tr><tr><td><code>trust</code></td><td><code>--trust</code> for this pane</td><td>Overrides the fleet-level <code>trust</code> for this one agent, capped at the fleet/session ceiling (a per-agent posture can de-escalate, never elevate). See <a href="trust-and-security.html">trust and security</a>.</td></tr><tr><td><code>worktree</code></td><td>worktree group</td><td>The worktree group this agent joins. Agents sharing a name co-develop one worktree and branch; distinct names are isolated. See <a href="worktrees.html">worktrees</a>.</td></tr><tr><td><code>kickoff</code></td><td>first prompt</td><td>An opening message submitted to the agent once it is up (distinct from <code>prompt</code>, which is appended to its system prompt) — the initiative that starts it working.</td></tr></tbody></table>
<p><b><code>can_spawn</code></b> is a capability the roster grants, not something implied by having ctl access. A depth cap bounds how far a fan-out goes, but never says who may start one, so the right to create teammates is declared here.</p>
<p>If a fleet turns the control plane on (<code>allow_ctl</code>) and coordinates by spawning teammates, set <code>"can_spawn": true</code> on its <b>lead</b> — a coordinator without it comes up unable to create the teammates it manages, and the denial only surfaces on its first <code>ctl spawn</code>. atrium warns at launch when a ctl-enabled fleet has no spawn-capable agent, but the fix is in the roster.</p>
<p>Each agent still gets its own <code>--session-id</code>, so the agent-aware chrome binds each pane independently.</p>
<h2>Designate a lead</h2>
<p>A fleet is not a row of agents each doing its own thing next to the others. That is how work gets dropped: no one owns the whole, no part gets reviewed, and a stuck worker just sits. The shape a fleet is <i>for</i> is <b>one lead that coordinates the team and drives it to completion</b>, a bench of workers, and usually an adversarial reviewer. Give one agent a lead persona and let it split the initiative, assign one part per teammate, watch progress, send each finished part to the reviewer, and mark a part done only once the reviewer clears it.</p>
<p>This works because of how <code>fleet up</code> wires the panes: every fleet agent comes up as an <b>operator</b> pane (it has no parent in the spawn tree), so any agent may drive any other over the control plane. The lead can <code>atrium ctl status <teammate></code>, <code>atrium ctl send <teammate> ...</code>, read the whole <code>atrium ctl board list</code>, and ask the reviewer to check any part. atrium hands every fleet agent the <i>capability</i>; the <code>prompt</code> and <code>kickoff</code> decide <b>who uses it as the lead</b>. So the lead role is not a field, it is a brief: cast one agent as the coordinator and the others as workers.</p>
<p>Two things have to be in place or the coordination silently does nothing:</p>
<ul><li><b><code>allow_ctl: true</code></b>, so the board and bus exist and every pane is told about <code>atrium ctl</code>. Without it the panes come up, the kickoffs tell them to publish to a bus that is not there, and nothing happens.</li><li><b>The coordination skills</b> installed, so a hosted claude reaches for <code>ctl</code> reliably instead of not knowing it exists. Install them once from the <a href="https://github.com/nativelite/marketplace">nativelite marketplace</a>: <code>/plugin marketplace add nativelite/marketplace</code> then <code>/plugin install atrium@nativelite</code>.</li></ul>
<p>A leaderless fleet with the skills missing is exactly the setup that comes up looking busy and finishes nothing. See <a href="control-plane.html">the control plane</a> for the <code>ctl</code> surface the lead drives, and a complete worked roster in <a href="https://github.com/nativelite/atrium/tree/main/examples"><code>examples/atrium.fleet.json</code></a>.</p>
<h2>Shared context: indexing and sharing knowledge across the fleet</h2>
<p>A fleet working together on a shared codebase or problem benefits from a <b>shared knowledge repository</b> — a searchable index of documentation, design decisions, relevant code, or conversation history that every agent can consult without re-reading or re-exploring the same ground.</p>
<p>The shared context model splits work into two roles:</p>
<ul><li><b>Indexing (one agent per fleet)</b>: A <b>lead or designated indexer</b> (usually the lead or a dedicated recon agent) is responsible for populating and maintaining the knowledge base. This agent reads widely, summarizes decisions, and feeds the index.</li><li><b>Consuming (all other agents)</b>: Every other agent in the fleet has read access to the shared context and pulls from it to inform their work, without the cost or latency of exploring from scratch.</li></ul>
<p>This avoids the loss of work and rediscovered decisions that plague leaderless fleets, and scales better than each agent re-exploring the same problem space.</p>
<h3>Configuring a shared context</h3>
<p>A fleet declares its shared context backend at the fleet level (not per-agent):</p>
<pre><code>{
"fleets": {
"review-crew": {
"allow_ctl": true,
"trust": "accept",
"context": {
"provider": "context-mode",
"share": "knowledge"
},
"agents": [
{
"name": "lead",
"cmd": ["claude"],
"can_spawn": true,
"prompt": "You index shared knowledge…"
},
{
"name": "worker",
"cmd": ["claude"],
"prompt": "You consult shared knowledge…"
}
]
}
}
}</code></pre>
<p><b>Field meanings:</b></p>
<ul><li><b><code>provider</code></b>: The backend that hosts the shared knowledge. <code>"context-mode"</code> is the current provider, backed by Claude Code's context-mode MCP server. Reserved for future providers (e.g., a native knowledge system). <b>This is provider-neutral by design</b>.</li><li><b><code>share</code></b>: The sharing mode for this fleet's context. One of: <code>"knowledge"</code> (shared knowledge/content index + private per-agent session memory, the default), <code>"full"</code> (shared knowledge AND shared session memory across the fleet), or <code>"none"</code> (no context provisioning). Fleet isolation is automatic: each fleet gets its own isolated context store under <code>.atrium/ctx/<fleet-name>/</code>.</li></ul>
<h3>The indexer role</h3>
<p>Designate one agent — usually the <b>lead</b> — as the <b>indexer</b>. This agent's job is to:</p>
<ol><li><b>Ingest and summarize</b>: Read design docs, specs, existing code, and ongoing conversation. Extract the key facts and decisions.</li><li><b>Keep the index current</b>: As new information arrives (a decision is made, a pattern emerges), feed it into the shared context.</li><li><b>Make it discoverable</b>: Organize the knowledge so other agents can search and find what they need.</li></ol>
<p>In the fleet file, the indexer is simply the agent who has the prompt and kickoff guiding this behavior:</p>
<pre><code>{
"name": "lead",
"cmd": ["claude"],
"prompt": "You maintain the shared knowledge base. Read widely, summarize decisions and designs, and keep the index fresh. Feed new insights to the shared context with `/ctx-index` or equivalent.",
"kickoff": "Start by ingesting the project spec and existing docs into the shared context. As we work, keep it current."
}</code></pre>
<p>Other agents run with prompts that direct them to <b>read</b> the shared context:</p>
<pre><code>{
"name": "implementer",
"cmd": ["claude"],
"prompt": "You read the shared knowledge base to understand the design before you code. Consult `/ctx` to search shared docs before asking questions."
}</code></pre>
<h3>What the shared context is NOT</h3>
<ul><li><b>Not a replacement for the control plane.</b> The control plane (<code>atrium ctl</code>) is for coordination and task tracking; shared context is for knowledge. A fleet uses both.</li><li><b>Not automatic.</b> An agent does not magically absorb knowledge; the indexer must deliberately feed it.</li><li><b>Not mandatory.</b> A small fleet or a synchronous team that talks often may not need it. Add shared context when the fleet grows or when knowledge is getting lost.</li><li><b>Not a sandbox.</b> Every agent consults the same knowledge base; there is no per-agent filtering. Use agent prompts to guide who contributes.</li></ul>
<h3>Implementation and versioning</h3>
<p>The <code>context</code> block is part of the frozen v1 contract, backed by the context-mode MCP server. The <code>provider</code> and <code>share</code> fields are stable. Each fleet automatically gets its own isolated context store under <code>.atrium/ctx/<fleet-name>/</code>, managed by the backend — no manual namespace configuration needed.</p>
<h2>The grant disclosure</h2>
<p><code>add_dirs</code> becomes <code>claude --add-dir</code>, that is, read access. A fleet file is often written by an agent and approved by a human <i>by running it</i>. So before <code>fleet up</code> blocks for your Enter, atrium resolves every <code>cwd</code> and <code>add_dirs</code> entry <b>through symlinks</b> and names the ones that are not plain subdirectories of the anchor.</p>
<p>For each such entry the disclosure names:</p>
<ul><li>where it really lands,</li><li>whether it exists yet,</li><li>whether atrium could not resolve it at all,</li><li>whether it is (or contains) a known credential store such as <code>~/.ssh</code>.</li></ul>
<p>The identity each agent comes up on is named too. Identical grants across agents are printed once, and the last line before the prompt names the destinations, so a tall roster cannot scroll the point away. The child is then spawned with exactly the resolved paths that were shown.</p>
<h3>The anchor</h3>
<p>The <b>anchor</b> is what "outside" is measured against. It is the repository the fleet file is checked into, else the file's own directory. For the user-global file the anchor is the directory you ran <code>atrium</code> in, since <code>~/.config/atrium</code> holds no project.</p>
<h3>Nothing here refuses</h3>
<p>A sibling checkout (<code>"../shared"</code> above) is a legitimate, documented use. A gate that blocks ordinary work is one people route around invisibly. The disclosure makes the reach <i>visible</i>, and the Enter is the approval.</p>
<h3>What the disclosure does not cover</h3>
<ul><li>A symlink <b>inside</b> a granted directory. <code>--add-dir d</code> grants <code>d</code>'s whole subtree, including links out of it.</li><li>Anything a <code>cmd</code> does for itself. <code>["sh","-c","claude --add-dir ~/.ssh"]</code> is a shell command atrium does not parse; an agent whose <code>cmd</code> is not an agent CLI is flagged as unreadable for exactly this reason.</li><li>The <code>prompt</code> or <code>kickoff</code> text.</li><li>A directory component of a resolved path re-pointed between the ack and the spawn (a TOCTOU repoint).</li></ul>
<p><b>atrium is not a sandbox.</b> At the same uid an agent can read anything atrium can. This mechanism bounds and discloses what atrium <i>hands over</i>; it does not confine what a running agent then does. See <a href="trust-and-security.html">trust and security</a>.</p>
<h2>Error handling</h2>
<p><b>Errors spawn nothing.</b> There is no partial fleet: if the launch cannot proceed, no pane comes up. The error cases are:</p>
<ul><li>no file (the message names both locations),</li><li>malformed JSON,</li><li>an unknown fleet name,</li><li>a fleet with zero agents,</li><li>a grid that does not fit the agent count,</li><li>a <code>cwd</code> that is not a directory.</li></ul>
<p>Each is a clear startup error. <b>Unknown fields are ignored</b>, so the format can grow without breaking older files.</p>
</main>
<footer><div class="wrap">
<div class="row">
<a href="index.html">home</a>
<a href="https://crates.io/crates/atrium">crates.io/atrium</a>
<a href="https://github.com/nativelite/atrium">github.com/nativelite/atrium</a>
<a href="https://github.com/nativelite/marketplace">marketplace</a>
<a href="https://github.com/nativelite">nativelite</a>
</div>
<div>Built on the nativelite stack. Zero third-party dependencies. MIT licensed.</div>
</div></footer>
</body>
</html>