v0.154.0 — R-48: one restore entry per app, and the intent is a described choice
The offsite restore list rendered up to five inline forms per app row. Two of them —
„Helyreállítás az élő adatok közé (csak a hiányzó fájlok)" and „Teljes visszaállítás
(fájlok + adatbázis)" — were sibling buttons whose difference is whether the customer's
data comes back at all. That mis-selection CAUSED the round-2 incident: an operator who
had read the source pressed the missing-only button and /backup/offbox/reconstitute was
never hit (DIAG-immich-restore-round2-2026-07-19, finding 1).
The rule this establishes: two adjacent controls whose difference is "your data comes
back" vs "your data cannot come back" must never be distinguishable only by layout.
Each row now carries ONE „Visszaállítás…" entry linking to a per-app wizard at
GET /backups/restore/app?name=<app>: three intent CARDS with consequence sentences, a
visible phase strip, danger styling plus the R-43 double-confirm carried over verbatim on
the destructive one, and the pair-honesty panel bound to real OffsiteScratchPair facts.
deriveWizardStep is pure over (op running, size-gate flash, scratch ready); the step is
never accepted from the request, and a running op outranks a stale ?full_prep= so no
commit button survives into a restore. While ANY op runs every mutation form is suppressed
server-side instead of being offered and refused.
No new mutation endpoint: every card posts to the pre-existing /backup/offbox/* handler
with the same field names and gates, and the page works with JavaScript disabled.
internal/{backup,appbackup,selfupdate} untouched. R-45's job registry stays its own item.
Fixes a latent bug found on the way: offboxRedirectTo hardcoded "?" when appending its
flash, which against the wizard's ?name=<app> target would have buried the flash inside
the name value.
No new agent coupling — MinAgent stays 0.90.0.
This commit is contained in:
@@ -1,5 +1,61 @@
|
||||
## Changelog
|
||||
|
||||
### v0.154.0 — one restore entry per app, and the intent is a described choice (2026-07-21)
|
||||
|
||||
Closes **R-48**. **No new agent coupling — MinAgent stays 0.90.0.** This is a UI-layer change:
|
||||
`internal/backup`, `internal/appbackup` and `internal/selfupdate` are untouched, and the release adds
|
||||
**no mutation endpoint** — every action still posts to the `/backup/offbox/*` handler it always did,
|
||||
with the same field names and the same gates.
|
||||
|
||||
**The defect.** The „Ellenőrző visszaállítás a távoli tárolóból" list rendered up to five inline
|
||||
`<form style="display:inline">` blocks per app row: verify, prepare, the revealed size-gated commit,
|
||||
the missing-only merge, and the true reconstitution. Two of them sat next to each other as sibling
|
||||
buttons —
|
||||
|
||||
- „Helyreállítás az élő adatok közé (csak a hiányzó fájlok)" — additive; **cannot** bring deleted
|
||||
content back, and
|
||||
- „Teljes visszaállítás (fájlok + adatbázis)" — the real restore
|
||||
|
||||
— and the difference between them is whether the customer's data comes back at all. This is not
|
||||
theoretical: it caused the round-2 incident. An operator who had *read the source* pressed the
|
||||
missing-only button, and the controller log shows `/backup/offbox/reconstitute` was never hit
|
||||
(`felhom.eu/documentation/audits/DIAG-immich-restore-round2-2026-07-19.md`, finding 1). The second
|
||||
half of the trap was that the decisive „Teljes visszaállítás indítása" appeared **only after**
|
||||
„…előkészítése" had been pressed, with nothing signposting that a second step existed or that the
|
||||
first one had done nothing to live data.
|
||||
|
||||
**The rule this establishes,** worth stating once and applying past this page: *two adjacent controls
|
||||
whose difference is "your data comes back" vs "your data cannot come back" must never be
|
||||
distinguishable only by layout.*
|
||||
|
||||
**The change.** Each app row on `/backups/restore` now carries exactly **one** control —
|
||||
„Visszaállítás…" — linking to a per-app wizard at `GET /backups/restore/app?name=<app>`, built on the
|
||||
`backups_escrow.html` precedent:
|
||||
|
||||
- **Three intent CARDS**, each with its own consequence sentence rather than a label alone:
|
||||
ellenőrzés külön mappába (live data untouched) · hiányzó fájlok visszahozása (additive, no
|
||||
database, deleted content does not reappear) · teljes visszaállítás (files + database, danger
|
||||
styling, the R-43 double-confirm carried over **verbatim** with its pair-honesty facts).
|
||||
- **A visible phase strip** — Előkészítés · Megerősítés · Végrehajtás · Eredmény — so the sequence is
|
||||
legible before the first click instead of after it.
|
||||
- **Server-derived steps.** `deriveWizardStep` is a pure function of (op running, size-gate flash,
|
||||
scratch ready); the step is never accepted from the request. Precedence is strict: a running op
|
||||
outranks a stale `?full_prep=` in the URL, so no commit button can reappear mid-restore.
|
||||
- **Mutation forms are suppressed server-side while any op runs** — the backup manager's
|
||||
single-flight is process-wide, so a restore for app X now suppresses app Y's controls instead of
|
||||
offering a button guaranteed to 409.
|
||||
- **No JavaScript requirement.** Every step is a real form POST and the server renders the next one.
|
||||
|
||||
**Redirect retargeting.** The app-scoped `/backup/offbox/{restore,place,reconstitute}` outcomes now
|
||||
land back on the wizard the customer acted from rather than on the list. Fixing that surfaced a
|
||||
latent bug in `offboxRedirectTo`, which hardcoded `"?"` when appending the flash — against a target
|
||||
that already carries a query (`?name=<app>`) that would have buried the flash inside the `name`
|
||||
value. The separator is now chosen.
|
||||
|
||||
**Deliberately NOT in scope:** the shares (`_shares`) entry, the local restore panel and the .fab
|
||||
block are untouched; the R-45 job registry is still its own item — the wizard polls the two existing
|
||||
status surfaces as-is.
|
||||
|
||||
### v0.153.0 — the database replay no longer races the application, on BOTH restore paths (2026-07-20)
|
||||
|
||||
Closes **R-47**. **No new agent coupling — MinAgent stays 0.90.0.** Nothing in this release talks to
|
||||
|
||||
Reference in New Issue
Block a user