bdeb1c0d8e
gates / gates (push) Successful in 27s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
92 lines
6.6 KiB
Markdown
92 lines
6.6 KiB
Markdown
# REPORT — controller v0.260.0: a box ahead of the catalog, and a pin that never moves backwards
|
|
|
|
**R-524 (P2).** Base `19ef0329ab66` → **v0.260.0** (`8f8a64cad7a5`). MinAgent 0.131.0 unchanged.
|
|
Architecture read first and named: `felhom.eu/documentation/architecture/09-update-architecture.md`
|
|
(§3 the nine decisions, §5.4 the render table, §6.1 slice 4 as shipped, §8 the limitations).
|
|
|
|
## What was wrong
|
|
|
|
**Measured, not imagined** — BIGNIGHT Phase 6, 2026-09-15, VM 333. privatebin was updated
|
|
2.0.5 → 2.0.6 through the guarded Update; the catalog was then reverted to 2.0.5. At 22:13:37Z the
|
|
box read `installed privatebin/pdo:2.0.6`, `catalog privatebin/pdo:2.0.5`, and the app page showed
|
|
„**Frissítés elérhető — ma**" with a title inviting the household to press Frissítés. The comparison
|
|
asked only *does the installed reference DIFFER?*, so **a catalog revert — an operator act on our
|
|
side — presented itself to a customer as an update**, and the guarded Update behind it would have
|
|
advanced the pin 2.0.6 → 2.0.5, onto a datadir the newer version may already have migrated, with §4's
|
|
ruling saying that cannot be undone.
|
|
|
|
## What shipped
|
|
|
|
- **`stacks.CatalogOrder`** (`controller/internal/stacks/updateorder.go`) — the comparison gains a
|
|
fourth verdict (Unknown / Current / Behind / **Ahead**) and **moves out of `web`**. That move is the
|
|
substance: two callers must reach the same verdict — the badge and `Manager.UpdatePreflight` — and a
|
|
comparison implemented twice is a comparison that drifts. `web.compareInstalledToTemplate` is now a
|
|
thin wrapper and keeps every property it had (absent means UNKNOWN and never „Naprakész"; it reads
|
|
`CatalogImages` and never `TemplateImages`; it queries no registry).
|
|
- **The badge.** Ahead reads „Naprakész" / "Up to date", `tag-ok` — the same word and class as level,
|
|
because there is nothing for the household to do — with a title that says why
|
|
(`badge.update.ahead.title`, born as a key in both bundles). No version number reaches the customer.
|
|
- **The refusal.** `UpdatePreflight` returns reason `downgrade`, HTTP 409,
|
|
„Ez a változat újabb a katalógusban lévőnél — visszalépés csak az üzemeltető kérésére.", logged with
|
|
both image maps. **The API now renders update refusals through `errText`** — without that one line
|
|
the new key would have been a seam built and never wired, which is a documented failure class here.
|
|
- **Ahead is the NARROW arm.** Every differing service must be orderable AND newer; one older, one
|
|
unorderable, and the verdict falls back to Behind — i.e. to v0.233.0..v0.259.0 behaviour. This gate
|
|
can BLOCK an update, so it errs towards letting one run.
|
|
- **Ordering is `util.Version.Compare` and nothing else** (house rule: one comparator). The new code
|
|
is a tag NORMALISER in front of it.
|
|
|
|
## What the fixture caught that the design did not
|
|
|
|
**The first implementation called every real catalog tag unorderable.** It accepted only bare
|
|
`X.Y`/`X.Y.Z`, and the test fixture uses `nextcloud:31.0.14-apache` — the real catalog pin. The
|
|
refusal test failed with `got nil`, and the cause was the code being right about a rule that was
|
|
wrong. The rule now takes the version at the FRONT of the tag and requires the trailing suffix to be
|
|
**identical on both sides**, so `31.0.14-apache → 31.0.15-apache` orders while `26.05.2-ls310 →
|
|
-ls311` (a build number with no rule), `postgres:16-alpine` (a major LINE, not a version),
|
|
`kimai/kimai2:apache-2.57.0` (version at the back), a date stamp and a digest pin all stay
|
|
unorderable. **Measured against the real catalog: 8 of 66 pins float and one puts its version last.**
|
|
|
|
## Red-proofs — three, each SEEN to fail
|
|
|
|
| # | the mutation | what failed |
|
|
|---|---|---|
|
|
| 1 | make `CatalogOrder`'s Ahead arm unreachable | `TestR524_PreflightRefusesDowngrade/ahead` — *"this update must be REFUSED with reason \"downgrade\", got nil"*. The update is ALLOWED and the next thing it does is move the pin back. |
|
|
| 2 | treat an UNORDERABLE pair as ahead (`cmp < 0` with the `ok` dropped) | the floating-tag, different-image and digest-pin cases all fail with Ahead — the verdict that would suppress a real „Frissítés elérhető" on the floating pins |
|
|
| 3 | delete the ahead arm from `localeFuncs` | `TestUpdateBadgeFollowsTheLanguage` — *"an app ahead of the catalog must carry a badge"* |
|
|
|
|
An honest note on #1: the first attempt deleted the preflight block and failed to BUILD (an unused
|
|
import), which proves nothing. It was redone by neutering the Ahead arm instead, and that failed for
|
|
the right reason.
|
|
|
|
## A claim in the brief that was wrong, named
|
|
|
|
**R-589 was NOT open.** The brief said, reviewer-verified, that `updatebadge.go` builds the badge
|
|
from four raw Hungarian literals with no key and that R-589 is therefore open. The literals are real;
|
|
the conclusion does not follow. They are the Hungarian form and are deliberately frozen — that IS the
|
|
parity guarantee — and the ENGLISH form has been rebuilt from the bundle in `web.localeFuncs` since
|
|
**v0.258.0**, pinned by `TestUpdateBadgeFollowsTheLanguage` and proven live on a fresh box the same
|
|
morning (`DRILL-first-hour-en-0258-2026-09-20.md` item 9). The register row was stale, not the code;
|
|
it is closed with that citation. **The general lesson: a reviewer who reads one producer cannot see a
|
|
second producer that overrides it.** `updatebadge.go` now says so in its own comment, and v0.260.0's
|
|
new arm was written into BOTH producers with a test that fails if either is missing.
|
|
|
|
## Green gate and delivery
|
|
|
|
`go build ./... && go vet ./... && go test ./...` — **all green** (full suite, not a subset).
|
|
`controller_gates.py --fast` — **17 gates, all OK**; `go-parity` convicted first and was satisfied
|
|
properly, by registering both new keys as BORN-AS-KEYS in `i18n_go_keys.json` with the test that pins
|
|
each. No `--no-verify`; the pre-push hook ran and passed.
|
|
|
|
Image `gitea.dooplex.hu/admin/felhom-controller:0.260.0` built and pushed. **Deployed and healthy on
|
|
three guests**: demo-felhom 9201, demo-hp 9201, and demo-hp 9202 (the scratch guest, upgraded from
|
|
0.245.0 for the live proof).
|
|
|
|
**The fleet floor was NOT raised — that is the operator's step**, as it was left for v0.258.0 and
|
|
v0.259.0; it still stands at 0.257.0. Stated rather than silently skipped: the standing rule asks for
|
|
the floor to be raised to deliver a release, and this session deliberately did not, because the two
|
|
preceding sessions recorded floor raises as the operator's own act.
|
|
|
|
Live proof, drift numbers and the state of the whole arc:
|
|
`felhom.eu/documentation/audits/UPDATE-ARC-STATE-2026-09-21.md` and `audits/update-arc-2026-09-21/`.
|