Files
felhom-controller/REPORT.md
T
admin 3603d1fc7f Tier-2 engine rework: class-driven legs, v2 layout, NAS-target exclusion (Task 3b, v0.135.0)
tier2_capture.go: classified apps get TierSecondary per-bind legs (paperless copy shrinks — export
drops); legacy apps keep the byte-identical resolver set. v2 relpath-mirroring layout
(backups/secondary/<stack>/{marker LAST, recovery-unit/, hdd/<rel>/, userdata/<rel>/}); N>1 native
(errTier2MultiDir/tier2AppDataName deleted). Migration=delete-and-rebuild + reconcile; all RemoveAll
via tier2SafeRemove (refuses outside backups/secondary/). SSD=state-only tier. selectTier2Target
never picks network storage (pinned+auto, F-6C-1). Restore reads v2 behind a marker gate.
Part 0: offbox_enlarge_blocked is a persisted one-time Load seed (opt-out sticks), not a getter
append. Part 0.5: offsite restore scratch prefers a local (non-network) path.
Full v2 test suite + all 10 §10 red-proofs verified. Destructive writes bounded to backups/secondary/.
2026-07-15 10:10:20 +02:00

76 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# REPORT — Tier-2 engine rework (Task 3b) — controller v0.135.0
## Summary
Task 3b: the tier-2 column of the matrix (architecture §2/§8). The tier-2 appdata leg becomes
**class-driven** for classified apps (per-bind mandatory + optional; paperless's copy shrinks as
`export` drops out), legacy apps keep a byte-identical resolver set — both mapped into a new **v2
relpath-mirroring layout** that lifts the N>1 refusal structurally and gains a reconcile step; the SSD
fallback is an enforced **state-only** tier; and `selectTier2Target` gains the F-6C-1 **network-target
exclusion** (pinned + auto). Restore reads v2 behind a marker gate. Plus **Part 0** (persisted prefs
seed — fixes the 3a-fix un-disableable checkbox) and **Part 0.5** (offsite scratch prefers local).
Every destructive write lands only under `backups/secondary/<stack>/` (code-asserted).
## Baselines (live-verified at session start)
| Repo | `main` @ start | Version | → |
|---|---|---|---|
| felhom-controller | `1245a6c` | v0.134.1 | **v0.135.0** |
## Files
- **new** `internal/backup/tier2_capture.go``tier2CaptureSet` (TierSecondary legs + gaps), `tier2Leg`, `tier2DestRel`.
- **mod** `internal/backup/tier2.go``selectTier2Target` (network exclusion, two sizes, `StateOnly`), `RunTier2` v2 body (migration/mirror/reconcile/marker-last), `tier2SafeRemove` + `tier2Reconcile` + `classifyTier2Rel`, `Tier2Info` rewired, header + `RunTier2` comments truthful; deleted `errTier2MultiDir`/`tier2AppDataName`.
- **mod** `internal/backup/tier2_restore.go` — v2 marker gate + two-subtree missing-only merge; deleted `errTier2MultiDirRestore`.
- **mod** `internal/backup/offbox_restore.go` — Part 0.5 scratch local-preference.
- **mod** `internal/backup/backup.go``tier2SSDFits` seam.
- **mod** `internal/backup/appbackup_bridge.go``ClassMandatory/Optional/Excluded` aliases.
- **mod** `internal/settings/settings.go` — Part 0 seed (`OffboxEnlargeNoticeSeeded` + `seedOffboxEnlargeNotice` at Load; getter append removed); `CrossDriveBackup.LastWarning`.
- **mod** `internal/web/handlers.go` + `templates/backups_apps.html` — surface `Tier2LastWarning`.
- **tests** new `tier2_v2_test.go`, `offbox_scratch_test.go`; rewrote `notif_migration_test.go` (Part 0), `tier2_restore_test.go` fixture (v2), `async_restore_test.go` fixture (v2), `tier2_test.go` signatures; **deleted** obsolete `tier2_appdata_test.go`.
- CHANGELOG / REPORT / CONTEXT / README.
Untouched: offbox engine (except `offboxRestoreScratchDir`), appexport, hub, agent; `rsyncMirror`/`rsyncRestoreMissing` unchanged.
## Tests — results
`go build ./... && go vet ./... && go test ./...`**green.** New: v2 suite (§7 AH + reconcile
keep/remove + safe-remove boundary proof + `classifyTier2Rel` table), Part 0 (idempotent seed +
opt-out-sticks + fresh-customer), Part 0.5 (local-preference + network-only WARN).
### §10 red-proofs (mutate → FAIL → revert), all verified
| ID | Mutation | Test |
|---|---|---|
| A | classified app routed to the legacy resolver | `ClassifiedPaperless` (`export` appears in dest) |
| B | legacy routed through ComputeCaptureSet | `LegacyMultiDir` (empty legs) |
| C | marker written before the legs | `MigrationAndMarkerLast` (marker present after a leg failure) |
| D-reconcile | disable stale detection | `Reconcile` (stale dir survives) |
| D-boundary | neuter the safe-remove prefix assert | `Reconcile` (removal outside destBase not refused) |
| E | SSD copies optional (no state-only filter) | `SSDStateOnly` (optional on the SSD) |
| F1 | drop the pinned IsNetwork check | `NetworkExclusion/pinned` (NAS pin honored) |
| F2 | drop the auto IsNetwork check (NFS iterated first) | `NetworkExclusion/auto` (NAS selected) |
| G2 | neuter the restore marker gate | `RestoreRefusesOldLayout` (merge runs on old layout) |
| Part 0 | don't persist the seed marker | `SeedOffboxEnlargeNotice_OptOutSticks` (type re-enabled) |
All reverted; post-revert full suite green; no residue.
## Deploy / verify
`<filled after deploy>`
## §13 live validation
`<filled after the live legs>`
## 6D-pending
`<updated after §13>`
## Observations
- The `Tier2LastWarning` surfacing is minimal (one line on the app's tier-2 row); the SSD state-only
hint was already rendered via `EffectiveIsSSD`/`EffectiveDesc` (no new template needed there).
- `tier2SSDFits` seam added because `system.GetDiskUsage` is Linux-only (nil on the Windows test host)
— production uses the real predicate.