docs(v0.229.0): R-102 + R-103 — CHANGELOG, CONTEXT rulings, README, REUSE, REPORT
gates / gates (push) Failing after 13s

CHANGELOG v0.229.0. CONTEXT records three rulings: the source moves and the destination does not;
two predicates and not one wider one (R-356's cost restated); and a destructive operation reached
from a non-destructive surface carries the difference in the CONFIRM, not the label. README documents
the new action and route and corrects the coverage note to the measured count. REUSE maps the four
unit-directory-relative primitives and the new manager methods, with the traps.

REPORT covers the live drill on demo-hp (docmost, class B, primary unit moved aside — 3 volumes of 3
and 1 database of 1 in 28.65 s, accented filename byte-identical verified as hex, the app reading its
own row over TCP; Scenario D with the guest app.yaml also aside, secrets recovered=2/2), the settled
count (A=7 B=45 C=1, and why the earlier 9/43/1 was wrong), the five named red-proofs, and seven
observations including R-403 and a process error of mine that changed the box and is now in memory.
This commit is contained in:
2026-08-31 12:21:28 +02:00
parent 4c8f0d2919
commit 8aa95b5831
5 changed files with 413 additions and 406 deletions
+40 -1
View File
@@ -7,7 +7,46 @@
>
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
Last updated: 2026-08-31 (v0.228.0 — R-399/R-400: the check reads the data, the debug page stops lying)
Last updated: 2026-08-31 (v0.229.0 — R-102/R-103: the second drive's copy becomes a way back)
> **2026-08-31 — v0.229.0. THREE RULINGS, recorded so none is re-litigated.**
>
> **1. The source moves; the destination does not.** `RestoreFromRecoveryUnitAt(stack, unitDir)` takes
> the recovery-unit DIRECTORY, so the same restore reads a unit from the primary drive or from the
> Tier-2 mirror on the second drive. What it must NEVER take is a destination: data still lands in the
> live Docker volumes and the live database container, and the definition in the guest, resolved by
> `GetAppDrivePath` exactly as the capture is. A restore that also relocated an app's data would be a
> migration wearing a restore's label, and the customer pressed a button that said neither.
>
> **2. Two predicates, never one wider one — and it is the SECOND time this is written down.**
> `Tier2Coverage.CanRestore()` answers *"can the additive file restore run?"* and nothing else;
> `CanRestoreUnit()` answers *"can the unit restore open this copy?"*. `HasUnit` keeps its third,
> distinct meaning: *"is there captured data the file restore is not looking at?"* — true even for a
> half-copied mirror the unit restore refuses, because the disclosure is still owed. The temptation is
> always to widen the predicate already there. **R-356 is what that costs:** one predicate meaning both
> *"has this app a drive?"* and *"is this app installed?"* refused 40 running apps for months, while
> they were running, with a message telling their owners to reinstall them somewhere those apps never
> offer.
>
> **3. A destructive operation reached from a non-destructive surface must carry the difference in the
> CONFIRM, not in the label.** „Teljes visszaállítás a másolatból" sits beside „Fájlok
> visszaállítása" on the same row; one overwrites the app's database and internal volumes, the other
> only adds files that are missing and never overwrites anything. The confirm says exactly that, names
> the copy's date, and says so DIFFERENTLY when that date is only an attempt clock (R-101). It is built
> from named Go constants (`tier2UnitConfirmBase` / `…DateFmt` / `…DateUnprovenFmt` / `…Contrast`) and
> asserted verbatim, because a sentence assembled inside an HTML attribute cannot be pinned and R-364
> makes grepping accented Hungarian out of rendered markup unreliable on top of that.
>
> **The count is settled and must not be re-derived.** At catalogue `459766cb1639`, by the production
> rule: **A = 7 · B = 45 · C = 1**. The C9-F1 Phase-0 count (9/43/1) was wrong by two — **radarr and
> sonarr**, whose `${USERDATA_PATH}` binds are WRITABLE (so the `:ro` default rule Phase 0 applied does
> not catch them) and are excluded by an explicit `class: excluded` entry instead. C is **bentopdf**.
>
> **R-403, filed and NOT fixed.** Two seconds after a restore that ran with the primary unit absent, the
> 5-minute status refresh (`captureAllRecoveryUnits`) rewrote the primary unit from a drive with no
> dumps, yielding `"db_dumps": []` / `"volume_dumps": null`. Measured on demo-hp 2026-08-31. The
> dangerous half — that the next Tier-2 run would mirror that hollow unit over the good secondary copy,
> `rsyncMirror` carrying `--delete` — **was not tested and is recorded as unverified.**
> **2026-08-31 — v0.228.0. TWO RULINGS, recorded so neither is re-litigated.**
>