controller v0.178.0 — R-88 Part 2: only a positive 'never' fires the valve
MinAgent: 0.105.0. scheduledRunAllowed fired on any nil age; it now requires a licence from valveLicensed, which grants it for AgeStateAbsent and for a LEGACY agent, and refuses it for AgeStateUnknown. An unreadable storage no longer masquerades as a first-ever backup and no longer quiesces apps outside the window. A missing wire field means legacy, not unknown — deliberately. Treating it as unknown would stop the valve firing on un-upgraded boxes and starve genuinely new ones. Degrade logged once; unrecognised future values also map to legacy. Caught in passing: TieredBackend is satisfied by a RUNTIME assertion, so the signature change compiled and vetted clean while quiesceBackend silently stopped satisfying it — which would have degraded every box to the single-tier path with no error. Added a compile-time witness. Also corrects the notifier comment that claimed operator-only came from a missing customerMessages entry; enforcement is hub-side operatorOnlyEvents (hub 0.79.0).
This commit is contained in:
@@ -1,5 +1,43 @@
|
||||
## Changelog
|
||||
|
||||
### v0.178.0 — R-88 Part 2 (controller) + R-97c comment fix (2026-07-27) — **MinAgent: 0.105.0** for the age_state semantics
|
||||
|
||||
**The safety valve now needs a licence.** `scheduledRunAllowed` fired on ANY nil age — "no recorded
|
||||
backup yet, never withhold the first one". With agent v0.105.0 the age carries a STATE, and only a
|
||||
POSITIVE claim licenses the bypass:
|
||||
|
||||
| `age_state` | licenses the valve? | why |
|
||||
|---|---|---|
|
||||
| `absent` | **yes** | the agent looked; there is genuinely nothing there |
|
||||
| `unknown` | **no** | unreadable storage — this is the whole fix |
|
||||
| `known` | n/a | a real age; the age comparison decides |
|
||||
| *(empty)* | **yes** | pre-v0.105.0 agent — see below |
|
||||
|
||||
**A missing field means LEGACY, not unknown, and that is deliberate.** Reading an old agent's silence
|
||||
as "unknown" looks safer and regresses Scenario D: the valve would stop firing on every un-upgraded
|
||||
box, so a genuinely new box would never take its first backup outside its window and nobody would
|
||||
notice for weeks. Preserving the KNOWN behaviour is correct; the MinAgent floor drives the upgrade.
|
||||
The degrade is logged **once** per process, the `logTierDegradeOnce` shape. An unrecognised FUTURE
|
||||
value also maps to legacy — a newer agent inventing a fourth state must not inherit "unknown"
|
||||
semantics from a controller that has never heard of it.
|
||||
|
||||
**Caught while doing it, and worth naming:** `TieredBackend` is satisfied by a RUNTIME type assertion
|
||||
in `resolveDueTiers`, so when `DueFor`'s signature changed the whole repo still built and vetted
|
||||
clean while `quiesceBackend` silently stopped satisfying the interface — which would have degraded
|
||||
every box to the untargeted single-tier path, losing R-82's multi-tier backups entirely, with no
|
||||
error anywhere. `TestQuiesceBackendSatisfiesTieredBackend` is now the compile-time witness. Sixth
|
||||
instance of the inert-seam class.
|
||||
|
||||
**R-97c follow-through:** the comment in `internal/notify` claiming these event types are
|
||||
operator-only "because they have no customerMessages entry" was **wrong** and is corrected — the hub
|
||||
falls back to the raw message when the entry is missing, and the only customer gate is
|
||||
`prefs.EnabledEvents`. Enforcement is hub-side `operatorOnlyEvents` (hub >= v0.79.0).
|
||||
|
||||
Unchanged: the R-88 Part 1 breaker and its timings, the window bounds, `dropBackedOffTiers`, and
|
||||
`TriggerNow` (still ungated by everything).
|
||||
|
||||
Tests +7 (6 age-state + 1 interface witness); 27 packages ok. Red-proofs observed for Scenarios A, B and C.
|
||||
|
||||
### v0.177.0 — R-97: a failing backup is HEARD, and stops blaming the apps (2026-07-27) — MinAgent unchanged; requires hub >= v0.78.0
|
||||
|
||||
**R-97a — the whole-guest tier had no route to the hub.** `internal/quiesce` did not import
|
||||
|
||||
Reference in New Issue
Block a user