Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.5 KiB
Previewing a Fleet Roster v1-to-v2 Migration
Issue: #758 · Card: FCM-M4-001 · Effect boundary: preview only
mosaic fleet migrate-v1 preview inventories a v1 roster and emits a canonical v2 candidate plus
recovery evidence. It does not write a roster, apply environment projections, invoke systemd or
tmux, contact connectors or remote hosts, launch an agent, run a canary, or execute rollback.
FCM-M4-002 owns reversible cutover and rollback.
Inputs
mosaic fleet migrate-v1 preview \
--source roster-v1.yaml \
--decisions migration-decisions.json \
--observations reviewed-observations.json
The command emits one JSON object and exits nonzero when the preview is blocked, including when any of
--source, --decisions, or --observations is missing. Decision and observation JSON is validated fail-closed: unknown fields, malformed values, and records for non-local
agents are rejected. Decisions must supply a positive v2 generation, a reviewed fleetHost whenever
v1 agents include host or ssh, explicit defaultRuntime, and per-local-agent provider, model,
reasoning, enabled state, and launch policy. A supported socket field declared by v1 remains authoritative,
including an explicit empty value representing the default tmux server; a matching socketName decision is
accepted, an incompatible decision blocks, and a decision supplies the socket only when both supported v1
socket fields are genuinely absent. If v1 omitted tool_policy, decisions must supply an
explicit replacement; it is never derived from class. model_hint is never split or treated as
authority.
Observations are separate reviewed evidence keyed by local agent name:
{
"coder0": { "systemd": "inactive", "tmux": "missing" }
}
Only active plus present maps to running; only inactive plus missing maps to stopped.
Missing, extra, unknown, or contradictory evidence blocks output. An observed-running agent cannot
be marked disabled. Observed-stopped agents always remain stopped.
Field disposition
| v1 field | v2 disposition |
|---|---|
version, transport, tmux, defaults, runtimes |
Inventoried and structurally compiled; omitted values retain v1 defaults, while present-empty holder/work-directory/reset values block for explicit disposition |
agent name, alias, runtime, working directory, persona/reset flags |
Copied or explicitly defaulted only when absent; present-empty alias/work-directory values block for explicit disposition, and ~ stays canonical while expanding only for environment preflight |
provider, model_hint, reasoning_level |
Explicit provider/model/reasoning decisions; no model-hint inference |
class, tool_policy |
Only approved aliases canonicalize automatically; other classes require explicit preserve/replace disposition and shared-resolver validation |
kickstart_template |
No v2 field; explicit inventory-only disposition required |
agent host, ssh |
host != fleetHost is demonstrably remote and inventory-only; host == fleetHost stays local; ssh-only, missing fleet-host evidence, or contradictory targets block |
agent socket |
Same-host candidate only when it matches the canonical fleet socket; conflicts block for explicit future disposition |
root connector |
Inventory-only; never contacted or reconciled |
| unknown fields or snake/camel synonym collisions | Inventoried and block readiness |
.env.generated |
Rebuild from canonical roster data |
no legacy .env |
absent; no legacy action required |
legacy .env containing generated keys only |
regenerate-only; replace later from canonical roster data |
legacy .env containing strict local keys |
relocate-local; preserve those keys in .env.local during a later reviewed cutover |
legacy .env containing forbidden/unsafe/sensitive/malformed keys |
quarantine; private input only, with diagnostics limited to code, key, and SHA-256 |
The only automatic aliases are implementer → code, reviewer → review, and
operator-interaction → interaction. Similar or domain-specific names are never inferred. Automatic
classes do not accept competing disposition records. Semantic validation delegates to the existing
baseline-plus-roles.local resolver after the candidate is compiled by the existing v2 compiler.
Evidence and recovery boundary
Ready output includes source and candidate SHA-256 identities, value-free field inventory, excluded remote/connector entries, explicit environment dispositions with sanitized diagnostics, and the lifecycle evidence used for each local candidate. Canonical evidence ordering compares Unicode code points directly and does not depend on process locale. Recovery is marked non-executable and assigns the executable gate to FCM-M4-002.
Before any later cutover, preserve these artifacts:
- authoritative v1 roster backup;
- agent environment backup, including
.env.localand private quarantine inputs; - reviewed lifecycle observations;
- canonical candidate v2 roster and its SHA-256.
See backup and restore. Preview output is migration-readiness evidence, not proof that migration, canary, or rollback occurred.