Files
stack/docs/fleet/migration/v1-to-v2.md
Jarvis 0e814324ca
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
feat(fleet): add reviewed v1-to-v2 migration preview
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 22:04:51 -05:00

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:

  1. authoritative v1 roster backup;
  2. agent environment backup, including .env.local and private quarantine inputs;
  3. reviewed lifecycle observations;
  4. 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.