diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c1a366..daa80273 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,10 @@ unchanged. Design: felhom.eu `07` §6.3 / §8.2a. /backup/kept/restore`, by its record only — path inside its tree, same device + inode) beside „Eltávolítás". Remove with „delete its backups too" deletes the app's set-aside units too (the D25 / R-1252 H9 rule). - A put-back writes the live install's identity back (the live definition copy carries it). +- **Found live on 9202, fixed without a row: the „Megőrzött másolatok" list was inside the off-site card** of the + restore page, so a box without off-site never listed a kept copy (D25's second-drive copies included). The section is + one named template now, shown in the off-site card when the box has off-site and after it when not + (`TestD47_KeptCopiesAreListedWithoutOffsite`, seen failing; the parity fixtures are unchanged). - Tests (each seen failing with the guard off — `audits/restore-truth-2026-10-25/redproof/`): `TestD47_TheSweepOrderNeverRewritesTheKeptUnit`, `…AUnitFromBeforeD47IsSetAsideWhenItsPasswordDiffers`, `…TheSetAsideUnitRestoresWithItsOwnPassword`, `…TheWriterRefusesWhenTheUnitCannotBeSetAside`, diff --git a/CONTEXT.md b/CONTEXT.md index 280e4647..27d346d0 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1,5 +1,15 @@ # CONTEXT.md — Project Memory +> **2026-10-25 (Sunday, unattended) — D47, D48, R-1270, R-1271 on `main`, NOT released (next v0.343.0, MinAgent 0.158.1 +> unchanged).** D47 (`02daebe`): `stacks.AppConfig.InstallID` (minted at install/import, taken from the unit by a +> restore, `EnsureInstallID` for older apps); `backup/unit_owner.go` `guardUnitWrite`/`guardUnitDir` in every unit writer +> (capture, refresh, dumps, update pre-backup, volume dump, second-drive unit copy, stale-drive prune) sets another +> install's unit aside whole (`backups/set-aside//`, a kept copy kind `unit`, restore page „Visszaállítás"). +> R-1270 (`b680db8`): `ErrRestoreNotStarted` from every restore path on a health timeout; undo copy kept; late-start +> watcher. D48 (`2d71270`): `prepareRestoreImages` before the stop on every restore path under +> `dockerexec.HoldImageWork`. R-1271 (`bd9f4f7`): `keptPick`/`KeptNewerAt` — a copy more than 10 min in the future is +> never the newest. Live proofs on 9202 with `0.343.0-rc.1`: felhom.eu `audits/restore-truth-2026-10-25/`. + > **2026-10-24 night (unattended restore-holes run) — on `main`, NOT released (next v0.343.0, MinAgent 0.158.1 unchanged).** > R-1251: a pending put-back blocks every start (`backup/appstop_putback_wait.go`: restore hold `putback_pending`, the > hold store every start path honours; `ResumePutBack` in the boot recreate and `restartStacks`; `ResumeWaitingPutBacks` diff --git a/controller/internal/web/kept_copies_test.go b/controller/internal/web/kept_copies_test.go index 9b97b00a..9d9012dd 100644 --- a/controller/internal/web/kept_copies_test.go +++ b/controller/internal/web/kept_copies_test.go @@ -163,3 +163,22 @@ func TestD47_KeptUnitRestoreHandlerRefusals(t *testing.T) { t.Fatal("a refusal must start nothing") } } + +// D25 + D47 (2026-10-25, found live on 9202): a box WITHOUT off-site lists its kept copies too. Before, the section sat +// only inside the off-site card, so 9202 (no off-site) never showed a set-aside unit or a kept second-drive copy. +func TestD47_KeptCopiesAreListedWithoutOffsite(t *testing.T) { + d := i18nLayoutData("backups-restore", "Visszaállítás a mentésből") + d["Backup"] = i18nBackupStatus(false, false) + d["OffboxConfigured"] = false + d["KeptCopies"] = []keptCopyRow{{ID: "aaaaaaaaaaaaaaaa", Source: "tandoor", Line: "belso", SizeHuman: "150 MB", Since: "2026-10-25T08:00:00Z", Restorable: true}} + d["KeptUnitRestorable"] = true + html := renderBackupPage(t, "backups_restore", d) + if strings.Count(html, "Megőrzött másolatok") != 1 || strings.Count(html, `action="/backup/kept/restore"`) != 1 { + t.Fatal("a box without off-site must list its kept copies once, with the restore") + } + d["OffboxConfigured"] = true + d["OffsiteStoreState"] = "known" + if html := renderBackupPage(t, "backups_restore", d); strings.Count(html, "Megőrzött másolatok") != 1 { + t.Fatal("with off-site the section is shown once (inside the off-site card), never twice") + } +} diff --git a/controller/internal/web/templates/backups_restore.html b/controller/internal/web/templates/backups_restore.html index 3fde7d9a..df2bc019 100644 --- a/controller/internal/web/templates/backups_restore.html +++ b/controller/internal/web/templates/backups_restore.html @@ -167,45 +167,7 @@ {{/* D25 (operator ruling 2026-10-21; R-1175, R-1181): second-drive copies whose source is gone (a drive away or removed, a folder missing). No night job removes them; only „Eltávolítás" here, after a confirm. On one line with the comment above so a page without kept copies renders byte-identically. */}}{{if .KeptCopies}} -
-

{{T "backups_restore.megorzott_masolatok"}}

-

{{T "backups_restore.megorzott_masolatok_hint"}}

-
- {{range .KeptCopies}} - {{template "app_list_row" dict "Slug" "" "Name" .Source "Secondary" (printf "%s · %s · %s" .Line (numText .SizeHuman) (fmtTimeStr .Since))}}{{if .Restorable}} -
- {{$.CSRFField}} - - - -
{{end}} -
- {{$.CSRFField}} - - - -
- {{template "app_list_row_end"}} - {{end}} -
- - {{end}} + so the copy can be found, opened, and cleaned up. -->{{/* D25 (operator ruling 2026-10-21; R-1175, R-1181): second-drive copies whose source is gone (a drive away or removed, a folder missing). No night job removes them; only „Eltávolítás" here, after a confirm. On one line with the comment above so a page without kept copies renders byte-identically. */}}{{template "kept_copies_section" .}}

{{T "backups_restore.meglevo_ellenorzo_masolatok"}}

{{if .OffsiteRestoreCopies}} @@ -228,7 +190,7 @@

{{T "backups_restore.nincs_ellenorzo_masolat_a_meghajton"}}

{{end}} -{{end}} +{{end}}{{if not .OffboxConfigured}}{{template "kept_copies_section" .}}{{end}}