Capture-set computation (INERT; Task 3-core, v0.133.0)

Pure appbackup.ComputeCaptureSet(binds, hasClassification, tier, hddPath) → CaptureSet
{HasClassification, Paths, Skipped}: legacy short-circuit → tier filter (§2) → structural
guards → equal-Abs collapse (mandatory>optional) → containment dedup → sort. Slash algebra,
no filepath/FS/log. Structural guards (traversal / bare HDD drive-root / reserved backups/)
are load-bearing (the compose parser does not reject ..). Pure CrossAppOverlaps advisory
(WARN wiring deferred to 3a/3b). INERT — no engine consumes it yet.

Tests: Groups A-F (appbackup) + F-S3 no-seam wiring (stacks); all 6 §10 red-proofs verified.
Docs: architecture §3 aligned (felhom.eu 8d85da7).
This commit is contained in:
2026-07-14 21:53:05 +02:00
parent 95f3180ab4
commit 2668ac4da3
7 changed files with 734 additions and 104 deletions
+40
View File
@@ -1,5 +1,45 @@
## Changelog
### v0.133.0 — Capture-set computation (INERT; Task 3-core) (2026-07-14)
Task 3-core of the backup-classification-redesign arc
(`felhom.eu/documentation/architecture/07-backup-architecture.md` §3; matrix §2; SP-1/2/3 spike
verdicts landed in `SPIKE-restic-snapshot-shape-2026-07-14.md`). Ships the **pure capture-set
computation** in the `appbackup` leaf package — **deliberately INERT**: NO backup tier changes
behavior. 3a (offsite policy engine) and 3b (tier-2 rework) are the consumers.
- **`ComputeCaptureSet(binds, hasClassification, tier, hddPath) CaptureSet`
(`internal/appbackup/captureset.go`, new):** turns an app's `ClassifiedBinds` into a tier-filtered,
structurally-guarded, containment-deduped absolute path set. Fixed pipeline (§8): legacy
short-circuit → tier filter → structural guards → equal-Abs collapse (mandatory > optional) →
containment dedup (keep ancestor) → sort by Abs. `CaptureSet{HasClassification, Paths []CapturePath,
Skipped []SkippedPath}`; each `CapturePath` carries `{Abs, Root, RelPath, Class}`.
- **Tier columns (§2):** `TierOffsite` = mandatory only (optional never ships offsite); `TierSecondary`
= mandatory + optional; **excluded** is silently dropped at every tier (never in Paths, never in
Skipped). A **legacy** app (`hasClassification=false`) resolves NOTHING — `{HasClassification:false}`,
nil Paths/Skipped — so the engines' no-block branch stays byte-identical (the SQ5 cost-regression
guard: an unmigrated app never resolves a bind into an automatic tier).
- **Structural guards (security-shaped, load-bearing):** the compose parser path.Cleans but does NOT
reject `..`, and `ValidateBackupSpec` vets only *spec* entries, so an unlisted writable
`${HDD_PATH}/../x` bind arrives classed **mandatory**. Guards (run after the tier filter) move
traversal (`..` segment / absolute), bare **HDD** drive-root (`""` — would nest `<hddPath>/backups`),
and reserved `backups/` zone captures into `Skipped` with distinct English reasons (a skipped
mandatory = a capture GAP the engines log loudly). Bare **userdata** root is allowed
(`<hddPath>/userdata`). Segment-wise `..` detection (a legit `a..b` dir passes).
- **`CrossAppOverlaps(map[app]CaptureSet) []Overlap`:** pure §4.2 advisory — same absolute path in ≥2
apps' Paths (exact-Abs only; cross-app *containment* is legitimate and does NOT report). WARN wiring
is deferred to 3a/3b by design — no log call sites here.
- **Purity:** no `os`/`exec`/`filepath`/logging; **slash algebra** (`path.Join`/`path.Clean`)
throughout — resolved paths are in-container Linux paths, and `filepath` on the Windows test host
would flip separators and break containment prefix checks.
- **Docs alignment:** architecture §3 sketch updated to the as-built API (`UnitOnly``HasClassification`,
`Skipped` added), felhom.eu commit `8d85da7`.
- **Tests (all green):** `internal/appbackup/captureset_test.go` (Groups AF: per-tier split, legacy
inertness, excluded-invisible, structural guards + legit `a..b`, containment/collapse/determinism,
cross-app overlap) + the F-S3 no-seam wiring test `internal/stacks/captureset_wiring_test.go`
(Group G, real Manager → ClassifiedBinds → ComputeCaptureSet end-to-end). All 6 §10 red-proofs
verified (mutation → fail → revert). **No behavior change; no UI; no engine edits.**
### v0.132.0 — Backup classification: schema + parser + pure classifier (INERT; Task 2) (2026-07-14)
Task 2 of the backup-classification-redesign arc