v0.217.0: prefill from the app's own backup, where-the-data-goes on deploy, bounded inventory fan-out
gates / gates (push) Successful in 10s
gates / gates (push) Successful in 10s
Completes R-351 and ships R-352's visibility half. Gates 11/11 OK, suite 28 packages ok, go vet clean, -race clean on the changed package - all run and read BEFORE this commit. PART 2 SCENARIO A - the deploy page prefills the address and data folder from the app's OWN backup. backup.RecordedUnitForStack scans every readable namespace root (the app is NOT installed in this case, so there is no own drive to ask) and reads manifest.json plus the captured compose/app.yaml. Local file reads only: no network, no restic, no restore. RecordedAddress.Known() requires BOTH halves on purpose - an absent SUBDOMAIN makes the live deploy path substitute the CATALOG default (stacks/deploy.go:88-90), and offering that back as "what your backup says" would be a fabricated fact. The prefill is labelled as coming from the backup and stays editable: a memory, not a lock. PART 1 VISIBILITY (R-352) - the deploy page now states where the app's data will live before the button is pressed. Measured 2026-08-21: 13 of 53 catalogue templates declare a storage field; the other 40 have none and their data goes to the system drive, which no screen said. Metadata.HasDeployField answers "does this app have somewhere to PUT a recorded value?" - for the 40-class a recorded placement is a fact to state, never a value written into a field that does not exist. NO PLACEMENT CHANGED. NOTHING MIGRATED. The rest is a filed specification. PART 4 - measured before theorising, on the live off-site target: snapshots --json 2605 ms once; stats 2697 ms PER APP, sequential, 5 app tags => 2605 + 5*2697 = ~16.1 s, matching the reported ten-to-fifteen seconds. The cause is the shape already on file, so the per-app size calls now run concurrently, BOUNDED TO 4. The bound is the safety property, not the speed one: the repository is a Hetzner Storage Box with a session cap, and a refused size call returns SizeBytes 0 - a silent UNDER-REPORT of the customer's data rather than a visible failure. Peak-in-flight is asserted. OffsiteInventoryList had no test at all before this. TEMPLATE SAFETY - every Restore* key is set UNCONDITIONALLY in the deploy handler, because a template doing index/eq against an undefined key errors at RENDER time: green build, green vet, green suite, 500 on the page. Four render tests, one per branch, because the existing deploy render test only renders AutoFields and never reaches these blocks. RED-PROOFS, mutation asserted applied then reverted to 0: A three template guards dropped (count asserted 3) -> the blank form returned P4 inventorySizeConcurrency = 1 -> "peak in flight was 1", elapsed 282ms = sequential DOCS: CHANGELOG v0.217.0 (MinAgent 0.129.0 unchanged), CONTEXT (the restore's own memory + what is next), controller/README.md (Backup System), REUSE.md (4 new rows), REPORT.md overwritten - the previous REPORT preserved to audits/REPORT-v0.216.0-2026-08-14.md first. NOT fixed here, filed as R-353 and named the next session's first item: a restore whose unit carries no db_dumps and no volume_dumps still reports a bare completion.
This commit is contained in:
+38
@@ -1950,6 +1950,44 @@ Last updated: 2026-06-13 (v0.60.0 backlog-Medium cleanup)
|
||||
|
||||
---
|
||||
|
||||
## THE RESTORE'S OWN MEMORY (v0.217.0, 2026-08-21) — R-351 / R-352 / R-353
|
||||
|
||||
> **Both sides of a written fact must be checked, not just the writing side.** Every recovery unit
|
||||
> has recorded `drive` and `namespace_root` since schema 1. **No non-test code in the repository ever
|
||||
> read either back.** A restore into a different destination than the backup recorded therefore
|
||||
> succeeded silently under a green message. This is the same shape as several defects closed this
|
||||
> month, and the cheap test for it is one grep: *who reads this field?*
|
||||
>
|
||||
> **`IsRunning()` is still the wrong flag, in one more place than we knew.** v0.154.0 fixed the wizard
|
||||
> and left a comment explaining why. The **seven handlers** were never moved over, so a second press
|
||||
> genuinely started a second run and reported „…elindult". A comment explaining a trap does not fix
|
||||
> the other call sites — grep for them.
|
||||
>
|
||||
> **A result nobody can see is the same defect as no result.** The banner gated its terminal state on
|
||||
> a page-local `sawRunning`. The 8.666 s OpenGist restore finished before any poll saw it, so no
|
||||
> screen said it had completed. Fixed with `RestoreOpStatus.LastRecent` — and the window now lives in
|
||||
> `internal/backup` as ONE expression that both surfaces read.
|
||||
|
||||
**State, and what is next.**
|
||||
|
||||
- **Shipped:** placement comparison + named mismatch + `ack_placement`; not-installed refusal names
|
||||
the recorded drive; deploy prefill from the app's own backup; `restoreOpBlocked()`; `LastRecent`;
|
||||
off-site listing bounded-concurrent (measured 16.1 s → two waves).
|
||||
- **R-352 partly closed.** 40 of 53 catalogue templates declare no data path, and
|
||||
`GetDefaultStoragePath()` is read by nothing that places data — its comment `// new apps use this by
|
||||
default` has never been true. **Only visibility shipped**; the deploy page now states where the data
|
||||
will live. **No placement changed, nothing migrated.** Specification:
|
||||
`felhom.eu/documentation/backlog/SPEC-app-data-placement-2026-08-21.md`.
|
||||
- **R-353 is the next session's first item.** A restore whose unit carries no `db_dumps` and no
|
||||
`volume_dumps` reports a bare completion. OpenGist's unit held configuration and nothing else, and
|
||||
the restore said only that it had finished. Fix the outcome first; *then* prove the off-site
|
||||
coverage of a named-volume app by running a dump cycle — do not close the first on the second.
|
||||
- **Open and unproven:** whether the 40-class reaches the off-site tier at all has **not been
|
||||
observed**. `runVolumeDumps` covers them on paper; every unit on the box read `volume_dumps: None`
|
||||
because no nightly run had happened yet.
|
||||
|
||||
---
|
||||
|
||||
## THE TWO RULES THE RECOVERY JOURNEY LEANS ON (v0.203.0, 2026-08-06)
|
||||
|
||||
> **1. A credential the hub stages is collected by the box, not waited for.** The reconcile that
|
||||
|
||||
Reference in New Issue
Block a user