R-100: record the offsite last-SUCCESS anchor (v0.181.0)
LastRun records an attempt, not a result. New OffboxTarget.LastSuccess, set only on the success branch via the pure offboxAnchorAfterRun rule, carried to the hub as last_success. Closes two silent-wipe sites (settings save, hub re-apply).
This commit is contained in:
@@ -1,5 +1,44 @@
|
||||
## Changelog
|
||||
|
||||
### v0.181.0 — R-100: record the last SUCCESS, not just the last attempt (2026-07-28)
|
||||
|
||||
The producer half of R-100. `OffboxTarget` gains **`LastSuccess`** (RFC3339), carried to the hub on the
|
||||
offsite report as `last_success`. The hub's staleness verdict counts from it (hub v0.80.0).
|
||||
|
||||
**Why a new field rather than reading `LastStatus`.** `LastRun` is written unconditionally at the end
|
||||
of every run, failures included — it records an **attempt**. "How long since `LastRun`" therefore
|
||||
answers "how long since we last TRIED", which is not the question a freshness verdict asks. The
|
||||
alternative — "`LastStatus == error` ⇒ stale" — turns every transient blip into an immediate alarm,
|
||||
which is the F-A1 noise failure mode. Anchoring on last success tolerates one bad night and catches a
|
||||
persistent one, using the threshold that already exists.
|
||||
|
||||
The rule is a pure function, `offboxAnchorAfterRun(prev, at, runErr)`, called unconditionally beside
|
||||
the `LastRun` write. Both directions are bugs if got wrong and both are pinned:
|
||||
- a failure must not **advance** it → or the original defect survives;
|
||||
- a failure must not **clear** it → or one bad night makes an established tier read as never-succeeded
|
||||
(the mirror-image over-correction, and on the hub side the newborn-box path).
|
||||
|
||||
**Two silent-wipe sites found and closed**, both of the "seam built but never wired" shape — the field
|
||||
exists, the writer sets it, and an unrelated routine path zeroes it:
|
||||
- `offboxConfigHandler` rebuilds the target from the form and copies runtime status field by field, so
|
||||
an ordinary settings save (edit the host, edit the path) would have erased the anchor;
|
||||
- `ApplyOffsiteTarget` does the same on a hub re-apply — an established tier reset to "never
|
||||
succeeded" every time the hub re-pushed its descriptor.
|
||||
|
||||
Neither would have surfaced until the hub's verdict changed, days later.
|
||||
|
||||
**A hollow test of my own, caught by red-proofing it.** The first version of
|
||||
`TestOffboxLastSuccess_OnlyAdvancesOnSuccess` re-implemented the rule in a local closure: mutating the
|
||||
production code left it **green**. That is what the extraction to `offboxAnchorAfterRun` is for — the
|
||||
test now calls the real rule, and the red-proof bites.
|
||||
|
||||
Red-proofs, all observed failing: drop the `runErr` guard → `a FAILED run advanced LastSuccess to
|
||||
"2026-07-21T02:15:00Z" — that is the R-100 defect in mirror image`; always return `prev` → `a
|
||||
successful run did not advance the anchor`; drop the wire field → `OffboxReportStatus dropped
|
||||
LastSuccess — the hub would degrade forever on a controller that has it`; drop the handler
|
||||
preservation → `a settings save erased LastSuccess`.
|
||||
|
||||
|
||||
### v0.180.0 — F-OBS: the dead-app check gets a positive observable (2026-07-28)
|
||||
|
||||
On a default `logging.level: info` box there was **no way to tell whether `deadapp-check` had run**.
|
||||
|
||||
Reference in New Issue
Block a user