返回 CodeWhale
FLEET.md
根目录 / docs / FLEET.md
1 # Agent fleet
2
3 > Baca terjemahan bahasa Indonesia: [id/FLEET.md](id/FLEET.md)
4
5 Agent fleet is the local-first roster and member-selection layer for durable
6 multi-worker runs. It does not execute or authorize work. After fleet resolves
7 who should participate, the delegated coordinator launches a headless
8 `codewhale exec` run and the Runtime tracks it durably. See
9 [AGENT_RUNTIME.md](AGENT_RUNTIME.md) for how sub-agents, `exec`, and
10 fleet-backed workers converge on one runtime. In product language, a user may
11 still "open a sub-agent"; in architecture language, durable nested work uses a
12 fleet member identity with delegated runtime execution.
13
14 ## Naming and compatibility boundary
15
16 **Fleet** is the public product noun. The durable ledger, saved rosters, config
17 tables, and `--fleet` flag share that name:
18
19 | Surface | Canonical |
20 | --- | --- |
21 | CLI | `codewhale fleet …` |
22 | Slash command | `/fleet …` |
23
24 These shared names are load-bearing wherever changing them would break
25 existing workspaces, receipts, or scripts:
26
27 - the durable ledger `.codewhale/fleet.jsonl` and the log directories
28 `.codewhale/fleet/` and `.codewhale/fleet-host/`;
29 - saved rosters `fleets/<name>.toml` and their `schema = "fleet"` header;
30 - the `[fleet]` config table (inline `[fleets.*]` tables were removed in 0.9.14; named fleets live in `fleets/<name>.toml` files);
31 - the `codewhale workflow run --fleet <name>` flag;
32 - wire, receipt, and control-plane operation ids such as `fleet.status`.
33
34 The rest of this document uses fleet as the public product noun and retains
35 these literal paths, keys, flags, and ids.
36
37 Use a fleet roster rather than anonymous short-lived `agent` fanout whenever a
38 delegated run needs stable member identities across retries, sleep/restart,
39 remote execution, receipts, or a ledgered audit trail. The initial CLI surface
40 is:
41
42 For a guided start-to-monitor walkthrough that combines fleet task specs with
43 Workflow authoring, see [fleet + Workflow Tutorial](FLEET_WORKFLOW_TUTORIAL.md).
44
45 ```sh
46 codewhale fleet init
47 codewhale fleet run tasks.json --max-workers 4
48 codewhale fleet status
49 codewhale fleet inspect <worker-id>
50 codewhale fleet logs <worker-id>
51 codewhale fleet artifacts <worker-id>
52 codewhale fleet interrupt <worker-id>
53 codewhale fleet restart <worker-id>
54 codewhale fleet resume <run-id>
55 codewhale fleet stop --all
56 ```
57
58 `codewhale fleet resume <run-id>` is the restart-recovery verb: it replays the
59 ledger, reconciles any in-flight lease whose worker stopped heartbeating
60 (retrying within the task's budget, else failing and escalating per the alert
61 policy), and prints the post-resume status. It launches no new work and is
62 idempotent, so it is safe to run after a manager exit, laptop sleep, or runtime
63 restart.
64
65 Coordinator state for fleet-backed runs is stored under the workspace in
66 `.codewhale/fleet.jsonl`. Worker logs and adapter logs are stored under
67 `.codewhale/fleet/` and `.codewhale/fleet-host/`.
68
69 ## Public contract: identity, membership, and selection
70
71 **Fleet** = the user's model inventory: who is in the roster and which member is selected.
72
73 A public fleet identity consists only of:
74
75 - a stable member id and an optional user-facing name;
76 - a semantic role, such as `explore`, `implement`, or `reviewer` (the legacy
77 spellings `worker`, `scout`, `builder`, `verifier`, `consultant`, and `oracle`
78 are still accepted on input and map to `general`, `explore`, `implement`,
79 `test`, and `advisor`);
80 - an exact provider/model identity, or an explicit inherited route;
81 - visible roster state or origin.
82
83 Project or workspace trust, filesystem and network reach, secret access,
84 approval mode, sandboxing, tool authorization, and every other form of runtime
85 authority are separate delegated-coordination and Runtime policy inputs. They
86 are never fleet identity fields, and they never select or reroute a fleet
87 member. The Runtime applies and clamps those policies only after member
88 selection; if the selected member cannot run inside the effective envelope,
89 launch fails closed instead of choosing somebody else.
90
91 Natural-language member selection is deterministic. A caller may name:
92
93 - an exact member id, optionally as `member:<id>` or `id:<id>`;
94 - a unique user-facing member name, optionally as `name:<name>`;
95 - a unique semantic role, for example `explore` or `role:explore`;
96 - an exact pinned model id, for example `deepseek-v4-flash`, or its offline
97 display name, for example `DeepSeek V4 Flash`; or
98 - an exact `route:<provider>/<model>`.
99
100 An unqualified exact member id wins. Every other match succeeds only when it
101 identifies one distinct roster member. Multiple matches produce an ambiguity
102 error that names the candidates and asks for `member:<id>`; Codewhale never
103 picks whichever match happened to be listed first. Users do not need to know
104 an internal role label such as `explore`: a unique member name, display model, or
105 exact model id is equally valid. Saved v2 fleets store that optional human name
106 as `display_name` (the input alias `name` is also accepted); it must be one
107 trimmed printable line of at most 80 characters.
108
109 ### Your fleet as models
110
111 The same fleet file answers a third question: **which models has this person
112 put in their fleet?** Every exact `provider` + `model` pin in the selected
113 fleet — the operator route, each pinned member, and each explicitly marked
114 shortlist row — is a fleet model. Executable member rows supply the roles it
115 fills; a shortlist row has no role. All remain in the same fleet file. Legacy
116 members that omit `role` still use their id as the role identity.
117 Shortlist rows carry only model choices; reasoning, instructions, and capability
118 requirements belong on executable role members and are refused on shortlist rows.
119
120 - `/fleet models` prints the fleet: `provider/model · roles · price · context ·
121 tools`, facts read from the model catalog. With no selected fleet the line
122 reads "Your fleet is the session model only".
123 - `/fleet add <provider> <model> [role…]` adds a model (one member row per
124 role, or one `shortlist = true` row for a role-less add). The provider must be one you configured
125 and, when the catalog knows the provider, must serve that exact id.
126 A role member asked to run the fleet's own operator route inherits it
127 instead of pinning — the role follows when the operator moves; a pin on
128 any other route is the deliberate opt-out. Files that already pin the
129 operator route are read as inheritance.
130 With no fleet selected, a user-global fleet named `My fleet` is created and
131 selected first. `/fleet remove <provider> <model>` drops every row that pins
132 the route; the operator route is changed with `/fleet save`, not removed.
133 - In `/model`, `⇧F` adds or removes an explicit shortlist row for that exact
134 route. It preserves saved role pins and the operator route;
135 fleet models are listed first, labelled `fleet · <roles>`, ahead of your
136 own `⇧P` pins and the provider lists. `/models` prints the fleet before the
137 provider's list.
138
139 The operator model reads this list when it assigns sub-agents (design
140 `MODEL-ROUTING-CATALOG-20260901.md` §10, slice F2).
141 The model-facing roster resolves roles through the same route admission code
142 as a start. Explicit profiles and manual role pins remain authoritative; a
143 unique saved role pin also applies to a start naming only its role. Task model
144 choices are available for unpinned roles, constrained to the selected models
145 plus the session route. Shortlist model rows disclose their own exact route
146 independently of any role pin.
147
148 ### Interactive and persistent status
149
150 `/fleet status` and `codewhale fleet status` are the **same** command on two
151 surfaces. Both read the durable `.codewhale/fleet.jsonl` ledger for the
152 workspace, through one shared control-plane contract, and both report the same
153 verb id (`fleet.status`), read-vs-write authority, persistence scope, and
154 receipt. When the workspace has no ledger they say so with a typed reason
155 (`no_fleet_ledger`) instead of rendering an empty-looking "all clear" — and
156 neither creates the ledger as a side effect of reading it.
157
158 The current interactive session's sub-agents are a **different set**, and now
159 have their own name:
160
161 - `/fleet workers` (or `/subagents`, or `n`) shows sub-agents attached to the
162 current TUI session. It does not read the persistent ledger.
163 - `/fleet list|status|interrupt|resume` and `codewhale fleet
164 list|status|interrupt|resume` act on the durable ledger.
165 - `codewhale fleet restart <worker-id>` is CLI-only: it re-leases the task and
166 then drives the manager loop to completion. `/fleet restart` does not
167 silently do a smaller thing — it reports `surface_not_supported` and names
168 the CLI command.
169
170 Before v0.9.2, `/fleet status` showed session sub-agents. That reading is gone;
171 `/fleet workers` replaces it.
172
173 The contract behind this — descriptors, availability reasons, exact-identity
174 targets, receipts, typed unknowns, and bounds — is documented in
175 [`docs/COMMAND_CONTROL_PLANE.md`](COMMAND_CONTROL_PLANE.md).
176
177 ## Authoring agent profiles (`/fleet setup`)
178
179 `/fleet setup` (also `/fleet setup edit` / `new`) opens an in-TUI wizard for
180 authoring a reusable agent-team profile. Bare `/fleet` and the
181 `roster`/`roles`/`profiles`/`party` aliases open the selected fleet's member
182 roster. `/fleet saved` opens the named saved-fleet picker. `/fleet workers` opens the
183 current-session worker view; `/subagents` is a
184 compatibility shortcut for that view. For durable run history, use
185 `/fleet status` or the shell command `codewhale fleet status` described above —
186 they are the same command.
187
188 The wizard is progressive: you make one focused choice at a time — a **role**,
189 then a **model** (`inherit`, or a concrete model from *any configured
190 provider*, not only the one the parent session is currently using), then
191 **where the profile lives**, and finally a **review** of the member identity
192 and route. When the review also previews thinking, tools, approvals, or another
193 execution control, those rows summarize separate Runtime policy; they do not
194 become fleet identity or member selectors. The header shows "Saves to: …" on
195 every step — the choice you still have to make, or the exact resolved file
196 once you have made it. Nothing is written until you activate the save control
197 on the review step.
198
199 The **Destination** step is a focused two-option list:
200
201 - **This project** writes `<workspace>/.codewhale/agents/<role>.toml`. It
202 applies to this project only and takes precedence over a Personal profile
203 with the same id. When project profiles are disabled for the session
204 (`--no-project-config`) or the workspace folder is unavailable, the option is
205 shown disabled with that reason; the wizard never falls back to Personal on
206 its own.
207 - **Personal** writes `$CODEWHALE_HOME/agents/<role>.toml` and is available in
208 every project on this machine, except where a project has its own profile
209 with the same id.
210
211 For the highlighted option the step shows the exact file, whether saving would
212 create a new file or **replace an existing one**, and the precedence
213 consequence for the roster. The review step repeats those facts under
214 "Saves to" and names the final action by its effect — **Save to this
215 project**, **Save as Personal profile**, or **Replace …**. Replacing an
216 existing file asks for a second confirmation on the save control. Reopening a
217 saved member from `/fleet` starts from what is on disk: its member identity,
218 route, and save scope. Thinking (`inherit`, `off`, `low`, `medium`, `high`,
219 `max`, or `auto`) is adjusted on the review step, but remains a route
220 execution setting rather than part of the member's fleet identity.
221
222 Profile scope controls where a role definition is reusable; it does not widen
223 the authority of a running operation and is not a project-trust setting. To
224 coordinate several nearby repositories, start Codewhale from their shared
225 parent directory so that parent is the workspace. Project/workspace trust,
226 external paths, filesystem and network reach, secrets, approvals, sandboxing,
227 and tool authorization come from delegated-coordination and Runtime policy.
228 For nested delegation, Runtime intersects the requested child posture with the
229 live parent. For standalone `codewhale fleet` execution, Runtime instead uses
230 the bounded tool-authority envelope minted from the task's explicit write
231 scope together with live config, sandbox, and platform enforcement. Neither
232 path reads authority from the profile's storage scope or identity selector.
233
234 Picking a concrete model pins its provider explicitly: the saved profile records both
235 `model` and `provider` fields, so the route it names doesn't depend on
236 whichever provider happens to be active when the profile is later loaded.
237 Pressing **Enter** ("start") on the review step previews the exact starter
238 profile TOML inline on that same screen; nothing is written until you save it.
239 The `provider` field may be a built-in provider id such as `openrouter` or a
240 user-named OpenAI-compatible provider configured under `[providers.<name>]`
241 such as `lm-studio`; the launch path preserves that id and fails closed if the
242 provider is not configured.
243
244 Profiles are also how the model-facing `agent` tool selects a route since the
245 v0.9.9 schema slim (#5324, #5123): the advertised surface no longer carries
246 `model` or `thinking` — a child either runs as a `profile` (whose saved route
247 and thinking tier it uses exactly) or inherits the operator's model. Removed
248 fields stay parse-accepted for saved transcripts, ACP/MCP clients and fleet
249 configs; see docs/SUBAGENTS.md for the advertised 12-field list and the
250 compat list.
251
252 When a provider is configured, the review step also offers model-assisted
253 drafting behind an explicit preview-before-save gate:
254
255 - Press **`m`** to have your first configured model draft the profile. The
256 draft arrives sanitized and bounded. Separately, the Runtime keeps its
257 conservative execution floor (no shell or trust escalation and approval
258 required) regardless of what the model proposes.
259 - **Drafting is not saving.** The exact rendered TOML preview renders
260 inline on the review step (not in a separate scrollable viewer), so nothing
261 is saved until you press **`g`** or **Enter** to save (or press `m` again
262 to redraft). Saving writes the profile to the project or personal scope
263 shown in the preview.
264
265 ## Naming: Modes, Workflow, and fleet
266
267 These names describe different layers, not competing systems. Plan and Act are
268 the everyday work modes. Operate accepts ordinary messages and keeps the
269 parent's normal tool surface under the same approval, sandbox, shell, ask-rule,
270 and repository protections as Act. It prefers background fleet workers for
271 independent, parallel, isolated, or long-running work, but does not require a
272 worker for every executable step. Workflow is an optional orchestration overlay
273 for work that needs ordering, gates, shared budgets, replay, or deterministic
274 fan-in.
275
276 The short public vocabulary is:
277
278 - **Fleet** is the durable roster and deterministic member-selection surface.
279 It records member ids and names, semantic roles, provider/model identities,
280 and roster state. Fleet is also the name used by storage and wire formats.
281 - **Workflow** = what order the work follows: phases, gates, budgets, replay,
282 and fan-in.
283 - **Lane** = one running Workflow instance and its live progress.
284 - **Runtime** = where, how, and with what authority selected work executes.
285 Runtime owns the local or remote process, provider route, project/workspace
286 trust, filesystem, network, secrets, approvals, sandbox, tools, and API
287 boundary.
288
289 - **Workflow** is the repeatable plan and user-facing orchestration
290 overlay: a script/IR that decides which phases and agents run next, keeps
291 intermediate results out of the main conversation, and can be inspected or
292 rerun. A Workflow run should have a visible progress view and a clear active
293 header state instead of feeling like a hidden background task.
294 - **Fleet** is the durable roster and deterministic member-selection surface:
295 member ids and names, semantic roles, pinned or inherited provider/model
296 identities, and roster state. The delegated
297 coordinator and Runtime own launch concurrency, leases, heartbeats, logs,
298 receipts, tools, sandboxing, approvals, and authority.
299 - **High fan-out** is a behavior of a Workflow run, not a separate system:
300 when a phase needs many workers at once, Workflow dispatches them as a
301 fleet-backed run (durable workers, receipts, goal re-dispatch) rather than
302 reviving prompt-only sub-agent fanout.
303 - **Fan-in is explicit:** when the user needs one combined result, an owner
304 aggregates, verifies, and synthesizes the worker receipts. Independent tasks
305 may finish separately; dispatch is never presented as completion.
306
307 UI guidance: keep the main transcript calm. A Workflow run should appear as a
308 compact progress card plus workbar rows (the strip under the composer, or
309 a side workbar) with phase names, worker counts, receipts, and nested
310 indentation for child workers. Use the whale mark sparingly as an active
311 header/status signal; avoid repeating emoji-heavy rows for every worker.
312
313 ## Saved fleets and the Reasoning Router
314
315 A selected v2 fleet freezes each selected member's id, semantic role, provider,
316 and model identity into the durable run before a Workflow starts. Save the
317 fleet as `fleets/<name>.toml` in the workspace or under `$CODEWHALE_HOME`.
318 Models cannot replace those identity or route assignments at runtime:
319
320 ```toml
321 schema = "fleet"
322 schema_revision = 2
323 name = "release"
324
325 [operator]
326 provider = "deepseek"
327 model = "deepseek-v4-pro"
328
329 [[members]]
330 id = "implementer"
331 display_name = "Release Builder"
332 role = "implement"
333 provider = "zai"
334 model = "glm-5.2"
335
336 [[members]]
337 id = "advice"
338 role = "advisor"
339 provider = "openai"
340 model = "gpt-5.6"
341 ```
342
343 The workflow crate's older `schema = "exact"`, revision 1 files are migration
344 input only. Do not author them for v0.9.11; the selected roster and setup UI
345 read and write only `schema = "fleet"`, revision 2.
346
347 Reasoning is a separate route-execution decision, not fleet identity. The
348 optional Reasoning Router is a reusable Runtime service, not a fleet member.
349 Save one profile at `routers/<name>.toml` in either search root and reference it
350 from any number of fleets:
351
352 ```toml
353 name = "luna-low"
354 schema = "reasoning_router"
355 schema_revision = 1
356 provider = "openai"
357 model = "gpt-5.6-luna"
358 call_reasoning = "low"
359 ```
360
361 At runtime it may choose only the reasoning tier for an already-frozen worker
362 route. It cannot change the member, provider, model, or semantic role. The
363 Router call itself is capped at `off` or `low`; more expensive values are
364 rejected. A manually selected worker reasoning tier makes no Router call. Route
365 and reasoning receipts name the worker model and, when used, the Router's exact
366 provider/model so the operator can see which model did which job. If the same
367 bare Router or fleet name exists in both roots, qualify it as
368 `workspace/<name>` or `codewhale_home/<name>` instead of relying on shadowing.
369
370 Compatibility schemas may serialize `reasoning`, `permissions`, tool hints, or
371 other execution settings beside a member. Those values are not fleet identity,
372 member selectors, or active authority. A valid legacy `schema = "exact"` roster snapshot
373 retains its old `permissions` bytes only while verifying and replaying that
374 snapshot's recorded content hash; a fresh capture emits the authority-free
375 member shape. New-run validation rejects legacy roster
376 `security_policy` and worker `trust_level` fields; configure execution authority
377 through Runtime policy. The delegated coordinator resolves and durably freezes
378 the member first. Runtime then applies either the delegating parent's effective
379 ceiling or, for standalone fleet CLI work, Runtime execution configuration plus
380 live sandbox/platform enforcement. That boundary may
381 reduce or refuse the selected worker's execution surface, but it must never
382 choose a different member or route. See
383 [`docs/MODES.md`](MODES.md), [`docs/SUBAGENTS.md`](SUBAGENTS.md), and
384 [`docs/AGENT_RUNTIME.md`](AGENT_RUNTIME.md) for the enforcement contract.
385
386 Reasoning receipts record the requested tier *and* the tier the provider was
387 actually asked for. Those differ whenever a route cannot express the requested
388 one — Codewhale's route normalizer sends `high` for a requested `low` on most
389 routes, and Z.AI's GLM routes express only thinking on/off — so the receipt
390 reports the real request rather than the label that was selected. The value a
391 call actually carries is spelled by that route's own normalizer, not by the tier
392 label: an OpenAI Codex route is asked for `xhigh`, not `max`, and cannot be
393 asked for `off` at all.
394
395 A v0.9.11 durable fleet CLI receipt keeps the selected profile id in
396 `effective_permissions.profile_id`, the resolved semantic role in
397 `resolved_route.role`, and the effective Runtime surface in the permission,
398 shell, and tool-scope fields. An exact Workflow launch receipt records
399 `member_role` separately from an optional Runtime `posture_role`, plus the
400 fingerprint of the effective authority envelope checked at the spawn boundary.
401 A member named `auditor` can therefore retain that identity while Runtime
402 reports a `custom` posture and independently proves the narrower surface it
403 enforced.
404
405 A Workflow start fails closed on anything decidable locally: an unresolvable
406 provider or model, a missing credential, a client that cannot be built for a
407 member's route, or an `auto` member with no usable Reasoning Router. Per-task
408 validation that the spawn boundary would refuse anyway — notably a write-capable
409 member with no declared `write_roots`/`exact_files`/`coordination_contracts` —
410 is checked before the Router is called, so an invalid task never spends a
411 routing request. If a spawn fails *after* a Router decision, the receipt is
412 still recorded: the tokens were spent, and any cross-provider disclosure already
413 happened.
414
415 ## Manager-owned Workflow fan-in
416
417 When parallel work must return one combined answer, prefer a manager-owned
418 Workflow over a flat `agent` fan-out. Default shape:
419
420 1. **Cast one manager** (operator or workflow orchestrator).
421 2. **Fan out** child tasks through `workflow` (`task()`, `parallel()`,
422 `pipeline()`, `phase()`) or a single manager session that owns the children.
423 3. **Wait** for child receipts or completion events.
424 4. **Aggregate and verify** load-bearing claims before treating them as facts.
425 5. **Synthesize** one result the operator can depend on.
426
427 Raw `agent` fan-out fits independent work with no combined result. When
428 results must be merged, compared, or verified, route through `workflow` so
429 the manager owns fan-in — that is what the shape above is for, not a ban
430 on simpler patterns when nothing needs combining.
431
432 ## Workflow on fleet
433
434 The intended high-capability path is agent-authored. When the main agent
435 decides a task needs more durable coordination than turn-by-turn sub-agent
436 calls, it drafts a Workflow script/IR, presents the run plan according to the
437 active permission mode, and the runtime compiles it into typed fleet work.
438
439 fleet remains the sub-agent roster and member-selection surface. It owns member
440 identity, membership, semantic roles, saved provider/model pins or inheritance,
441 and roster state. Workflow owns the orchestration plan:
442 branch, sequence, loop, expand, review, and reduce decisions. The delegated
443 coordinator and Runtime own slot admission, launch concurrency, the execution
444 ledger, and every authority decision. A workflow script receives no direct
445 shell, filesystem, network, provider-secret, cancellation, or TUI authority;
446 workers perform real work as `codewhale exec` processes under the effective
447 Runtime policy.
448
449 Default Workflow-to-fleet validation is intentionally bounded:
450
451 - 1,000 total worker agents per Workflow run;
452 - 16 live worker agents at once; larger populations queue (block) on the host's
453 per-run concurrency gate until a live slot frees, then route through fleet;
454 - Workflow IR structural nesting no deeper than 5;
455 - Runtime child delegation defaults to 3 levels and has an opt-in hard ceiling
456 of 8, independently of the Workflow document's structural depth;
457 - bounded loops only (`max_iterations` required);
458 - bounded dynamic expansion only (`max_children` plus a template required).
459
460 These are delegated-coordination population limits, not fleet identity and not
461 a demand to launch everything at once. A 1,000-agent Workflow should still
462 drain through the configured Runtime worker pool. They are also not model-step
463 budgets: omitted or zero `max_steps` remains unbounded. An explicit positive
464 `max_steps` may cap that task, while wall-clock timeouts, cancellation,
465 provider safeguards, heartbeats, and admission controls remain independent.
466
467 Recommended model layouts, such as a DeepSeek Pro orchestrator with Flash
468 workers in the first ring and cheaper workers farther out, are presets only.
469 Every slot can inherit the active model or carry an explicit model override.
470 Inheritance is literal: the model you select in `/model` is the **operator**
471 (the pinned first row in `/fleet roster`), and any worker whose task spec and
472 roster profile pin no model runs on that session model. Once a selected member
473 has an exact provider/model pin, the Runtime does not silently reroute that
474 identity because a policy input differs; it either runs that route inside the
475 effective envelope or fails closed. Route receipts record the requested and
476 resolved identity.
477
478 The setup UI should render this as an expanding grid: an orchestrator plus a
479 small number of visible sub-agent slots, with Right/Enter drilling into a slot's
480 next recursive ring rather than trying to show the whole tree at once.
481
482 ## Task Spec
483
484 `codewhale fleet run` accepts JSON or TOML. A minimal JSON spec:
485
486 ```json
487 {
488 "name": "local smoke",
489 "tasks": [
490 {
491 "id": "lint",
492 "name": "Lint",
493 "instructions": "Run the lint check and report failures.",
494 "expected_artifacts": ["log"]
495 }
496 ]
497 }
498 ```
499
500 Workers are optional. If omitted, Codewhale creates local worker slots up to
501 `--max-workers`.
502
503 Task specs are typed in Rust and keep verification data separate from worker
504 transcripts. Only the `worker` member/role reference participates in fleet
505 identity selection. The remaining execution fields are delegated-coordination
506 or Runtime inputs applied after the member is resolved. A task can declare:
507
508 - `id`, `name`, `description`, `objective`, and `instructions`
509 - `worker` role, tool profile, tools, and required capabilities
510 - `workspace` root, required files, writable paths, and environment allowlist
511 - `input_files`, extra `context`, `budget`, `timeout_seconds`, and `retry_policy`
512 - `expected_artifacts`, `scorer`, `tags`, and free-form `metadata`
513
514 None of those execution-policy fields becomes part of a fleet identity or an
515 alternate member selector. Omitted or zero `max_steps` means no model-step
516 ceiling; Codewhale must not synthesize a default step budget. Explicit positive
517 step limits, timeouts, cancellation, provider safeguards, heartbeats, and
518 admission control are enforced independently by the delegated coordinator and
519 Runtime.
520
521 Workers write bounded artifact files under `.codewhale/fleet/` and ledger only
522 the artifact refs: kind, path, checksum, MIME type, and size. Receipts record
523 `pass`, `fail`, `partial`, `skip`, or `timeout`; failed receipts may also mark
524 the source as `transport`, `task`, or `verifier`. `codewhale fleet status`
525 surfaces those failure-source counts separately.
526
527 Deterministic built-in scorers are `exit_code`, `file_exists`, `regex_match`,
528 and `json_path`. Specs may also declare `command`,
529 `code_whale_verifier_prompt`, or `manual`; those record a partial receipt until
530 an explicit verifier pass completes.
531
532 ### Using Role Presets
533
534 Tasks can reference a semantic role name to select one unique roster member.
535 Built-in role names (`smoke-runner`, `reviewer`, `builder`, `read-only`) remain
536 available for compatibility, and custom roles may be defined in
537 `[fleet.roles]`.
538
539 ```json
540 {
541 "name": "smoke check",
542 "tasks": [
543 {
544 "id": "lint",
545 "name": "Lint check",
546 "instructions": "Run lint and report failures.",
547 "worker": { "role": "smoke-runner" },
548 "expected_artifacts": ["log"]
549 }
550 ]
551 }
552 ```
553
554 After identity resolution, compatibility role presets may provide tool,
555 timeout, or retry defaults to the delegated coordinator. Those defaults do not
556 grant authority, do not change which member was selected, and remain subject to
557 Runtime clamping. A task spec may request its execution settings explicitly:
558
559 ```json
560 {
561 "id": "deep-review",
562 "name": "Deep review",
563 "instructions": "Review the entire crate for soundness issues.",
564 "worker": {
565 "role": "reviewer",
566 "tools": ["cargo", "rg", "git"],
567 "capabilities": ["rust"]
568 },
569 "input_files": ["crates/**/*.rs"],
570 "budget": { "max_tokens": 32000 },
571 "expected_artifacts": ["log", "report"],
572 "scorer": { "kind": "regex_match", "path": ".codewhale/fleet/report.md", "pattern": "finding|all clear" }
573 }
574 ```
575
576 ### Multi-Task Run Example
577
578 A single fleet run can dispatch several independent tasks in parallel:
579
580 ```json
581 {
582 "name": "CI gate",
583 "tasks": [
584 {
585 "id": "check",
586 "name": "Compile check",
587 "instructions": "Run cargo check --workspace and report errors.",
588 "worker": { "role": "builder" },
589 "expected_artifacts": ["log"],
590 "scorer": { "kind": "exit_code" }
591 },
592 {
593 "id": "clippy",
594 "name": "Clippy lint",
595 "instructions": "Run cargo clippy --workspace and report warnings.",
596 "worker": { "role": "reviewer", "tools": ["cargo", "cargo-clippy"] },
597 "expected_artifacts": ["log"],
598 "scorer": { "kind": "exit_code" }
599 },
600 {
601 "id": "security",
602 "name": "Secret audit",
603 "instructions": "Search for plaintext secrets and report any matches.",
604 "worker": { "role": "read-only", "tools": ["rg"] },
605 "input_files": ["crates/**/*.rs"],
606 "expected_artifacts": ["log", "report"],
607 "retry_policy": { "max_attempts": 1 }
608 }
609 ]
610 }
611 ```
612
613 ## Alerts
614
615 fleet alerting is disabled by default. A caller must supply an enabled alert
616 config before anything is sent. Routes match typed fleet event classes, not log
617 strings:
618
619 - `stale`
620 - `restart_exhausted`
621 - `needs_human`
622 - `budget_exceeded`
623 - `verifier_failed`
624 - `run_completed`
625
626 Adapter config stores environment variable names, not secret values. Send-time
627 code resolves those names from the environment or a future secrets provider.
628 Ledger records store only audit labels such as `slack`, `webhook`, or
629 `pagerduty`; task specs persisted in the ledger redact webhook URLs and routing
630 keys.
631
632 Example alert config shape:
633
634 ```json
635 {
636 "enabled": true,
637 "dry_run": true,
638 "routes": [
639 {
640 "events": ["stale", "restart_exhausted", "verifier_failed"],
641 "adapter": "ops-slack"
642 },
643 {
644 "events": ["restart_exhausted"],
645 "adapter": "pager"
646 }
647 ],
648 "adapters": {
649 "ops-slack": {
650 "kind": "slack",
651 "webhook_env": "CODEWHALE_FLEET_SLACK_WEBHOOK",
652 "channel": "#codewhale-fleet"
653 },
654 "pager": {
655 "kind": "pager_duty",
656 "routing_key_env": "CODEWHALE_FLEET_PAGERDUTY_ROUTING_KEY",
657 "severity": "critical"
658 }
659 }
660 }
661 ```
662
663 Use dry-run to inspect a redacted adapter payload without sending:
664
665 ```sh
666 codewhale fleet alert-dry-run \
667 --event stale \
668 --run-id fleet-demo \
669 --worker-id fleet-demo-local-1 \
670 --task-id release-triage \
671 --reason "worker heartbeat stale since 2026-06-13T02:00:00Z" \
672 --adapter slack
673 ```
674
675 The payload includes the run id, worker id, task id, status, short reason, and
676 safe inspection commands such as `codewhale fleet status` and
677 `codewhale fleet inspect <worker-id>`. Endpoints, webhook secrets, and
678 PagerDuty routing keys are shown as `<redacted:env:...>`.
679
680 ## Status Surfaces
681
682 `codewhale fleet status` shows compact counts for queued, running, completed,
683 partial, failed, restarted, escalated, cancelled, stale, and verifier/transport
684 failure sources. `inspect` shows the worker state plus the current task
685 objective, role, host, heartbeat, latest event, artifact refs, latest error, and
686 alert state. `logs` prints bounded log artifact contents, and `artifacts` lists
687 artifact refs without embedding large payloads.
688
689 The Runtime API exposes the same ledger-backed projection behind the existing
690 runtime auth middleware:
691
692 ```text
693 GET /v1/fleet/runs
694 GET /v1/fleet/runs/{run_id}
695 GET /v1/fleet/runs/{run_id}/workers
696 GET /v1/fleet/workers/{worker_id}
697 POST /v1/fleet/workers/{worker_id}/interrupt
698 POST /v1/fleet/workers/{worker_id}/restart
699 POST /v1/fleet/runs/{run_id}/stop
700 ```
701
702 Action endpoints call the same manager controls as the CLI and record their
703 decisions in the fleet ledger.
704
705 ## Manager-Agent Runbook
706
707 Manager agents should treat fleet operations as typed, ledgered control-plane
708 work. Start with `codewhale fleet status`, then inspect one run or worker with
709 `codewhale fleet inspect <worker-id>`, `logs`, and `artifacts`. Use direct
710 reads of `.codewhale/fleet.jsonl`, host logs, or remote files only when the
711 typed CLI/API surface cannot provide the required evidence.
712
713 Classify the worker before taking action:
714
715 - `transient failure`: stale heartbeat, host timeout, interrupted transport,
716 retryable provider/network error, or an adapter status that can plausibly
717 recover without changing the task.
718 - `task failure`: the worker completed but produced an incorrect result,
719 domain failure, missing required artifact, or explicit task-level error.
720 - `verifier failure`: the worker result exists, but the scorer/verifier failed,
721 timed out, or disagrees with the receipt.
722 - `needs-human`: missing authority, secret request, destructive operation,
723 repeated restart exhaustion, ambiguous product decision, or conflicting
724 evidence that the manager cannot resolve from typed artifacts.
725
726 Choose one typed action:
727
728 - Restart a worker only when the failure is transient, retry budget remains,
729 the task is idempotent or retry-safe, and no permission or secret boundary is
730 involved: `codewhale fleet restart <worker-id>`.
731 - Interrupt or stop only when the current task is unsafe to continue or the
732 operator explicitly asks for cancellation: `codewhale fleet interrupt
733 <worker-id>` or `codewhale fleet stop --all`.
734 - Do not restart pure task failures by default; preserve artifacts and hand the
735 receipt to the task owner unless the task spec says retrying can produce new
736 evidence.
737 - For verifier failures, inspect scorer inputs and artifact refs first. If the
738 verifier cannot be corrected through typed fleet actions, escalate for human
739 review.
740 - For `needs-human`, draft an escalation instead of sending it unless alert
741 config explicitly authorizes sending.
742
743 Safe Slack or PagerDuty draft:
744
745 ```text
746 Codewhale fleet needs attention
747 Run: <run-id>
748 Worker: <worker-id>
749 Task: <task-id or unknown>
750 Classification: <transient failure | task failure | verifier failure | needs-human>
751 Reason: <one sentence, no secrets>
752 Latest typed evidence: codewhale fleet inspect <worker-id>; codewhale fleet artifacts <worker-id>
753 Safe log excerpt: <3 lines max or "see artifact <ref>">
754 Requested decision: <restart approval | verifier review | task owner review | permission decision>
755 ```
756
757 Post-run summaries should include the run id, workers checked, classification,
758 typed action taken or drafted, expected ledger effect, artifact refs reviewed,
759 and next owner. Keep summaries bounded; link artifact refs instead of copying
760 full logs or transcripts.
761
762 The bundled `fleet-manager` skill mirrors this runbook for manager agents. It
763 is a first-party system skill and should be discoverable through the normal
764 skill registry after system skills are installed or refreshed.
765
766 ## Host Adapters
767
768 The Runtime host-adapter boundary supports local child processes and explicit
769 SSH workers. Host choice is Runtime placement on a worker spec, not fleet member
770 identity or a member selector. It does not authenticate the host or grant
771 access. Adapters expose the same operations: start, read status, read bounded
772 logs, interrupt, restart, stop, and cleanup.
773
774 Local workers run as child processes with stdin closed and stdout/stderr written
775 to bounded host-adapter logs. They inherit only a small safe base environment
776 such as `PATH` and explicitly allowlisted variables.
777
778 SSH workers run through the system `ssh` client with `BatchMode=yes` and a
779 bounded connect timeout. Remote environment variables are sent with OpenSSH
780 `SendEnv`; values are not embedded in the local ssh argv or fleet logs.
781
782 Example SSH worker spec:
783
784 ```json
785 {
786 "id": "builder-1",
787 "name": "Builder 1",
788 "host": {
789 "kind": "ssh",
790 "host": "builder.example.com",
791 "user": "codewhale",
792 "port": 22,
793 "identity": "~/.ssh/codewhale_fleet",
794 "working_directory": "/srv/codewhale/work",
795 "env_allowlist": ["CODEWHALE_PROFILE"],
796 "codewhale_binary": "/usr/local/bin/codewhale"
797 },
798 "capabilities": ["local", "linux", "tests"],
799 "max_concurrent_tasks": 1
800 }
801 ```
802
803 Defaults are intentionally conservative:
804
805 - no hosted control plane or cloud provisioning is enabled;
806 - SSH requires an explicit host, working directory, and Codewhale binary path;
807 - secret-like environment names such as `TOKEN`, `SECRET`, `PASSWORD`,
808 `API_KEY`, and `PRIVATE_KEY` are rejected from adapter allowlists;
809 - secrets should remain in Codewhale config providers or remote host config,
810 not in task instructions, argv, or fleet logs.
811
812 ## Runtime policy and authority are not fleet identity
813
814 fleet does not define a project/workspace trust level, filesystem or network
815 reach, secret access, approval mode, sandbox, tool set, or execution authority.
816 Those belong to delegated-coordination and Runtime policy. This separation is
817 load-bearing:
818
819 - member resolution considers only member id/name, semantic role,
820 provider/model identity, and roster state;
821 - the selected identity is frozen before any authority policy is evaluated;
822 - the Runtime applies the live parent ceiling when one exists; standalone
823 fleet CLI launches instead carry an explicit bounded authority envelope,
824 and both paths remain subject to live sandbox and platform enforcement;
825 - no trust, permission, capability, secret, sandbox, approval, or tool-policy
826 value may select another member or silently change its provider/model route;
827 and
828 - receipts report requested and effective Runtime posture separately from the
829 fleet member identity.
830
831 Older persisted configuration and protocol shapes may still contain fields such as
832 `security_policy`, `trust_level`, `permissions`, `capability_grants`, secret
833 references, host authentication, environment allowlists, or tool profiles.
834 They remain deserializable for ledger replay, but new fleet run creation rejects
835 `security_policy` and worker `trust_level` rather than pretending they grant
836 authority. Their presence in old data does not make them fleet variables or
837 grants. The active Runtime remains the final authority and fails closed when a
838 requested operation cannot be enforced.
839
840 For current enforcement behavior, use [Modes](MODES.md),
841 [Sub-agents](SUBAGENTS.md), [Agent Runtime](AGENT_RUNTIME.md), and the
842 [Command Control Plane](COMMAND_CONTROL_PLANE.md). Keep secret values out of
843 task instructions, arguments, logs, and receipts; adapter and Runtime layers
844 must continue to redact or reject them independently of fleet selection.
845
845 lines MARKDOWN