Backup classification: schema + parser + pure classifier (INERT, v0.132.0)
Task 2 of the backup-classification-redesign arc. Ships the referential-
coupling classification as DATA + PARSER + PURE CLASSIFIER, deliberately
inert — no backup tier changes behavior. Task 3 (tier policy engine) and
Task 4 (manual .fab UI) consume it.
- appbackup/classify.go: BackupSpec/BindSpec/ComposeBind/ClassifiedBind;
ClassifyBinds (SQ5 two-level default — explicit beats :ro; unlisted
writable→mandatory, unlisted :ro→excluded; nil spec→legacy/false);
ValidateBackupSpec (whole-block-reject on any defect, first defect named).
- stacks/classify_binds.go: ParseComposeClassifiableBinds — ${VAR}-relative
binds + :ro flag (NOT ParseComposeHDDMounts/ExportDataMounts, the traps).
- Metadata.Backup + LoadMetadata as the single validation choke point (bad
catalog block → nil + one ERROR → legacy, within one sync cycle).
- Manager.ClassifiedBinds + StackDataProvider.GetStackClassifiedBinds seam
(delegated by stackAdapter, nil-stubbed in every fake) — wired + tested
now so Task 3 consumes a tested seam.
INERT: full pre-existing suite green with zero test-logic edits. +14 tests;
red-proofs RP-1..RP-4 confirmed. The 13 catalog backup: blocks ship in the
same app-catalog change (this controller deploys first).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A45Qop8YY8tS94bz63LFne
This commit is contained in:
@@ -252,6 +252,17 @@ Each app can define rich metadata in `.felhom.yml`:
|
||||
surfaces it on `/apps/{slug}` as a "Kezdeti belépési adatok" card (masked password + reveal/copy),
|
||||
labelled as the *initial* credential (stays valid only until the customer changes it in-app). Hidden
|
||||
when the container is down / file missing / parse fails. Reuse for any future self-seeding app.
|
||||
- `backup` (v0.132.0): the **referential-coupling classification** block (Task 2). Two optional lists,
|
||||
`userdata:` (relative to `${USERDATA_PATH}`) and `hdd:` (relative to `${HDD_PATH}`), each of
|
||||
`{path, class}` where `class ∈ {mandatory, optional, excluded}` (COUPLED / DECOUPLED-precious /
|
||||
DECOUPLED-bulk). `LoadMetadata` validates the block against the app's compose binds and **rejects the
|
||||
WHOLE block** (→ nil + one `[ERROR]`, app behaves as legacy) on any defect. Semantics
|
||||
(`appbackup.ClassifyBinds`): an explicit entry wins over the `:ro` default; an unlisted writable bind
|
||||
defaults `mandatory`, an unlisted `:ro` bind defaults `excluded`; **no block at all = legacy behavior
|
||||
per tier**. **INERT** as of v0.132.0 — the schema/parser/classifier + the
|
||||
`Manager.ClassifiedBinds` / `StackDataProvider.GetStackClassifiedBinds` seam exist, but no backup
|
||||
tier consumes them yet (Task 3 = tier policy engine, Task 4 = manual `.fab` UI). See
|
||||
`felhom.eu/documentation/audits/SPIKE-backup-classification-2026-07-14.md`.
|
||||
|
||||
The `/apps/{slug}` page renders hero section, screenshots, setup guide, and optional config form.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user