Files
felhom-controller/REPORT.md
T
admin cf9ce01917 .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.
2026-07-15 11:28:40 +02:00

3.9 KiB
Raw Blame History

REPORT — .fab exclusion scoping (Task 4) — controller v0.136.0

Summary

Task 4: the .fab column of the matrix (SQ5 verdict + ruling #1 + Viktor's R1-C). The SQ6 over-capture is fixed for classified apps — a manual export's userdata root tar is now exclude-scoped (keeps only ancestors/descendants of a selected bind), so sibling apps' content no longer rides along; an all-excluded app produces no userdata tar at all. Mechanics unchanged from v0.130.0 (one root tar + per-mount skip), so the manifest stays v1 and the import side is untouched; legacy apps export byte-identically. Mandatory is a server-side floor; optional is a pre-selected checkbox; excluded is opt-in behind the two-number warning.

Baselines (live-verified at session start)

Repo main @ start Version
felhom-controller 5f21613 v0.135.0 v0.136.0

Files

  • mod internal/appbackup/captureset.go — extracted resolveGuardCollapse (shared pipeline); new ComputeFabBuckets + FabBuckets.
  • new internal/appexport/fabplan.gofabPlan, computeFabPlan, fabUserdataExcludes + classifyFabRel, tarDirectoryExcluding, fabEstimateSplit.
  • mod internal/appexport/export.goExportRequest += DeselectOptional/OptInExcluded; dirLister seam; exportHDDData consumes the plan; tarDirectory → thin wrapper.
  • mod internal/appexport/estimate.goExportEstimate class-split fields (additive); FabItem.
  • mod internal/appexport/provider.go + cmd/controller/main.goGetStackClassifiedBinds on the export seam + adapter.
  • mod internal/web/handler_export.go + handler_export_download.go — both start handlers carry the selections (two-call-site).
  • mod internal/web/templates/app_export.html — the class-selection UI.
  • tests new fabbuckets_test.go, fabplan_test.go, fabexport_test.go, fab_export_test.go; provider fakes updated.
  • CHANGELOG / REPORT / CONTEXT / README.

Untouched (§5/§12): manifest.go, validate.go, restore.go, every import handler, ExportDataMounts — the verdict rests on the import side staying byte-identical.

Tests — results

go build ./... && go vet ./... && go test ./...green. New: bucket/guard/no-containment (ComputeFabBuckets); plan scenarios AF + §8 (unmatched-mount-kept, skip-when-none, mandatory floor, nested excludes); 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.

§10 red-proofs (mutate → FAIL → revert), all verified

ID Mutation Test
A legacy routed through the classified plan (drop short-circuit) FabExport_LegacyFullRoot (userdata tar skipped)
B always-tar the userdata root FabExport_AllExcludedNoUserdataTar (tar present)
C invert the keep-rule FabExport_ExcludeScopedUserdataTar (mandatory excluded)
D3 drop the server-side mandatory floor FabPlan_MandatoryFloor (mandatory deselected)
E skip guards for excluded buckets ComputeFabBuckets_GuardsAllClasses (traversal enters a bucket)
F drop selection fields from one start handler Fab_SelectionsRideBothStartPipelines/download (opted-in content absent)

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

  • ComputeCaptureSet was refactored to share resolveGuardCollapse with ComputeFabBuckets (no duplicate pipeline); its existing tests stayed green through the refactor.
  • The estimate split is in the shared EstimateExport, so both estimate pipelines surface it by construction; the genuine two-call-site risk (and the F red-proof) is on the START selections, which the bundle test exercises per-handler.