v0.203.0: the box collects what the hub staged for it (R-218 consume half) + R-220's message
gates / gates (push) Successful in 10s

R-218's declaration half shipped in v0.201.0 and works. Its consume half never
existed. Reconcile ran exactly twice per process — at start-up and when the
recovery screen drives it — and BOTH fire before the hub has anything staged,
because the hub stages in RESPONSE to the declaration those runs precede.

Measured on the R-201 re-walk: unlock reconcile 11:43:07, hub staged 11:44:57
saying 'next cycle', a full report cycle ran 11:55:46, still unconsumed at
12:06. A guest command line applied it in 18 seconds — everything correct except
the trigger.

Bridge.RetryIfDeclared re-runs the SAME reconcile on a 5-minute tick, driven from
the box's own published declaration (OffboxReportStatus().State) — the very
statement the hub acts on, so the two cannot disagree.

Poll, not an ACK flag, decided on the promise: the no-target message says 'amint
megvannak' (no deadline) and the card says 'within a day'. Five minutes is inside
both by a wide margin and needs no hub change.

It stops by construction — a healthy box does no work and logs nothing — and the
settle gate is deliberately kept via ReconcileWhenSettled.

The marker was investigated and left alone: applied_marker lives in the guest's
DataDir, which a rebuild destroys, so it cannot suppress a legitimate re-run.

R-220's customer half: the refusal no longer tells the customer to choose from a
list that may be empty. It names the rebuild, points at the Meghajtók page, and
promises no outcome.

Red-proofs: remove the retry -> credential uncollected (the dead end reproduced);
drop the stop condition -> a healthy box hammers the hub; call Reconcile instead
of ReconcileWhenSettled -> settle gate bypassed; restore the old sentence -> the
impossible action returns.

28 packages ok, vet clean, all controller gates OK.
This commit is contained in:
2026-08-06 12:56:12 +02:00
parent 66d80efb9f
commit 9dc26459ea
6 changed files with 290 additions and 1 deletions
+51
View File
@@ -1,3 +1,54 @@
## v0.203.0 — the box collects what the hub staged for it (2026-08-06, R-218 consume half / R-220 message) — MinAgent 0.127.0
**R-218's declaration half shipped in v0.201.0 and works. Its consume half never existed.**
A stranded box says `offsite.state=needs_credential`; the hub's `offsiteheal` re-stages the one-time
secret and logs *"the box re-consumes on its next cycle"*. **There was no next cycle.** `Reconcile`
ran exactly twice in a process's life — once at start-up (whose own comment said *"retries on next
config refresh/restart"*) and once when the recovery screen drives it (R-219) — and **both fire before
the hub has anything staged, because the hub stages in RESPONSE to the declaration those runs
precede.** So the hub held a credential the box would never fetch.
**Measured on the R-201 re-walk, 2026-08-06:** unlock reconcile **11:43:07** · hub staged **11:44:57**
saying "next cycle" · a full report cycle ran **11:55:46** · **still unconsumed at 12:06**. A guest
command line applied it in **18 seconds** — proving the credential, the target and the key were all
correct and only the trigger was missing. That was the first of the two dead ends that kept the
recovery journey failing.
**The fix: re-run the SAME reconcile, on a tick, for exactly as long as the box says it needs a
credential.** `Bridge.RetryIfDeclared` is driven from the box's own published declaration
(`OffboxReportStatus().State`) — **the very statement the hub acts on**, so the two can never disagree
about whether a retry is wanted.
**Why a poll and not an ACK flag.** The deciding criterion was the promise the customer is given: the
no-target message says *"amint megvannak"* (no deadline) and the backups card says *"ha egy napon
belül nem áll be"* — **within a day**. A 5-minute tick is inside both by a wide margin and needs **no
hub change**. If either promise ever tightens to minutes, revisit.
**It stops by construction.** The instant a target exists the declaration goes false: a healthy box
does no work and **logs nothing** (asserted). **The settle gate is deliberately kept** — the retry goes
through `ReconcileWhenSettled`, so the day-0 floor race it guards is unchanged.
**The marker was investigated and left alone.** `applied_marker` lives at `<DataDir>/offbox/` — inside
the guest's data dir, which a rebuild destroys — so it cannot suppress a legitimate post-rebuild
re-run. It is not part of this defect.
### R-220's customer-facing half — a refusal that named an impossible action
The deploy refusal said *"Válasszon a listából csatlakoztatott meghajtót"* — choose an attached drive
from the list — **while the list was empty**, on a rebuilt box, for a reason the customer had no part
in. That is the I3 breach the campaign recorded. It now says what is true (the drive is not registered
**on this machine**, which is what a rebuild causes), points at the page where re-attaching happens
rather than at a possibly-empty list, and **promises no outcome**, because whether the drive can be
re-attached is not knowable from there. The NAS refusal is a different situation and is untouched.
Tests: `internal/offsiteapply/retry_test.go` (a credential staged after start-up is collected; a
healthy box does nothing and logs nothing; a nil bridge is silent; the settle gate holds) and
`internal/settings/refuse_message_test.go`. **Red-proofs:** removing the retry leaves the credential
uncollected — the re-walk's dead end reproduced; dropping the stop condition makes a healthy box
hammer the hub; calling `Reconcile` instead of `ReconcileWhenSettled` bypasses the settle gate;
restoring the old sentence brings the impossible action back.
## docs — the "CI is still owed" claim was stale; corrected (2026-08-06, R-229 part 2) — no version bump
**One sentence, no code.** This file asserted that continuous integration was still owed