v0.155.0 — the restore wizard read the wrong "is something running" flag
Fixes a defect shipped in v0.154.0, found by the operator on the first live click-through of the new wizard. backup.Manager carries TWO running booleans. `running` (read by IsRunning) is the concurrency single-flight, acquired inside the background goroutine — and RestoreOffboxScratch never acquires it at all. `opRunning` (read by RestoreStatus) is the display flag, set synchronously by BeginRestoreOp in the handler. The wizard sourced OpRunning from IsRunning(), so for „Ellenőrzés" and the full-restore preparation — its two most-used and longest actions, both streaming from restic — the execution step was unreachable: the page offered all three intents with live buttons while a restore was running, and the progress banner contradicted the phase strip on the same screen. Pressing anything there would have been refused by the handler, which is the exact "offering a control guaranteed to fail" dishonesty R-48 exists to remove. Fix: restoreOpInFlight(st) behind a documented seam, fed by a SINGLE RestoreStatus() read per render so the strip, the suppression decision and the running-op name cannot diverge. Why the tests missed it: the Scenario-E table proved deriveWizardStep behaves correctly GIVEN OpRunning=true, but nothing proved the handler ever computes true — hollow at exactly that seam. TestRestoreOpInFlight_UsesDisplayFlagNotConcurrencyFlag now drives a real Manager through BeginRestoreOp and asserts the render suppresses every form. Red-proofed against the v0.154.0 shape. Also: „Eredmény" was a dead label. The strip's highlight is now its own derived Phase, separate from Step — a finished restore returns to the intent step (everything available again) while the strip reads „Eredmény" and an outcome card shows the result. Bounded by restoreResultWindow (10 min) so a stale result cannot look fresh, and bound to the app so a finished bookstack restore does not light immich's page with bookstack's message. The card survives a reload; the redirect flash does not. No new agent coupling — MinAgent stays 0.90.0.
This commit is contained in:
@@ -1,5 +1,43 @@
|
||||
## Changelog
|
||||
|
||||
### v0.155.0 — the restore wizard read the wrong "is something running" flag (2026-07-21)
|
||||
|
||||
**No new agent coupling — MinAgent stays 0.90.0.** Fixes a defect shipped in v0.154.0 and found by
|
||||
the operator on the first live click-through, plus the dead phase-strip label from the same release.
|
||||
|
||||
**The bug.** `backup.Manager` carries two different booleans and v0.154.0 read the wrong one:
|
||||
|
||||
| flag | read by | set by | covers the verification restore? |
|
||||
|---|---|---|---|
|
||||
| `running` | `IsRunning()` | `acquireRunning()`, **inside** the goroutine | **no — `RestoreOffboxScratch` never acquires it at all** |
|
||||
| `opRunning` | `RestoreStatus()` | `BeginRestoreOp()`, in the handler, synchronously | yes, all four offsite actions |
|
||||
|
||||
The wizard sourced `OpRunning` from `IsRunning()`. For „Ellenőrzés" and the full-restore preparation
|
||||
— the wizard's two most-used actions, and the long ones, since they stream from restic — that flag is
|
||||
false for the *entire* operation. So the execution step was unreachable: the page kept offering all
|
||||
three intents with live buttons while a restore was downloading, and the progress banner (which polls
|
||||
the op status) contradicted the phase strip on the same screen. Any button pressed there would have
|
||||
been refused by the handler — which is exactly the "offering a control guaranteed to fail" dishonesty
|
||||
R-48 exists to remove.
|
||||
|
||||
**The fix** is one line of behaviour behind a named seam: `restoreOpInFlight(st)` takes the
|
||||
`RestoreOpStatus` the handler already reads once, and its doc comment states which flag is which and
|
||||
why. The handler now takes a single `RestoreStatus()` read, so the strip, the suppression decision
|
||||
and the running-op name can no longer disagree with each other.
|
||||
|
||||
**Why the v0.154.0 tests missed it.** The Scenario-E table proved `deriveWizardStep` behaves
|
||||
correctly *given* `OpRunning=true`; nothing proved the handler ever computes `true`. Hollow at exactly
|
||||
that seam. `TestRestoreOpInFlight_UsesDisplayFlagNotConcurrencyFlag` now drives a real `Manager`
|
||||
through `BeginRestoreOp` and asserts the wizard suppresses every form — red-proofed against the
|
||||
v0.154.0 shape.
|
||||
|
||||
**„Eredmény" is now reachable.** The fourth phase label never lit up in v0.154.0. The strip's
|
||||
highlight is now its own derived value (`Phase`), separate from `Step`: a finished restore is back on
|
||||
the intent step — everything is available again — while the strip rightly reads „Eredmény" and an
|
||||
outcome card shows the result. Bounded by `restoreResultWindow` (10 min) so a stale result cannot
|
||||
claim to be fresh, and bound to the app, so a finished bookstack restore does not light up immich's
|
||||
page with bookstack's message. The card survives a reload, which the redirect flash does not.
|
||||
|
||||
### 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:
|
||||
|
||||
Reference in New Issue
Block a user