Declares the coupling: an agent below 0.126.0 answers 400 for both a fetch failure and a wrong code, so FeatureRecoveryFailureClass withholds the refusal reading and the 400 degrades to the neutral message. The gate blocks nothing — it only decides whether the customer may be told to check their typing.
This commit is contained in:
@@ -1,3 +1,96 @@
|
||||
## v0.202.0 — the customer is blamed only after a real attempt refused their code (2026-08-06, R-224/R-226/R-225/R-227/R-228) — MinAgent 0.126.0
|
||||
|
||||
**CAMPAIGN-11's headline defect had moved, not gone.** v0.201.0 stopped an agent that is too OLD from
|
||||
being reported as a wrong recovery code. An agent that is **stopped**, and a hub that cannot be
|
||||
**reached**, still fell through to a message about the code — measured live on 2026-08-05 with a
|
||||
**correct, current** code at **0.0299 s** and **0.0556 s**, against ~1.0 s for a genuine unseal. The
|
||||
machine had not tried, and told the customer their code was wrong.
|
||||
|
||||
**And the inverse was true at the same time (R-226).** The one message that says *"check your ten
|
||||
words"* was tested AFTER the retained-earlier-package message, so on any box that has re-escrowed —
|
||||
precisely the box whose customer has just been handed a new code — a genuine mistype could never
|
||||
reach it. Three unrelated failures got the accusation; the one that deserved it got something else.
|
||||
|
||||
**One defect from both ends: nothing on that path asked WHY it failed.** `rerr` was never inspected.
|
||||
|
||||
### The rule, and it is the whole change
|
||||
|
||||
> **The customer is blamed only after a real attempt refused their code. Every other outcome —
|
||||
> including one we cannot classify — says something else.**
|
||||
|
||||
Five classes, **from the value and never the text**:
|
||||
|
||||
| class | from | what the customer is told |
|
||||
|---|---|---|
|
||||
| hub-unreachable | 502/503 | the connection failed; **the code was NOT used** |
|
||||
| agent-unreachable | no agent verdict at all | the machine's own service is not answering; **NOT used** |
|
||||
| no-bundle | 404 | nothing is held for this machine; not about the code |
|
||||
| bundle-too-old | 409 | the code WORKED; the package predates the field |
|
||||
| **asked-and-refused** | **400** | **the only class that may mention typing** |
|
||||
| unknown | anything else | **neutral — claims neither that the code was wrong nor that it went unused** |
|
||||
|
||||
`agentapi.RecoveryRefusal` carries the status as a value; `refusalError` flattened it into a sentence,
|
||||
and a sentence is not something a caller can branch on.
|
||||
|
||||
**THE SAFE DEFAULT IS THE POINT.** `RecoveryUnknown` is the zero value, and an unrecognised status
|
||||
lands there rather than in an accusation. **That is the rule whose absence let this survive being
|
||||
fixed once.**
|
||||
|
||||
**Coupling — `MinAgent 0.126.0`.** An agent below it answers **400 for both** a fetch failure and a
|
||||
wrong code, so a 400 from one cannot be read as a refusal. `FeatureRecoveryFailureClass` withholds
|
||||
that reading and the 400 degrades to **neutral**. The gate **blocks nothing** — the unlock is
|
||||
attempted either way — it only decides whether the customer may be told to check their typing, and
|
||||
"not sure" means they may not. It heals itself when the agent updates.
|
||||
|
||||
**Elapsed time is logged** (it is what diagnosed this, and it is the cheapest tell for the operator)
|
||||
**and is never the classifier.** Time is a symptom; the status is the fact.
|
||||
|
||||
### R-225 — unknown is not zero
|
||||
|
||||
An unread store rendered `Tároló méret · 0 pillanatkép` and `Tárhelykeret: 0 / 50 GB (0%)` **directly
|
||||
above a card saying the store held backups under another key**. An SFTP listing found snapshot
|
||||
`f3d9cd67` and **12 535 KB** really there; `snapshot_count` and `repo_size_bytes` were simply ABSENT
|
||||
from `settings.json` and the zero value spoke for them. `StatsKnown` is **named**, for the same reason
|
||||
`OffsiteInventory.Empty` is — zero is what an unread store and an empty one both look like, and both
|
||||
fields are `omitempty` ints, so on disk "absent" and "0" are the same bytes. The fill bar renders only
|
||||
when the fill is known: **a 0 %-wide bar is a picture of emptiness, and a picture is a claim.** A
|
||||
*measured* zero still says zero.
|
||||
|
||||
### R-227 — the gateway error
|
||||
|
||||
**Which layer answers: traefik**, whose config this repo generates. A branded proxy page is therefore
|
||||
possible here, but traefik v3 serves no static files, so it would need a **new always-up container**
|
||||
for every 502 on the box — out of proportion to this finding, and **scoped in the report rather than
|
||||
built**. Shipped instead: the unlock posts via `fetch`, so a gateway failure is answered in Hungarian
|
||||
without leaving the page. **Progressive enhancement** — with no JS the plain POST is unchanged and
|
||||
still shows the proxy's error, and this entry says so rather than implying otherwise.
|
||||
|
||||
### R-228 — the set-aside history is visible, and honest
|
||||
|
||||
A customer who chose "I do not want the old data" was told the backups would be **kept**, and the
|
||||
move-aside did exactly that — 12 535 KB, byte-exact. The box recorded the path in
|
||||
`orphaned_renamed_to` and **a census found zero references to it in any template or handler.** It is
|
||||
surfaced now as two facts and stops.
|
||||
|
||||
**It does NOT promise the history can be reopened, and the confirmation copy was corrected for the
|
||||
same reason.** *"a helyreállítási kód nélkül többé nem lesznek megnyithatók"* implied that **with**
|
||||
the code they could be; serving a superseded package is an unbuilt link (R-199's inventory), so it
|
||||
cannot be opened by the customer, the operator, or anyone. The field's own comment called it
|
||||
*"recovery-code-recoverable"* — the same over-promise, in the code.
|
||||
|
||||
### Tests
|
||||
|
||||
Scenarios A-H at the **handler** and as **render tests per branch of each gate**. Red-proofs, each
|
||||
demonstrated failing and restored: delete the 502 case (A) - remove the mistype clause (C) - default
|
||||
to the accusation (D) - route an instant transport failure to the typing message (E) - remove the
|
||||
`StatsKnown` guards (F) - delete the set-aside block (H).
|
||||
|
||||
**Two existing tests encoded the defect and were corrected rather than deleted.** The web fake
|
||||
returned a **bare** error for "wrong code" — which is the shape of a failure we cannot classify, and
|
||||
now correctly renders the neutral message; saying "wrong code" in a test requires saying it the way
|
||||
the agent says it. And R-222's test forbade **any** mention of typing on a superseded box, **half of
|
||||
which R-226 deliberately reverses**: what stays forbidden is the bare accusation, not the hint.
|
||||
|
||||
## v0.201.0 — a correct recovery code is never called wrong again (2026-08-05, CAMPAIGN-11) — MinAgent 0.125.0
|
||||
|
||||
CAMPAIGN-11 walked the whole recovery journey end to end for the first time. **The data came back
|
||||
|
||||
Reference in New Issue
Block a user