R-356: the off-site restore refused every app that has no data drive
gates / gates (push) Failing after 12s

ReconstituteFromOffsite and PlaceOffsiteRestore both resolved the restore
destination with the RAW HDD_PATH and read an empty answer as "the app is not
installed". For 40 of the 53 catalogue apps that answer is correctly empty and
permanent, so both actions refused forever for a running, healthy app — and told
the customer to reinstall it "in the same place", which those apps never offer.

Separate the two questions. "Installed?" is asked of ListDeployedStacks via a new
Manager.isStackDeployed that fails CLOSED on a nil provider. "Where?" is answered
by GetAppDrivePath — the same resolver CaptureRecoveryUnit wrote the snapshot
with, so the restore aims at the place the backup came from.

The 13 drive apps are unchanged: own drive, mismatch check, ack still required.
A third refusal, with its own sentence, covers installed-but-no-resolvable-root.

Fixtures that marked an app "installed" by giving it an HDD path now state
deployment as its own fact. No assertion weakened.
This commit is contained in:
2026-08-22 13:08:46 +02:00
parent 2da259af38
commit 08eb1a6e3a
9 changed files with 541 additions and 25 deletions
+50
View File
@@ -1,3 +1,53 @@
## v0.219.0 — the off-site restore refused every app that has no data drive (2026-08-22, R-356)
**MinAgent: 0.129.0** (unchanged — no new agent coupling)
**What broke.** Forty of the fifty-three catalogue apps could not be restored from the off-site copy
at all. The customer opened the restore page, pressed the button, and was told the app **"nincs
telepítve"** — while it was running in front of them — and instructed to reinstall it "ugyanerre a
helyre", a place those apps never offer. The instruction could not be followed, so the restore never
started.
**Why.** `ReconstituteFromOffsite` and `PlaceOffsiteRestore` both resolved the restore destination
with the RAW `HDD_PATH` (`stackProvider.GetStackHDDPath`) and read an empty answer as "the app is not
installed". One predicate was answering two questions. Measured in the catalogue at `459766cb1639`:
**53 templates, 13 declare `needs_hdd: true`, 40 declare `false`** — and for those 40 the answer is
*correctly* empty, permanently. The capture side never had this defect: `CaptureRecoveryUnit` resolves
via `GetAppDrivePath`, which falls back to the system data path — which is why the 2026-08-21 refusal
was able to print `/mnt/sys_drive`, a destination the backup had recorded and the restore refused to
use.
**The fix separates the two questions.** *Installed?* is now asked directly, of
`ListDeployedStacks()`, through a new `Manager.isStackDeployed` that fails CLOSED on a nil provider —
"cannot tell" must not become "go ahead" when the caller's next act is a write. *Where?* is then
answered by `GetAppDrivePath`, the same resolver the capture side wrote the snapshot with, so the
restore aims at the place the backup came from.
**The 13-class behaviour is unchanged.** A drive app still resolves to its own drive, the placement
mismatch check still fires when the recorded drive differs, and `ackPlacementChange` is still required
to pass it. That claim is not an absence claim: the red-proof plants the system-data fallback into the
drive path, and three tests convict it.
**A third refusal now exists, with its own sentence.** Installed, driveless, and the box cannot name
its own data root ⇒ refuse and name the storage page. Widening `nincs telepítve` to cover this would
send a customer to reinstall a running app and hide the real fault.
**Why now.** v0.218.0 (R-354) taught the reconstitution to replay an app's named volumes. Those forty
apps keep **all** of their data in exactly those volumes, so that fix could not reach the apps that
need it most until this one shipped.
**Changed:** `internal/backup/backup.go` (`isStackDeployed`), `internal/backup/offbox_reconstitute.go`,
`internal/backup/offbox_restore.go`. **Not changed, deliberately:** `offboxCaptureSet`'s raw
`GetStackHDDPath` call — capture resolves an app's declared `userdata`/`import` file legs against that
value, and a system-data fallback there would write a snapshot claiming to hold files it does not.
**Tests:** `internal/backup/r356_hot_only_restore_test.go` (Scenarios A–E, both entry points, and the
positive/negative control on the deployment predicate) and `cmd/controller/r356_deployed_seam_test.go`
(AST walk over the production adapter wiring). Five companion red-proofs, each SEEN failing.
**Fixture correction in the same commit:** several existing fixtures marked an app "installed" by
giving it an HDD path. That is the conflation this change removes, so they now state deployment as its
own fact. No assertion was weakened.
## v0.218.0 — the database nobody backed up, and the restore that returned most apps nothing (2026-08-22, R-354/R-355)
**MinAgent: 0.129.0** (unchanged — no new agent coupling)