4.1 KiB
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 <mosaic-home>/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 <name> and plan delete <name> 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 <name>.env.generated projection for the removed roster entry. Operator-owned <name>.env.local, legacy <name>.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.