Decision recorded: keep both Fleet and agent profiles — the confusion is naming and duplicated fields, not the model
- Dominant language
- Rust
- Stars
- 41k
- Forks
- 3.6k
- Avg merge
- 13h 59m
- Merged PRs (30d)
- 299
Description
## The question
Founder, 2026-09-10: *"i'm also confused by us having both 'fleets' and 'agents' and labels for it — i think that's the huge issue here"*, then: *"we should probably just rip out fleets altogether and rebuild it just using agents?"*, then, after seeing what Fleet does: *"but then we still use the term /fleet - i like it - and there is stuff that it lets us do."*
The conclusion moved twice. Recording the evidence so it does not move a third time.
## Both layers are load-bearing
**Fleet is 34,372 lines of runtime**, not a config format:
```
manager.rs 5,255 worker_runtime.rs 4,937 ledger.rs 4,314 exact.rs 2,705
executor.rs 2,267 host.rs 2,202 profile.rs 1,562 store.rs 1,483
task_spec.rs 1,275 members.rs 1,192 roster.rs 1,184 control.rs 1,050
scheduler.rs 1,039 identity.rs 911
```
Dispatch, scheduling, worker lifecycle, a usage ledger. Operate mode runs on it. Deleting it means rebuilding a scheduler and a ledger that work today.
**Agent profiles are not redundant either.** `AgentProfile` (`fleet/profile.rs:71`) carries `description`, `requires` (closed capability requirements), `profile: FleetProfile` and `origin` — behaviour and authority. The fleet shortlist carries none of that; `FleetModel` (`fleet/members.rs:25`) is only `provider`, `model`, `roles`, `fleet`.
The intended split is stated in `members.rs:1-8`: *"A person builds their fleet by adding models from the providers they have configured; the operator model later picks sub-agent routes from that list only. Shortlist rows carry no role; the roles a model fills are the executable member rows that pin it."*
So: **Fleet = which routes are available. Agent profile = what a role does.** That is also the local-config half of Hmbown/Codewhale#6028's capability / Fleet / policy separation.
## What is actually wrong
Three small things, none of which justify a rewrite:
1. **The same field has two names.** `agents/*.toml` calls it `role_hint`; fleet members call it `role`. Pick one.
2. **A member duplicates profile fields rather than referencing the profile.** On a real machine `scout` exists in both with identical `id`, `model`, `provider` and instructions, free to drift. `FleetMember` already supports the fix — omit `model`/`provider` and it inherits — but the code paths that write members do not use it (see the sibling over-pinning issue).
3. **The roster merges three layers** — `[fleet.profiles]` in `config.toml`, personal `$CODEWHALE_HOME/agents/*.toml`, workspace `.codewhale/agents/*.toml` (`fleet/roster.rs:10-12`). That is reasonable, but nothing surfaces *which layer won*, so a project profile silently shadowing a personal one is invisible. `doctor_operate_fleet_report_json` already computes the losing paths (#5098) — surface it in the fleet view too.
## Decision
Keep both. Keep the `/fleet` name. Work the three items above rather than a rewrite.
## Rejected, with reasons
* **Delete Fleet, keep agents** — throws away 34k lines of working orchestration to fix a field-duplication bug.
* **Delete agents, keep Fleet** — loses `description`, `requires` and profile authority, which the shortlist has no place for.
* **Merge into one store** — re-creates the layering problem the roster already solves, and breaks the capability/Fleet separation Hmbown/Codewhale#6028 depends on.
Contributor guide
Research direction
Start by reading fleet/profile.rs, fleet/members.rs, and fleet/roster.rs, then inspect doctor_operate_fleet_report_json for how losing profile paths are computed. Trace the member-writing paths and the fleet view before changing anything. Done means the shared field naming is consistent, duplicated member data is avoided through profile inheritance, and shadowed profile layers are visible in the fleet view.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli, devtools
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100