D47 follow-up (found live on 9202): the kept-copies list („Megőrzött másolatok”) shows on a box without off-site too — it sat only inside the off-site card, so 9202 never listed a set-aside unit or a D25 copy; one named template, two call sites, parity fixtures unchanged. Red-proved. Commit only.
gates / gates (push) Successful in 1m26s
gates / gates (push) Successful in 1m26s
This commit is contained in:
@@ -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`,
|
||||
|
||||
+10
@@ -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/<app>/<stamp>`, 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`
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,45 +167,7 @@
|
||||
<!-- v0.147.0 (4a): what the verification restores actually LEFT on disk. Until now the result of
|
||||
an ellenőrző visszaállítás was invisible — the flash said "a verification folder on the
|
||||
drive" without naming it, and nothing listed what had accumulated. Full path, size and date,
|
||||
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. */}}{{if .KeptCopies}}
|
||||
<div class="backup-tier-divider" style="margin-top:1.5rem"></div>
|
||||
<h3 style="margin-bottom:.5rem">{{T "backups_restore.megorzott_masolatok"}}</h3>
|
||||
<p class="form-hint" style="margin-bottom:.75rem">{{T "backups_restore.megorzott_masolatok_hint"}}</p>
|
||||
<div class="app-row-list">
|
||||
{{range .KeptCopies}}
|
||||
{{template "app_list_row" dict "Slug" "" "Name" .Source "Secondary" (printf "%s · %s · %s" .Line (numText .SizeHuman) (fmtTimeStr .Since))}}{{if .Restorable}}
|
||||
<form method="POST" action="/backup/kept/restore" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="id" value="{{.ID}}">
|
||||
<input type="hidden" name="confirm" value="1">
|
||||
<button type="button" class="btn btn-xs btn-outline"
|
||||
onclick="confirmRestoreKeptUnit(this)">{{T "backups_restore.visszaallitas"}}</button>
|
||||
</form>{{end}}
|
||||
<form method="POST" action="/backup/kept/remove" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="id" value="{{.ID}}">
|
||||
<input type="hidden" name="confirm" value="1">
|
||||
<button type="button" class="btn btn-xs btn-danger-outline"
|
||||
onclick="confirmRemoveKeptCopy(this)">{{T "backups_restore.eltavolitas"}}</button>
|
||||
</form>
|
||||
{{template "app_list_row_end"}}
|
||||
{{end}}
|
||||
</div>
|
||||
<script>
|
||||
{{if .KeptUnitRestorable}}function confirmRestoreKeptUnit(btn){
|
||||
felhomConfirm(btn, '{{T "backups_restore.megorzott_visszaallitas_megerosites"}}', function(){
|
||||
var f = btn.closest('form');
|
||||
if (f) { if (f.requestSubmit) f.requestSubmit(); else f.submit(); }
|
||||
});
|
||||
}
|
||||
{{end}}function confirmRemoveKeptCopy(btn){
|
||||
felhomConfirm(btn, '{{T "backups_restore.megorzott_eltavolitas_megerosites"}}', function(){
|
||||
var f = btn.closest('form');
|
||||
if (f) { if (f.requestSubmit) f.requestSubmit(); else f.submit(); }
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{{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" .}}
|
||||
<div class="backup-tier-divider" style="margin-top:1.5rem"></div>
|
||||
<h3 style="margin-bottom:.5rem">{{T "backups_restore.meglevo_ellenorzo_masolatok"}}</h3>
|
||||
{{if .OffsiteRestoreCopies}}
|
||||
@@ -228,7 +190,7 @@
|
||||
<p class="form-hint">{{T "backups_restore.nincs_ellenorzo_masolat_a_meghajton"}}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}{{if not .OffboxConfigured}}{{template "kept_copies_section" .}}{{end}}
|
||||
|
||||
|
||||
<!-- Hordozható mentéscsomag (.fab) — v0.124.0, decision 3: PORTABILITY, not a backup tier (no
|
||||
@@ -464,3 +426,45 @@ function submitRestore() {
|
||||
|
||||
{{template "layout_end" .}}
|
||||
{{end}}
|
||||
{{/* D25 + D47: the kept copies (second-drive copies whose source is gone; set-aside units of an earlier install). Shown
|
||||
inside the off-site card when the box has off-site, and after it when it has none — before 2026-10-25 it sat only inside
|
||||
the off-site block, so a box without off-site never listed its kept copies. One body, two call sites. */}}
|
||||
{{define "kept_copies_section"}}{{if .KeptCopies}}
|
||||
<div class="backup-tier-divider" style="margin-top:1.5rem"></div>
|
||||
<h3 style="margin-bottom:.5rem">{{T "backups_restore.megorzott_masolatok"}}</h3>
|
||||
<p class="form-hint" style="margin-bottom:.75rem">{{T "backups_restore.megorzott_masolatok_hint"}}</p>
|
||||
<div class="app-row-list">
|
||||
{{range .KeptCopies}}
|
||||
{{template "app_list_row" dict "Slug" "" "Name" .Source "Secondary" (printf "%s · %s · %s" .Line (numText .SizeHuman) (fmtTimeStr .Since))}}{{if .Restorable}}
|
||||
<form method="POST" action="/backup/kept/restore" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="id" value="{{.ID}}">
|
||||
<input type="hidden" name="confirm" value="1">
|
||||
<button type="button" class="btn btn-xs btn-outline"
|
||||
onclick="confirmRestoreKeptUnit(this)">{{T "backups_restore.visszaallitas"}}</button>
|
||||
</form>{{end}}
|
||||
<form method="POST" action="/backup/kept/remove" style="display:inline">
|
||||
{{$.CSRFField}}
|
||||
<input type="hidden" name="id" value="{{.ID}}">
|
||||
<input type="hidden" name="confirm" value="1">
|
||||
<button type="button" class="btn btn-xs btn-danger-outline"
|
||||
onclick="confirmRemoveKeptCopy(this)">{{T "backups_restore.eltavolitas"}}</button>
|
||||
</form>
|
||||
{{template "app_list_row_end"}}
|
||||
{{end}}
|
||||
</div>
|
||||
<script>
|
||||
{{if .KeptUnitRestorable}}function confirmRestoreKeptUnit(btn){
|
||||
felhomConfirm(btn, '{{T "backups_restore.megorzott_visszaallitas_megerosites"}}', function(){
|
||||
var f = btn.closest('form');
|
||||
if (f) { if (f.requestSubmit) f.requestSubmit(); else f.submit(); }
|
||||
});
|
||||
}
|
||||
{{end}}function confirmRemoveKeptCopy(btn){
|
||||
felhomConfirm(btn, '{{T "backups_restore.megorzott_eltavolitas_megerosites"}}', function(){
|
||||
var f = btn.closest('form');
|
||||
if (f) { if (f.requestSubmit) f.requestSubmit(); else f.submit(); }
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{{end}}{{end}}
|
||||
|
||||
Reference in New Issue
Block a user