.fab exclusion scoping: classes in the manual export (Task 4, v0.136.0)

SQ6 over-capture FIXED for classified apps: the userdata root tar is exclude-scoped (keeps only
ancestors/descendants of a SELECTED bind relpath — R1-C, the tier2Reconcile keep-rule); no selected
userdata bind → no root tar (radarr state-only). New appbackup.ComputeFabBuckets (shared
resolveGuardCollapse pipeline; class buckets; guards over ALL classes; no cross-bucket containment).
appexport/fabplan.go: computeFabPlan + tarDirectoryExcluding + fabEstimateSplit. ExportRequest gains
DeselectOptional/OptInExcluded (both start handlers — two-call-site); mandatory is a server-side
floor. Manifest v1 + import UNTOUCHED; legacy apps byte-identical to v0.130.0. Estimate additive
class split; export UI: locked-mandatory/optional-checkboxes/excluded-opt-in + two-number warning.
All 6 §10 red-proofs verified. 6D Accept #1 now runs against this shape.
This commit is contained in:
2026-07-15 11:28:40 +02:00
parent 5f216138e5
commit cf9ce01917
19 changed files with 1392 additions and 177 deletions
+38
View File
@@ -1,5 +1,43 @@
## Changelog
### v0.136.0 — `.fab` exclusion scoping: classes in the manual export (Task 4) (2026-07-15)
Task 4 — the `.fab` column of the matrix (architecture §2; the SQ5 exclusion-scoping verdict + Viktor
ruling #1 + R1-C). **The SQ6 over-capture is fixed for classified apps:** a manual export no longer
drags every sibling app's content along in the userdata root tar. Mechanically unchanged from
v0.130.0 — ONE exclude-scoped userdata-root tar + per-mount skip — so the **manifest stays v1**
(basename keying, the `userdata` fallback), the **import side is untouched**, and old controllers
import new bundles correctly (they just extract a root tar containing less). **Legacy (no-block) apps
export byte-identically to v0.130.0** (the SQ5 safety net).
- **`appbackup.ComputeFabBuckets` (new, pure):** the same resolution + structural guards + equal-Abs
collapse as `ComputeCaptureSet` (shared pipeline extracted, not duplicated), but bucketed by class
(mandatory / optional / excluded), guards over ALL classes (a traversal path is never plannable —
opt-in or not), and NO cross-bucket containment dedup (a mandatory child inside an excluded parent
stays independently addressable).
- **The export plan (`appexport/fabplan.go`):** `ExportRequest` gains `DeselectOptional` +
`OptInExcluded`; `computeFabPlan` resolves the selection (mandatory forced-in — a **server-side
floor** ignores a client trying to deselect a mandatory path; optional default-in; excluded
default-out) into `{SkipMounts, SkipUserdataTar, UserdataExcludeRels}`. The userdata root tar is
**skipped entirely** when no userdata bind is selected (radarr → state-only, the SQ6 headline); else
the exclude list is the **topmost dirs neither ancestor nor descendant of a selected relpath**
(R1-C — the exact `tier2Reconcile` keep-rule). An HDD mount matching no classified bind is KEPT
(fail toward capture). `tarDirectoryExcluding` skips excluded subtrees in the walk (`tarDirectory`
is now a thin wrapper).
- **Estimate split (additive):** `ExportEstimate` gains `HasClassification` + `BaseBytes` +
per-path `MandatoryItems`/`OptionalItems`/`ExcludedItems` (each with its du size) — existing fields
and the fits gate are unchanged. Both estimate pipelines surface it (shared `EstimateExport`).
- **UI (`app_export.html`, classified apps only):** locked mandatory list, pre-selected optional
checkboxes, a collapsed excluded opt-in behind the two-number warning
("Alap mentés: ~X. A kihagyott, nagy méretű tartalommal együtt: ~Y.") + the FileBrowser pointer;
totals recompute client-side per toggle. Selections POST through BOTH start endpoints (the
two-call-site).
- **Tests:** `ComputeFabBuckets` bucket/guard/containment tests; `computeFabPlan` scenarios AF +
§8 edge cases; `tarDirectoryExcluding` FS-level; export-level bundle tests (exclude-scoped tar,
legacy full-root, all-excluded no-tar); the two-call-site bundle test across both start pipelines.
All 6 §10 red-proofs verified. **CAMPAIGN-6D Accept #1** (the ≥1 GiB `.fab` full circle) now runs
against this final capture shape.
### v0.135.0 — Tier-2 engine rework: class-driven legs, v2 layout, NAS-target exclusion (Task 3b) (2026-07-15)
Task 3b of the backup-classification-redesign arc — the tier-2 column of the matrix (architecture