Files
stack/docs/scratchpads/758-fcm-m4-001-v1-v2-migrator.md
jason.woltje 9745bc3f29
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
feat(fleet): add reviewed v1-to-v2 migration preview (#788)
2026-07-16 13:11:16 +00:00

9.3 KiB

FCM-M4-001 — v1-to-v2 inventory, preview, and migrator

  • Task / issue: FCM-M4-001 / Fleet configuration control plane and operator documentation (mosaicstack/stack#758)
  • Branch / base: feat/758-v1-v2-migrator from origin/main c1aecfabe97a5dc81a72f44910cd4e626f41863f
  • Base tree: 46cdfbcdc1d1ff9c7b8b2b9cf3841086590bf774
  • Scope: field-complete inventory, non-mutating preview, canonical v2 migration output, and migration/recovery disposition evidence. All effects use injected fakes or temporary fixtures.
  • Budget: 35K task estimate is the hard working cap. Keep one card/one PR and prefer focused reuse of the v2 compiler, shared role resolver, generated-env boundary, M1 executable disposition inventory, and reconciler observations.

Objective

Implement preview-first v1 migration that never infers unresolved classes or lifecycle, preserves observed running/stopped state, quarantines forbidden legacy environment inputs with key-name/SHA-256-only diagnostics, inventories remote/connector/schema-only entries without reconciling them, covers every M1-classified shipped artifact, and emits deterministic recovery disposition evidence for the later M4-002 canary/rollback gate.

Acceptance mapping

  1. Field-by-field v1 inventory and no-mutation preview.
  2. Canonical output compiled by roster-v2.ts and semantically validated by the existing baseline-plus-roles.local resolver.
  3. Only approved deterministic aliases; every other noncanonical class requires an explicit disposition.
  4. Observed stopped/running maps explicitly to persisted lifecycle; stopped observations never produce running targets.
  5. Generated env is regenerated; strict local data is relocated; forbidden keys are quarantine inputs reported only by key name and SHA-256.
  6. Remote/connector/schema-only entries are inventory-only and excluded from local reconciliation output.
  7. Every shipped M1 example/profile/service preset has executable migration disposition evidence.
  8. Deterministic migration/recovery evidence records source, output, exclusions, quarantine, and restore prerequisites without executing a canary or rollback.

Boundaries

Out of scope: FCM-M4-002 executable canary/rollback and host fixture, #766 communications, #636 commands/channels, live fleet/systemd/tmux/session/migration/deploy/connector/remote/gateway effects, docs/TASKS.md, parent issue mutation, commit, push, and PR operations.

TDD plan

Migration rules and redaction are critical data-mutation/security logic, so tests are written red-first for inventory completeness, explicit class disposition, observed-state preservation, quarantine redaction, inventory-only remote/schema entries, compiler/resolver reuse, artifact coverage, and recovery evidence. Production code follows only after the focused tests fail for the missing behavior.

Plan

  1. Map existing v1 loader, v2 compiler/resolver, env quarantine, reconciler observation, and M1 disposition guard.
  2. Add behavior-oriented failing migration tests with temporary fixtures and injected observation/filesystem adapters only.
  3. Implement the narrow migration module and CLI boundary without a second resolver or live command runner.
  4. Add scoped M4 migration/recovery documentation and executable shipped-artifact evidence.
  5. Run focused tests, full package tests, package/root typecheck and lint, formatting, diff checks, and adversarial redaction/no-effect verification.
  6. Run independent code/security review, remediate findings, reconstruct the synthetic tree using a temporary index, and stop uncommitted.

Progress

  • Collision checks passed: no local/remote branch, worktree, target path, or open PR owned feat/758-v1-v2-migrator.
  • Dedicated worktree created at the exact green origin/main base.
  • Required global/repository guides and FCM requirements/evidence loaded.
  • No matching migration skill exists under the configured skill directories; no unrelated skill loaded.
  • Added a preview-only CLI and migration module that compile with the existing v2 parser/renderer and validate through the shared persona resolver.
  • Added value-free raw-v1 inventory, strict unknown-field/synonym/duplicate detection, inventory-only remote and connector handling, and explicit class/tool-policy decisions.
  • Added separate reviewed lifecycle observations with only unambiguous running/stopped mappings.
  • Added sanitized, non-mutating environment preflight and recovery evidence explicitly marked non-executable.
  • Added executable disposition evidence derived from the exact 13-entry M1 inventory and operator documentation.
  • Tightened untrusted decisions/observations to reject unknown keys, invalid types/enums, extra local records, and competing automatic-alias dispositions.
  • Remediated independent review findings: v1 runtime/reset defaults are preserved, ~ workdirs expand only at env preflight, malformed/required agent fields fail closed before remote exclusion, and all seven shipped v1 fixtures now execute real previews with explicit evidence.
  • Remediated socket and locality authority blockers: socket-only agents stay local; host == fleetHost stays local; only host != fleetHost is inventory-only; ssh-only, missing reviewed fleet-host identity, and contradictory host/ssh targets block explicitly without lifecycle omission.
  • Remediated final exact-tree blockers: a declared v1 root socket cannot be overridden; matching/conflicting socket decisions retain reviewed running evidence; canonical ordering uses a shared locale-independent Unicode code-point comparator; migration evidence preserves all four legacy environment dispositions; backup documentation no longer claims validation that M4-001 does not perform.
  • Remediated immutable-review socket-presence blocker: both socket_name and socketName are field-presence-aware, so explicit empty/default-server declarations remain authoritative and incompatible named decisions block rather than replacing them.
  • Remediated the replacement-tree blockers: the shared v2 compiler and reconciler accept an explicit empty socket as literal default-server identity; present-empty holder session, default/agent work directory, runtime reset command, and alias values block rather than defaulting; missing preview inputs emit one stable blocked JSON object with non-zero status. Snake/camel aliases and whitespace-only input have adversarial coverage.
  • Remediated the subsequent authority blockers: each present-empty CLI path emits exactly one stable blocked JSON object with exit 1 before file reads, and reconciler start/restart or desired-state apply/reconcile fail closed before fixed mosaic-fleet systemd services can act on a default-server roster.
  • Remediated committed-head review blockers: explicitly declared empty runtime objects use the production v1 /clear reset fallback while omitted pi retains /new; lifecycle observations are sorted by canonical agent name; and bare CLI path flags reach preview validation, emit one stable blocked JSON object with exit 1, and perform zero reads. Built production-CLI subprocess tests cover all three bare flags.
  • Remediated late-audit blockers: the documented M4 guard invokes the 13-artifact validator and all seven v1 previews; canonical ~/~/... workdirs remain unchanged in migration evidence and traversal-free forms expand at the shared production projection boundary while ordinary relative and home-relative traversal paths remain rejected; remote inventory and exclusion evidence sort canonically; and every default-server lifecycle-mutating reconciler path fails before observation, projection preparation/application, or fixed-unit effects. Explicit regressions preserve plan, status, doctor, and verify as observational default-server commands.
  • Remediated exact-tree traversal review: ~/../escape and ~/src/../../escape remain unexpanded and fail the unchanged shared unsafe-path validation. Both the shared generated-environment boundary and the production v1 environment caller have red-first regressions, preventing earlier caller normalization from bypassing the boundary.

Verification evidence

  • Focused migration/compiler/environment/reconciler/CLI: 8 files, 372 tests passed.
  • Documented 13-artifact guard: 1 matching test passed and executed all seven v1 previews.
  • Full @mosaicstack/mosaic: 59 files, 902 tests passed.
  • Workspace build: 23 tasks passed.
  • Root typecheck: 42 tasks passed.
  • Root lint: 23 tasks passed.
  • Root format check and git diff --check: passed.
  • Built production CLI: canonical ~/src remains in ready roster/YAML evidence; generated projection preflight succeeds with no blockers; a bare path flag emits one blocked JSON object, exit 1, and no stderr.
  • Independent high-effort late-audit review: no blocker remained in the four assigned repair surfaces; separate exact-tree security audits found no qualifying newly introduced vulnerability.
  • Exact temporary-index synthetic tree includes every tracked changed path; immutable SHA and duplicate reconstruction are recorded in the final handoff.

Risks / blockers

  • M4-001 emits rollback prerequisites/evidence only; executable rollback/canary and the managed/unmanaged host fixture remain owned by M4-002.
  • Remote/connector entries remain inventory-only; later federation or connector reconciliation requires separately reviewed work.
  • Existing environment data is only preflighted. Cutover backup, quarantine write, legacy removal, and generated projection application remain later reviewed effects.