# REPORT — R-88 Part 2: the agent can say "unknown" (v0.105.0) (2026-07-27) **Overwritten** per the standing rule. Agent **v0.104.0 → v0.105.0**. Producer half of a wire contract; the controller consumer is v0.178.0. ## What was broken `newestArchiveOn` promised in its own doc comment that *"errors and unsupported services degrade to 'unknown', never to 'no backup'"*. Its `(time.Time, bool)` signature made that **impossible**: an error and a genuine not-found both returned `(zero, false)`, so `/backup/due` emitted a POSITIVE `"no successful backup recorded yet"` with a nil age. The controller read that as *never backed up*, fired its window-gate safety valve, and quiesced customer app stacks **outside** the backup window — what happened during the 2026-07-27 PBS outage. The comment described an intent the type forbade. ## The wire encoding, and how the zero value is handled **A string enum**, matching the repo's existing convention (`phase` on `/backup/status`): ``` age_state: "known" | "absent" | "unknown" (omitted entirely by a pre-v0.105.0 agent) ``` The zero value is `""`, and it means **"legacy agent, no information"** — never "unknown". A `bool` would have made the legacy case indistinguishable from a real `false`, which is the trap the task named. The controller maps `""` (and any unrecognised future value) to `AgeStateLegacy` explicitly. Internally `archiveLookup` (`found`/`absent`/`unknown`) replaces the old bool, so the doc comment's promise is now something the type can actually express. ## Two decisions worth stating **The fail-safe direction is unchanged: unknown is still DUE.** An agent that cannot read the storage must never suppress a backup. Only the *window-gate bypass* narrows. **A service with NO lister deliberately stays `absent`, not `unknown`.** This broke a pre-existing test (`TestBackupDue_ServiceWithoutLister_UnchangedBehaviour`) and the test was right: calling it "unknown" would stop the controller firing its first-backup valve on a pre-R-84 build, so a genuinely new box would never back up outside its window and nobody would notice for weeks. On that path the in-memory record is the only registry that exists, so its absence means "no backup recorded" in the only terms available. `unknown` is reserved for a lister that was asked and could not answer. An **unparseable** in-memory timestamp is now `unknown` too — a backup did happen, we simply cannot date it. It previously fell through to the same positive "never" claim. ## Tests — 29 packages ok, 0 failed; `build`/`vet`/`test` each rc=0, run separately, vet unpiped +5. Red-proofs observed and restored, in both directions: | Mutation | Observed | |---|---| | error path → `archiveAbsent` (the pre-fix collapse) | `an unreadable storage must report age_state="unknown", got "absent" — that is a POSITIVE claim of 'never backed up' built out of two absences` | | `!found` → `archiveUnknown` (the over-correction) | `a genuine never-backed-up tier must report age_state="absent", got "unknown" — the controller only licenses a first backup outside the window on ABSENT` | ## Deployed — producer BEFORE consumer demo-hp then demo-felhom, both `felhom-agent 0.105.0`, service active, previous binary kept as `.bak-0.104.0`. **Verified on the live wire** from inside guest 9201, over the exact route the controller calls: ``` tier=local due=True age_state=known age_seconds=91097 reason=older than cadence tier=felhom-pbs due=False age_state=known age_seconds=100172 reason=within cadence window ``` ## NOT yet live-validated - **No `unknown` state has ever been observed on real hardware.** Both boxes report `known`; the unknown path is unit-proven only, and reproducing it live means making PBS unreadable, which must not be done to ep0. - The `absent` path is likewise unobserved live — it needs a genuinely fresh tier.