v0.205.0 — a run that skipped an app the customer selected is not successful (R-234)
gates / gates (push) Successful in 21s

THE VERDICT. The R-203 block already said "a warning beside a success is read as a
success" and applied it to ONE of the two shapes it describes: an app missing a
declared mandatory FOLDER made the run incomplete, while an app skipped ENTIRELY
still reported ok. Both do now. Which skips count, decided by measurement:
selected+deployed with no recovery unit YES; selected but NOT deployed no (named,
with what to do — a box left amber by an app somebody removed is a status nobody
reads); disconnected/decommissioned drive no (own signal); nothing selected no.
LastSuccess and SnapshotCount still record what WAS captured.

THE FILED MECHANISM WAS NOT THE MEASURED CAUSE, and saying so is the point. §3
stated that toggling an app on leaves it without a bundle so the first run skips
it. Measured on demo-hp: the run's own pre-dump phase calls captureAllRecoveryUnits
for every DEPLOYED stack, through admitApp, before the push — a unit moved aside
was RECREATED and the run reported ok. That state does not survive a run.

What actually produced the 2026-08-06 sequence: the manual run was dropped by the
single-flight while an earlier run was still going. runOffboxBackup returned nil,
the handler had already answered "A tavoli mentes elindult", and the card then
showed the PREVIOUS run's green verdict — read as covering the app just selected.
The decision is now taken synchronously in the handler and a dropped request says
so. The nightly path still returns nil on purpose: nobody asked, and it retries.

§7.3 measured before deciding: CaptureRecoveryUnit writes a few KB of compose +
manifest, only ENUMERATES dumps rather than creating them, is idempotent and does
NOT stop the app — and already runs inside the off-site run. So there is no wait to
remove for a deployed app and NOTHING was built.

28 packages ok, 9/9 gates. Four red-proofs, each asserted to have applied. Fixture
note: the shared provider's ListDeployedStacks returned nil, so Scenario A first
passed for the wrong reason; fixed with an opt-in deployed set that defaults to nil.
This commit is contained in:
2026-08-06 21:58:21 +02:00
parent 53e9bf0224
commit c6b69d888e
11 changed files with 578 additions and 84 deletions
+55
View File
@@ -1,3 +1,58 @@
## v0.205.0 — a backup that skipped an app the customer chose is not „Rendben" (2026-08-06, R-234) — MinAgent 0.127.0
**Two defects, and the one that actually produced the measured sequence was NOT the one filed.**
### The verdict now counts a skipped selection
The R-203 verdict block already carries the sentence *"a warning beside a success is read as a
success"* — and applied it to **one of the two shapes it describes**. An app missing a declared
mandatory FOLDER made the run `incomplete`; an app skipped **entirely**, with nothing of it in the
snapshot at all, still reported `ok` with a warning beside it. The smaller gap moved the verdict and
the bigger one did not. It does now.
**Which skips count (§7.2), decided by measurement rather than assumption:**
| skip | counts? | why |
|---|---|---|
| selected + **deployed**, no recovery unit | **yes** | the app the customer chose is not protected |
| selected but **not deployed** | **no**, but NAMED with what to do | a box left amber forever by an app somebody removed is a status nobody reads |
| drive disconnected / decommissioned | **no** | it has its own card and its own signal |
| nothing selected at all | **no** | unchanged: the existing zero-selection notice |
The operator signal is **reused, not mirrored** — a skipped app is reported through the existing
mandatory-gap notification as a whole-unit gap, so one vocabulary covers both.
`LastSuccess` and `SnapshotCount` still record what WAS captured: half a backup is not no backup.
### The measured cause: a manual run silently dropped by the single-flight
Reproduced on demo-hp: **the pre-dump phase (`captureAllRecoveryUnits`) writes a unit for every
deployed stack before the push**, so "selected but no bundle yet" does not normally survive a run —
moving a unit aside and running recreated it and reported `ok`. So the filed mechanism could not have
produced the 2026-08-06 sequence.
What did: the customer pressed „Távoli mentés most”, the handler answered „A távoli mentés elindult”, `acquireRunning` refused because a run was already going, and the run
returned **nil** — no error, no signal. The card then showed the **previous** run's „Rendben", which
reads as covering the app just selected. It did not, and the restore refused minutes later.
The single-flight decision is now taken **synchronously in the handler**, before the goroutine, and a
dropped request says so. The nightly path is deliberately unchanged: returning nil is right for it —
nobody asked, and the next scheduled run retries.
### §7.3 — the wait, measured before deciding
`CaptureRecoveryUnit` writes compose config + a manifest (**a few KB**, per `admission.go`'s own
note), **enumerates** dumps already present rather than creating them, is idempotent, and **does not
stop the app**. It already runs for every deployed stack inside the off-site run's own pre-dump phase,
through `admitApp`. **So the inline capture this task contemplated already exists — nothing was built**,
and for a deployed app there is no wait to remove.
### Hungarian
- „Ezek az alkalmazások NEM kerültek be a távoli mentésbe, mert még nincs helyi mentési egységük: %s. A következő mentés általában már elkészíti — ha a második futás után is itt szerepelnek, szólj az üzemeltetőnek.”
- „Ezek az alkalmazások ki vannak jelölve távoli mentésre, de nincsenek telepítve, ezért nem menthetők: %s. Ha már nincs rájuk szükséged, vedd ki a kijelölésüket a Távoli mentés oldalon.”
- „Már fut egy távoli mentés — ez a kérés nem indított újat. A most látható eredmény még a korábbi futásé; várd meg, míg ez befejeződik.”
- sibling (mandatory folders), extended so both read alike: „… Ellenőrizd, hogy a mappák megvannak-e a meghajtón; ha igen és ez a következő mentés után is látszik, szólj az üzemeltetőnek.”
## v0.204.0 — what you can restore is decided by the store, not by what happens to be installed (2026-08-06, R-237 / R-238) — MinAgent 0.127.0
**A household that had just lost its box was shown nothing to restore.** Measured live on the R-201