R-1182: a finished off-site abandonment closes on the hub's abandon_purge_done (hub v0.163.0 keeps other copies' packages); unreleased
gates / gates (push) Successful in 1m18s
gates / gates (push) Successful in 1m18s
This commit is contained in:
@@ -1137,7 +1137,14 @@ func main() {
|
||||
// on every ACK and discarded. Wired at the same point as the two above, deliberately —
|
||||
// a second wiring site in this file is how six features got built and never reached.
|
||||
RecordEscrowKeyHash: sett.SetHubEscrowKeySHA256,
|
||||
Logger: logger,
|
||||
// R-1182 (hub v0.163.0): the hub finished this box's abandonment purge while it rightly keeps the
|
||||
// packages of OTHER set-aside copies — close the declaration (superseded_present may stay true).
|
||||
AbandonPurgeDone: func() {
|
||||
if backupMgr != nil {
|
||||
backupMgr.ClearAbandonPurgeIfConfirmed(false)
|
||||
}
|
||||
},
|
||||
Logger: logger,
|
||||
}
|
||||
// R-314/R-279/R-177 (`09` §3 decision 185): the operator's closed list of actions, carried in the
|
||||
// report reply and run in THIS process (opactions_wiring.go). The result rides the next report;
|
||||
|
||||
@@ -36,6 +36,11 @@ type EscrowStatus struct {
|
||||
// package" and simply keeps today's message: an older hub cannot make the screen say anything new.
|
||||
SupersededPresent bool `json:"superseded_present"`
|
||||
SupersededAt string `json:"superseded_at"`
|
||||
// AbandonPurgeDone (R-1182, hub v0.163.0) — the hub answers THIS box's abandonment declaration: the packages that
|
||||
// open the deleted set-aside copy are gone. The hub now keeps the packages of OTHER stored copies, so
|
||||
// SupersededPresent can stay true for good after a finished abandonment; this field is what closes the
|
||||
// declaration then. Absent on an older hub → false → the old rule (SupersededPresent=false) still closes it.
|
||||
AbandonPurgeDone bool `json:"abandon_purge_done"`
|
||||
}
|
||||
|
||||
// EscrowAutoConfirmer runs the auto-confirm check on each report ACK. Long-lived (one per process) so
|
||||
@@ -70,6 +75,10 @@ type EscrowAutoConfirmer struct {
|
||||
// immediately, so the one fact that distinguishes a REBUILT box from a box that never had
|
||||
// off-site backups was thrown away on every cycle. nil → not recorded (older wiring, tests).
|
||||
RecordPresence func(present bool) error
|
||||
// AbandonPurgeDone is called when the ACK says the hub finished THIS box's abandonment purge (R-1182) — the box
|
||||
// closes its declaration. Wired at the same point as RecordPresence, for the reason given there. nil → the
|
||||
// declaration closes only on SupersededPresent=false (the pre-R-1182 rule).
|
||||
AbandonPurgeDone func()
|
||||
// RecordSuperseded persists the ACK's `superseded_present` / `superseded_at` (v0.201.0, R-222) —
|
||||
// whether the hub is ALSO keeping an EARLIER sealed package for this box. Wired here for the same
|
||||
// reason as RecordPresence: this is the one place the ACK's escrow object already arrives, and a
|
||||
@@ -157,6 +166,10 @@ func (c *EscrowAutoConfirmer) Reconcile(es *EscrowStatus) {
|
||||
c.logf("[WARN] [escrow-confirm] could not record the hub's superseded-package state (present=%v): %v", es.SupersededPresent, err)
|
||||
}
|
||||
}
|
||||
// R-1182: the hub confirmed this box's abandonment purge. Same place and discipline as the two recorders above.
|
||||
if es.AbandonPurgeDone && c.AbandonPurgeDone != nil {
|
||||
c.AbandonPurgeDone()
|
||||
}
|
||||
// R-241: persist the hash the hub's package covers, with the moment we heard it. Same discipline,
|
||||
// same place, same reason as the two above — and this one is the fact the recovery screen has been
|
||||
// unable to see. A record failure is logged, never swallowed, and never blocks the auto-confirm.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-1182 (hub v0.163.0): the hub now keeps the packages of OTHER set-aside copies after an abandonment, so
|
||||
// superseded_present can stay true for good; `abandon_purge_done` is what closes this box's declaration. The hook
|
||||
// runs on an unconfigured box too (the declaring box has deleted its set-aside copy; its target may be anything).
|
||||
// COMPANION RED-PROOF: drop the AbandonPurgeDone call in Reconcile → the first assertion fails.
|
||||
func TestR1182_TheHubsPurgeDoneClosesTheDeclaration(t *testing.T) {
|
||||
calls := 0
|
||||
c := &EscrowAutoConfirmer{
|
||||
Pending: func() bool { return false },
|
||||
Escrowed: func() bool { return false },
|
||||
AbandonPurgeDone: func() { calls++ },
|
||||
Logger: log.New(io.Discard, "", 0),
|
||||
}
|
||||
c.Reconcile(&EscrowStatus{IdentityBlobPresent: true, SupersededPresent: true, AbandonPurgeDone: true})
|
||||
if calls != 1 {
|
||||
t.Fatalf("purge-done hook calls = %d, want 1 — the box would declare its abandonment for ever", calls)
|
||||
}
|
||||
c.Reconcile(&EscrowStatus{IdentityBlobPresent: true, SupersededPresent: true})
|
||||
if calls != 1 {
|
||||
t.Fatalf("hook ran on an ACK without abandon_purge_done (calls = %d)", calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestR1182_MainWiresAbandonPurgeDone(t *testing.T) {
|
||||
found, saw := confirmerFieldIsWired(t, "AbandonPurgeDone")
|
||||
if !saw {
|
||||
t.Fatal("no EscrowAutoConfirmer literal found in main.go")
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("EscrowAutoConfirmer is built WITHOUT AbandonPurgeDone — a finished abandonment never closes (R-1182)")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user