# REPORT — v0.204.0: the restore list is keyed on the store (R-237), and the size gate stops refusing in silence (R-238) 2026-08-06. Controller **v0.203.0 → v0.204.0**. MinAgent unchanged (**0.127.0**) — nothing here needs a new agent capability. `felhom-agent` untouched. ## R-238 — classified: a HARNESS ARTIFACT, with a real residue that is fixed The two runs diverge at one parameter, and the state at that point is quoted rather than inferred: - `POST /backup/offbox/restore` with `mode=full` and **no** `confirm=1` is **step 1 of a deliberate two-step** (`offbox_handlers.go:314`). It computes size + headroom via `OffboxRestorePrepareFull`, **starts no job**, and redirects to `restoreWizardPath(app) + "&full_prep=&full_size="`. - `deriveWizardStep` (`restore_wizard.go`) reveals the commit **only** when `in.FullPrepApp == in.App`, sourced from `?full_prep=`. - The endpoint-level driver posted step 1 and then re-fetched the wizard **without** that parameter. The pure function therefore returned the **intent** step — correctly. `restore-status.last == null` is likewise **correct**: step 1 starts no job by design. - The operator's browser run followed the redirect, saw the confirm, pressed „Igen" twice, and the restore completed. **So the button is not dead, and the wizard was not re-keyed.** The precedence rules exist so a stale `?full_prep=` can never resurrect a commit button mid-restore, and they were left alone. **The residue, which is real whoever triggers it:** neither branch of step 1 wrote anything to the log. `offboxRedirectTo` only flashes to the page. A customer refused a disaster restore — **including a refusal by the headroom gate** — left **no trace on the box at all**. Fixed: the refusal logs `[WARN] … full-restore preparation REFUSED for (no job started): `, the success logs `[INFO] … full-restore prepared for (size N) — awaiting the customer's confirm; no restore has started`, and the concurrent-op refusal logs too. ## R-237 — the restore list, rebuilt on the store `buildOffsiteRestoreRows` (new, pure) merges `OffsiteInventoryList` (the repository's own snapshot tags — the existing R-193 reader) with the installed set. `resolveOffsiteRestoreApp` replaces `resolveWizardApp`'s toggle requirement. Every §7 case, and the Hungarian as rendered: | case | rendered | |---|---| | snapshot present, app not installed | restore offered + „Nincs telepítve — a visszaállítás előbb újratelepíti." | | installed, no snapshot | „Nincs mentése a távoli tárolóban — nincs mit visszaállítani." | | store unreadable | „Nem tudjuk elolvasni a távoli tárolót, ezért **nem tudjuk, mi van benne**. Ez nem azt jelenti, hogy üres — próbáld újra később, vagy jelezd az üzemeltetőnek." + „Nem tudjuk, van-e mentése — a tárolót nem sikerült elolvasni." **and the action stays offered** | | no target yet | „A távoli tároló kapcsolódási adatai még nem érkeztek meg ehhez a géphez, ezért még nem tudjuk megmutatni, mi van benne. Ez magától rendeződik." | | store empty | „A távoli tároló üres — nincs mit visszaállítani." | | app under a different name | **not guessed** — it lists under the tag the store holds, and if nothing is installed under that name the row says so. No fuzzy matching. | Wizard refusals also changed: „Ehhez az alkalmazáshoz nincs mentés a távoli tárolóban." and, when the store could not be read, „Nem tudjuk elolvasni a távoli tárolót, ezért nem tudjuk, van-e benne mentés ehhez az alkalmazáshoz." Both log an INFO naming the app and the store state. `felhom-offbox` and `_shares` are excluded from the app list. ## Tests `go build` · `go vet` · `go test ./...` → **28 packages ok**. `controller_gates.py --fast` → **9/9 OK**. New: `offsite_restore_list_test.go` (7 tests — the truth table, the rebuilt box, unreadable-is-unknown, no-target, empty, and two rendered-page tests) and `offbox_restore_silence_test.go` (handler-level, because the silence was in the handler). ## Red-proofs — each mutation asserted to have applied before the result was trusted | # | mutation | result | |---|---|---| | RP-1 | store rows dropped from the builder — the list keyed back on installed apps (the original defect) | `TruthTable` + `RebuiltBox_SeesItsSnapshots` **FAIL** | | RP-2 | `state` forced to `known` **and** `StoreUnknown` forced false (both guards) | `UnreadableStoreIsUnknownNotEmpty` + `NoTargetIsItsOwnState` **FAIL** | | RP-3 | both size-gate log lines removed | `FullPrepareRefusal_IsNotSilent` **FAIL** — „wrote NOTHING to the log" | All three restored; suite green again afterwards. ## Files - new `internal/web/offsite_restore_list.go`, `internal/web/offsite_restore_list_test.go`, `internal/web/offbox_restore_silence_test.go` - `internal/web/restore_wizard.go` (gate on the store, not the toggle) - `internal/web/offbox_handlers.go` (three log lines) - `internal/web/handlers.go` (wire the rows) - `internal/web/templates/backups_restore.html` (the list + the state wording) - `internal/web/backups_split_test.go` (fixture: the new data contract) - `internal/backup/offbox_inventory.go` (`ErrNoOffsiteTargetSentinel`, so the no-target case is constructible from another package's table test) - `CHANGELOG.md`, `controller/README.md` ## Not done here, deliberately - **R-236 is diagnosed, not fixed** — the fix's shape depends on the diagnosis, and this arc has twice shipped a fix aimed at the wrong half of a defect. - The R-193 unlock listing still shows the `felhom-offbox` marker as if it were an app. Noticed while reusing its reader; **out of scope and not touched.**