# REPORT — TASK B1+B2 data migration + self-serve decommission → controller v0.65.0 (2026-06-14) Customer-self-serve storage **migration** (move app data between drives) and **decommission** (retire a drive), implemented trunk-based on `main` against the locked spike design (`felhom.eu/documentation/audits/SPIKE-decommission-migration-2026-06-14.md`). Pairs with **agent v0.32.0**. Each change carries non-hollow tests; the load-bearing companions were mutation-proven to fail on the pre-fix code. Built + deployed both images to demo guest 9201 / felhom-pve. > Validate against pushed source, not this report. **Live decommission/migration of real data is NOT > yet validated — that is the supervised B3 session (see the end).** ## Confirmed baselines - Controller `main` was `c37ee6d` = **v0.64.0** → shipped **v0.65.0**. - Agent `main` was `8e6d00a` = **v0.31.0** → shipped **v0.32.0**. ## Commits (controller repo unless noted) | # | Commit | Scope | |---|---|---| | 1 | `b57150e` | B1 migration engine (`internal/stacks/migrate.go`, MigrateAll+MigrateApp) + tests + main wiring + backup mutual-exclusion (Change 3) | | 2 | `16a4c3e` | B1 UI — `/api/storage/migrate{,-app,/status}` + settings.html & app_info.html + progress panel | | 3 | `f43697c` (felhom-agent) | v0.32.0 — `POST /disks/decommission` + intent-aware `ReassertGuestBinds` + `GuestBindStore.Remove` + tests | | 4 | `f2596ea` | B2b decommission orchestration + "Hiányzó tárhely" indicator + Change 4 re-enroll fix + tests | ## What shipped - **B1 engine** — crash-safe, resumable, journaled (`/migration.json`). Pipeline validate → stop → copy (`rsync -a --checksum`, additive, **no `--delete`**; conflict-merge walk for non-app content) → verify (`rsync -ani --checksum`, zero pending) → flip+redeploy (`RedeployFromEnv`) → **cleanup gated on all-verified AND all-redeployed**. Two entry points (whole-namespace + per-app incl. SSD→drive) over one pipeline. Single-flight; mutually exclusive with the backup orchestrator (migration refuses while a backup runs; scheduled DB-dump/Tier-2 skip while a migration runs). - **B1 UI** — migrate-all (settings page) + per-app "Áthelyezés másik tárhelyre" (app-info page) + shared status poll/progress panel. - **B2a (agent)** — self-serve `POST /disks/decommission` mirroring eject (user-data role gate, no signature, never formats): intent=decommissioned + bind-prune + unmount. **`ReassertGuestBinds` is now intent-aware** (skips non-enrolled) — the F9-reconnect correctness fix. Operator-signed path untouched. - **B2b** — `handleStorageDecommission` (migrate-all-or-none): migrate-then-decommission via the migration done-hook, or decommission-anyway (stop apps, keep HDD_PATH). `agentapi.Decommission` added. Soft-marker retained. **Change 4:** re-enroll clears the marker (`ClearDecommissioned` had zero callers before). **"Hiányzó tárhely"** badge for apps on a missing drive. ## Tests (per-commit green: `go build && go vet && go test ./...`) - `internal/stacks/migrate_test.go` — siblingName, conflict-merge (differ→`(1)`, identical→dedup, re-run idempotent no `(1)(1)`, 3rd→`(2)`), assert-only verify, skip-dirs, cleanup-only-after-redeploy (+companion: redeploy-fail → source intact), verify-catches-corruption, resume (no re-copy), collision-refused, backup-exclusion, single-flight, SSD→drive resolution, cleanup gate. - `internal/backup/migration_exclusion_test.go` — scheduled DB-dump **skipped** while migrating. - `internal/web/decommission_test.go` — finalize soft-marks + calls agent (entry retained), re-enroll clears marker (Change 4), missing-storage label, unflagged-migration no-op. - **Mutation-proven companions** (fail on pre-fix, pass after revert): migration collision guard, cleanup gate, Change-4 re-enroll clearing, and (agent) the intent-aware reassert (`disks.go` guard removed → decommissioned drive re-bound → test FAILS). ## Build / deploy / verify - Built+pushed `gitea.dooplex.hu/admin/felhom-controller:0.65.0` (192.168.0.180); deployed to guest 9201 via golden/bootstrap → `docker ps`: `:0.65.0 Up (healthy)`, clean startup (`controller_started (0.65.0)`, no errors), `GET /api/storage/migrate/status` → `{"job":null}` (idle, wired). - Built agent v0.32.0 on 180; installed on felhom-pve (`/usr/local/bin/felhom-agent`, backup `.bak-0.31.0`), `systemctl restart felhom-agent` → active, `--version` 0.32.0. - **Live-proven (incidental, safe):** on agent restart the new intent-aware re-assert logged `F9 re-assert: skipping non-enrolled drive (intent-gated) vmid=9201 durable_id=uuid:ce9d… intent=ejected` — the exact drive that lingered in `guest-binds.json` (B0 spike finding) is now correctly NOT re-bound. ## NOT yet live-validated — awaiting supervised B3 The real end-to-end UI flow with actual data was deliberately NOT run (per the task STOP): - put data on a drive → decommission (migrate-to-another OR decommission-anyway) → verify data integrity end-to-end → drive un-enrolled and does NOT auto-mount/re-bind on reconnect → orphaned apps show "Hiányzó tárhely" → re-enroll clears it. Operator-present, real UI (no agent/CLI shortcuts, per the A4 rule). The engine/endpoint logic is unit-proven and the intent-aware reassert is already proven live by the restart log above; the data-bearing round-trip is what B3 covers.