86 lines
4.7 KiB
Markdown
86 lines
4.7 KiB
Markdown
# REPORT — R-100: record the last SUCCESS, not just the last attempt (v0.181.0) (2026-07-28)
|
|
|
|
**Overwritten** per the standing rule. Controller **v0.180.0 → v0.181.0** — the **producer** half of
|
|
R-100, shipped and deployed BEFORE the hub so the consumer never reads a field nothing sends.
|
|
Consumer: hub **v0.80.0**. Full arc, Phase 0 answers and the live proof: `felhom.eu/REPORT-r100.md`.
|
|
|
|
## Baselines (reconfirmed, not copied)
|
|
`felhom-controller 4056fec` = origin/main, clean; controller 0.180.0 live on both boxes; hub 0.79.0
|
|
(manifest and running pod); agent 0.110.0.
|
|
|
|
## What changed here
|
|
|
|
`OffboxTarget` gains **`LastSuccess`** (RFC3339), carried to the hub on the offsite report as
|
|
`last_success`. `LastRun` is written **unconditionally** at the end of every run including failures — it
|
|
records an **attempt**, so "how long since `LastRun`" answers "how long since we last TRIED", which is
|
|
not the question the hub's freshness verdict was asking of it.
|
|
|
|
The rule is a pure function, called unconditionally beside the `LastRun` write:
|
|
|
|
```go
|
|
func offboxAnchorAfterRun(prev, at string, runErr error) string {
|
|
if runErr != nil { return prev } // failures neither advance nor clear the anchor
|
|
return at
|
|
}
|
|
```
|
|
|
|
Both directions are distinct bugs and both are pinned: a failure must not **advance** it (the original
|
|
defect) and must not **clear** it (one bad night making an established tier read as never-succeeded —
|
|
the mirror-image over-correction, and on the hub side the newborn-box path).
|
|
|
|
**Why not simply `LastStatus == "error" ⇒ stale`:** that is the tempting one-line fix and it pages on
|
|
every transient blip — the F-A1 noise mode that trains an operator to ignore the alarm. Anchoring on the
|
|
last success tolerates one bad night and catches a persistent one, using the threshold that already
|
|
exists. No new knob.
|
|
|
|
## Two silent-wipe sites, found and closed
|
|
|
|
Both are the "seam built but never wired" shape — the field exists, the writer sets it, and an unrelated
|
|
routine path zeroes it. Neither would have surfaced until the hub's verdict changed, days later.
|
|
|
|
- **`offboxConfigHandler`** rebuilds the target from the form and copies runtime status across 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.
|
|
|
|
## 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** — it asserted my model, not the system. 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
|
|
| red-proof | observed failure |
|
|
|---|---|
|
|
| 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 `LastSuccess` from the wire | `OffboxReportStatus dropped LastSuccess — the hub would degrade forever on a controller that has it` |
|
|
| drop the handler preservation | `a settings save erased LastSuccess (got "") — the tier would read as never-succeeded` |
|
|
|
|
`go build`, `go vet ./...`, `go test ./...` — 27 packages, `rc=0`, run separately from the commit.
|
|
|
|
## Live, on demo-hp and demo-felhom
|
|
```
|
|
demo-hp success run → ok last_run=11:24:20Z last_success=11:24:20Z
|
|
demo-hp settings save (port 23→2) → last_success PRESERVED 11:24:20Z ← the wipe-site fix, live
|
|
demo-hp failing run → error last_run=11:25:48Z last_success=11:24:20Z ← ANCHOR HELD
|
|
demo-felhom healthy run→ ok last_run=11:29:22Z last_success=11:29:22Z ← advanced
|
|
```
|
|
Both shapes reached the hub. The failure was induced with a **closed port** (creates nothing, touches no
|
|
data) and the config was restored field by field. `peti-felhom` was never touched.
|
|
|
|
**Not proven live:** the hub's 48h staleness threshold itself — that is unit-tested with an injected
|
|
clock and would take days to observe live.
|
|
|
|
## Deployed
|
|
`felhom-controller` **0.181.0** on **demo-hp** and **demo-felhom**, both `Up (healthy)`.
|
|
|
|
## NOT yet live-validated (carried forward)
|
|
- The 48h threshold, and with it an actually-observed `offsite_stale` firing from a stale anchor.
|
|
- **R-101** (filed today) — Tier-2 `LastRun` is also written on failure and three customer surfaces
|
|
render it without a status, incl. the restore-confirm dialog.
|
|
- **Fault 4** — restic transport interruption; the closed-port injection sidesteps it, not solves it.
|
|
- **R-99** — prune never removes phantom snapshots.
|
|
|