Applies the document contract from
docs/plans/2026-08-20_stack-docs-flatten-and-alignment.md section 3, partially:
`kind` and `status` only. `parent` is deliberately held until the flatten in
section 4 lands, so that 127 documents do not have to be re-pointed by hand
when docs/fleet/NORTH_STAR.yaml moves to docs/NORTH_STAR.yaml.
Scope, measured on origin/next at 63069149:
127 live docs = all *.md under docs/ minus docs/archive/ minus docs/_old_structure/
104 stamped here
19 held operator judgement (plan section 9), worklist in the same PR
3 held the SUPERSEDED TASKS.md stamps, which cite the moving path
1 untouched docs/fleet/FLEET-DOCTRINE.md, already stamped in W1
Kinds applied: 54 guide, 34 record, 9 spec, 6 tracking, 1 projection.
Every row carries a confidence and a one-line rationale in the worklist.
Two collisions with the existing state, both flagged rather than resolved:
1. docs/README.md:150-160 already documents a front-matter convention
(title/type/audience/status/source_of_truth) with its own allowed values.
It is applied to 4 of 127 files. Its `status` vocabulary is
current|draft|deprecated|historical; the new contract's is active|superseded-by.
The key collides. This commit lets the new contract win and rewrites
`status: current` to `status: active` on those 4 files, keeping their other
legacy keys untouched. No code reads any of them: `git grep source_of_truth`
outside docs/ returns nothing. docs/README.md still prescribes the old
convention and is an operator row, so it is not edited here.
2. Two of the plan's 20 operator rows are YAML files, not markdown
(docs/fleet/examples/roster-v2.yaml, docs/openapi-tess.yaml), and the
contract's front-matter form has no defined meaning for a .yaml document.
That gap also applies to docs/fleet/NORTH_STAR.yaml, the source of truth
itself. Raised in the worklist.
A third row from the plan, docs/fleet/north-star.md, no longer exists: W1
renamed it to docs/fleet/FLEET-DOCTRINE.md.
Verification: 104/104 parse with the expected kind and status in front matter;
the check was shown to reject a wrong kind before it was trusted. The diff
removes 4 lines total, all of them `status: current`.
4.1 KiB
kind, status
| kind | status |
|---|---|
| guide | active |
Local Fleet Agent Mutations
FCM-M2-002 provides local roster-v2 create, get, update, delete, and plan operations. They only change desired state and derived environment projections. They never start, stop, inspect, reconcile, or otherwise act on runtimes, systemd units, tmux sessions, or heartbeats.
CLI contract
The commands operate only on the canonical /fleet/roster.yaml v2 authority and print one JSON object to stdout. --agent is a JSON object with the roster agent fields expressed as className, toolPolicy, workingDirectory, persistentPersona, resetBetweenTasks, and launch: { "yolo": boolean }.
mosaic fleet get <name>
mosaic fleet plan <create|update|delete> [<name>] --expected-generation <n> [--agent '<json>'] [--persisted-start]
mosaic fleet create --expected-generation <n> --agent '<json>' [--dry-run] [--persisted-start]
mosaic fleet update <name> --expected-generation <n> --agent '<json>' [--dry-run]
mosaic fleet delete <name> --expected-generation <n> [--dry-run]
get returns the authoritative generation and the selected agent. plan create derives its name from --agent; plan update and plan delete require the target name. --agent accepts only the documented roster-v2 request fields and launch.yolo; unknown keys such as commands, channels, or secret references are rejected. Rejection diagnostics return only the stable invalid-request code and never echo a rejected value. plan and --dry-run validate the complete proposed roster and projections but write neither the roster nor projections. --persisted-start is available only for a create request: it records desired_state: running, but does not start a process. Without it, create records enabled: true and desired_state: stopped. Handled failures return JSON with error.code and exit non-zero; unclassified validation/projection failures use the redacted mutation-failed code.
Generation, validation, and idempotency
Each create, update, or delete request includes expectedGeneration. A request whose expected value differs from the authoritative roster generation fails with stale-generation; reload and retry with a newly computed plan. A private mutation lock rejects concurrent writers with concurrent-mutation.
planFleetAgentMutation is deterministic and side-effect free. executeFleetAgentMutation validates the complete proposed roster through the existing structural and shared persona resolver, prepares generated/local/quarantine projections, and writes the roster authority atomically before applying derived projections. Equivalent create retries and delete requests for an already-absent agent are idempotent no-ops.
Delete removes only the exact .env.generated projection for the removed roster entry. Operator-owned .env.local, legacy .env, quarantine records, and unrelated projections remain untouched. An already-absent generated projection is treated as stale derived state, not as a failed mutation.
Result and recovery
Mutation results are JSON-safe objects with applied, authoritativeRoster, projections, plan, and—only if a derived projection write fails after the authoritative roster write—a recovery object. applied is true only when every roster and derived-projection write completed. The explicit state fields prevent a partial result from being mistaken for a rollback or a no-op:
{
"applied": false,
"authoritativeRoster": "committed",
"projections": "incomplete",
"recovery": {
"code": "projection-apply-failed",
"action": "regenerate-projections-from-roster"
}
}
Dry-runs and idempotent no-ops report authoritativeRoster: "unchanged" and projections: "not-applied"; a complete mutation reports "committed" and "complete". Recovery output identifies the authoritative roster path and regeneration action only. It never contains generated/local/quarantine values, credentials, or command text. A recovery result exits non-zero because the authoritative roster was persisted but derived projections require regeneration. Regenerate projections from the roster before attempting another mutation.