Correct a stale count repeated in four places: 10 floating pins, not 23
gates / gates (push) Successful in 25s
gates / gates (push) Successful in 25s
Recounted at catalog 18a6d2d8: 66 unique pins — 48 full X.Y.Z, 6 two-part lines, 4 major lines (10 float), 8 exact versions wearing a variant suffix. The '23' carried since v0.233.0 matches no definition the catalog supports. Definition written down beside the number so it can be rechecked. Also: CONTEXT said the fleet floor was 0.257.0; the hub says 0.259.0. Comment and doc only — no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
Last updated: 2026-09-21 (v0.260.0 — a box ahead of the catalog, and a pin that never moves backwards)
|
||||
|
||||
> **2026-09-21 — v0.260.0 (R-524, update arc).** A box that updated before the catalog was reverted under it read „Frissítés elérhető" over an Update that would have moved its pin BACKWARDS (measured BIGNIGHT Phase 6, privatebin 2.0.6 vs catalog 2.0.5). **The comparison gains a fourth verdict and MOVES OUT OF `web`:** `stacks.CatalogOrder` — Unknown/Current/Behind/**Ahead** — is read by BOTH the badge and `UpdatePreflight`, because a comparison implemented twice drifts. Ahead reads „Naprakész"/"Up to date" (`tag-ok`, same word and class as level — nothing for the household to do) with a title saying why; the Update is refused `downgrade` 409. **The API now renders update refusals through `errText`** — otherwise the new key would be a seam built and never wired. **Ahead is NARROW:** every differing service must be orderable AND newer, else Behind — this gate can BLOCK an update, so it errs towards letting one run. **THE TRAP, and the fixture caught it, not the design:** the first tag rule accepted only bare `X.Y.Z`, so every REAL catalog tag was unorderable and the refusal test failed with `got nil`. The rule now takes the version at the FRONT and requires the trailing suffix to be IDENTICAL on both sides — `31.0.14-apache → 31.0.15-apache` orders; `26.05.2-ls310 → -ls311`, `postgres:16-alpine`, `apache-2.57.0`, a date stamp and a digest pin do not. Ordering is `util.Version.Compare` and nothing else (one comparator, house rule). Three red-proofs, each seen to fail. **R-589 was NOT open** — it shipped in v0.258.0 and only its row was stale; a reviewer who reads ONE producer cannot see a SECOND that overrides it. **Floor NOT raised — the operator's step**; still 0.257.0. Deployed on demo-felhom 9201, demo-hp 9201 and demo-hp 9202 (scratch, upgraded from 0.245.0 for the live proof). Seven questions for Slices 6 and 7 are in `09` §3b; the state of the whole arc with drift numbers is `audits/UPDATE-ARC-STATE-2026-09-21.md`.
|
||||
> **2026-09-21 — v0.260.0 (R-524, update arc).** A box that updated before the catalog was reverted under it read „Frissítés elérhető" over an Update that would have moved its pin BACKWARDS (measured BIGNIGHT Phase 6, privatebin 2.0.6 vs catalog 2.0.5). **The comparison gains a fourth verdict and MOVES OUT OF `web`:** `stacks.CatalogOrder` — Unknown/Current/Behind/**Ahead** — is read by BOTH the badge and `UpdatePreflight`, because a comparison implemented twice drifts. Ahead reads „Naprakész"/"Up to date" (`tag-ok`, same word and class as level — nothing for the household to do) with a title saying why; the Update is refused `downgrade` 409. **The API now renders update refusals through `errText`** — otherwise the new key would be a seam built and never wired. **Ahead is NARROW:** every differing service must be orderable AND newer, else Behind — this gate can BLOCK an update, so it errs towards letting one run. **THE TRAP, and the fixture caught it, not the design:** the first tag rule accepted only bare `X.Y.Z`, so every REAL catalog tag was unorderable and the refusal test failed with `got nil`. The rule now takes the version at the FRONT and requires the trailing suffix to be IDENTICAL on both sides — `31.0.14-apache → 31.0.15-apache` orders; `26.05.2-ls310 → -ls311`, `postgres:16-alpine`, `apache-2.57.0`, a date stamp and a digest pin do not. Ordering is `util.Version.Compare` and nothing else (one comparator, house rule). Three red-proofs, each seen to fail. **R-589 was NOT open** — it shipped in v0.258.0 and only its row was stale; a reviewer who reads ONE producer cannot see a SECOND that overrides it. **Floor NOT raised — the operator's step; and CONTEXT's own 0.257.0 was STALE — the hub says 0.259.0 (read live from `/configs`, never from a doc).** Deployed on demo-felhom 9201, demo-hp 9201 and demo-hp 9202 (scratch, upgraded from 0.245.0 for the live proof). Seven questions for Slices 6 and 7 are in `09` §3b; the state of the whole arc with drift numbers is `audits/UPDATE-ARC-STATE-2026-09-21.md`.
|
||||
|
||||
> **2026-09-21 — v0.259.0 (R-596 P1 + R-598, the drill's blockers).** The claim page's answers and the Backup page's protection warnings follow the reader's language. **Fourteen** live sites carrying **nine** messages, not the sixteen literals R-596 counted — and the sixteenth, `data["Title"]`, was **DEAD** (`claim.html` is standalone; `.Title` belongs to `layout.html`) and was DELETED rather than translated. `backup_handlers.go` had **nine** code literals, not twelve; three were inside comments. `degradedMessageFor` now returns a **KEY**, so the decision stays language-free in one place while the words are chosen by whoever knows the reader; `buildTierViews`/`backupTargetLabel`/`loadGuestBackup` take `lang` the `buildDataPathCards` way. **The anonymous cookie-less claim page's language chain** (`langFor` → `settings.GetLanguage` → `configLanguage` ← `cfg.Customer.Language`) **was an unpinned assumption and is now a test.** Six existing copy-contract tests were kept rather than weakened — each resolves its key through the real bundle, so they still convict on a reworded Hungarian sentence. **Two things the tests caught and review did not:** an apostrophe in an English value is escaped to `'` and NEVER matches on the page (the failure reads exactly like an unwired handler — R-603), and the i18n gate convicted two of my English sentences for saying "please". **Proven live on guest 9201 through the `felhom_lang` cookie** — and the lockout proved itself unasked: Hungarian attempts locked out the English request from the same source, so the counter is per SOURCE, not per language. **The instrument trap that nearly cost a second fix (R-602): the cookie works only on ANONYMOUS pages** — `langFor` step 2 means a signed-in request reads the household's setting and ignores the cookie, so `/backups?felhom_lang=en` returns HUNGARIAN and reads like an unfixed defect. Use `?lang=` behind auth. **Floor NOT raised — the operator's step**; it still stands at 0.257.0. Guest 9201 is on **felhom-pve**, and **demo-hp answers on no route** (R-601).
|
||||
|
||||
|
||||
@@ -82,10 +82,18 @@ Image `gitea.dooplex.hu/admin/felhom-controller:0.260.0` built and pushed. **Dep
|
||||
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.
|
||||
**The fleet floor was NOT raised, and the number in the repo's own CONTEXT was stale.** CONTEXT.md
|
||||
said the floor stood at 0.257.0; **the hub says 0.259.0** (read live from `/configs`, not from a
|
||||
document — the operator raised it after that entry was written, which `felhom.eu/STATUS.md` records).
|
||||
v0.260.0 therefore reaches the two demo boxes by hand and **no further**.
|
||||
|
||||
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. **Why:** a floor raise reaches `peti-felhom`, a real
|
||||
customer box, and the unprompted-work fence puts anything that changes risk to customer data behind
|
||||
an operator word. The two preceding sessions recorded the raise as the operator's own act, and the
|
||||
one that happened came after the operator asked for it. Put to the operator with what happens if they
|
||||
do nothing: the fix stays on the two demo boxes and the rest of the fleet keeps offering a downgrade
|
||||
as an update — which is a badge and a button, not data at risk, so waiting costs little.
|
||||
|
||||
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/`.
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
| `Manager.BackfillInstalledImages` (v0.234.0) | controller/internal/stacks/installed.go | `() int` | seeding `installed_images` for apps that have NO record — call ONCE at startup | Beside `BackfillDesiredState` in `cmd/controller/main.go`, after it and BEFORE the boot reconciler (pinned by an AST-walking test that asserts the ORDER). **READS only** — starts nothing, writes no compose file. **Never overwrites an existing record** (an app that has one is not even observed). **REFUSES a partial observation** (`observationCoversTemplate`): `web.compareInstalledToTemplate` reads a service-count mismatch as BEHIND, so seeding a degraded app from what is visible renders „Frissítés elérhető" over an app that is current. The bring-up paths may write a partial because they follow a SUCCESSFUL `up -d` where a gap is real news; a backfill meets any state and must be stricter |
|
||||
| `stacks.ParseComposeImages` (v0.233.0) | controller/internal/stacks/installed.go | `(composePath string) (map[string]string, error)` | compose SERVICE name -> the image the FILE pins; feeds `Stack.TemplateImages` and the update badge | yaml.v3 `services:` MAP parse, never a line scan (same reason as `DBServiceNames`). An error means CANNOT-TELL — `ScanStacks` leaves `TemplateImages` nil and the badge renders NOTHING, never "current" |
|
||||
| `stacks.CatalogOrder` / `CompareImageRefs` (v0.260.0, R-524) | controller/internal/stacks/updateorder.go | `(Stack) UpdateOrder` — Unknown/Current/Behind/**Ahead** | THE one "how does this app stand against the catalog?" verdict | **Both the badge AND `Manager.UpdatePreflight`'s `downgrade` refusal read it — never re-implement the comparison.** `web.compareInstalledToTemplate` is a thin wrapper. Ahead is NARROW: every differing service must be orderable AND newer, else Behind. Ordering is `util.Version.Compare` behind a tag normaliser (`X.Y`/`X.Y.Z`, optional `v`, suffix must be IDENTICAL on both sides) — **never add a second comparator**. Queries NO registry; absent record = Unknown, never „Naprakész" |
|
||||
| `web.updateBadge` / `updateBadgeAt` / `Metadata.CatalogSince` + `CatalogSinceAge` (v0.233.0) | controller/internal/web/updatebadge.go, controller/internal/stacks/metadata.go | `(stacks.Stack) *MetaBadge` | THE "is this app current?" label — „Naprakész" / „Frissítés elérhető — N napja" | The SECOND `*MetaBadge` user the type was built for: existing `meta_badge` partial, **no new markup or CSS**. **NO RECORD RENDERS NOTHING — absent means UNKNOWN, never current** (R-166 applied to an observation; red-proved). **No version number reaches the customer** and **no registry is queried**. `catalog_since` is tolerant in the `lifecycle` style — absent/empty/malformed/**future** all degrade to a badge with no age + one WARN. LIMITATION: for the 23 floating pins the ref can match while the image has moved, so those read „Naprakész" when they may not be |
|
||||
| `web.updateBadge` / `updateBadgeAt` / `Metadata.CatalogSince` + `CatalogSinceAge` (v0.233.0) | controller/internal/web/updatebadge.go, controller/internal/stacks/metadata.go | `(stacks.Stack) *MetaBadge` | THE "is this app current?" label — „Naprakész" / „Frissítés elérhető — N napja" | The SECOND `*MetaBadge` user the type was built for: existing `meta_badge` partial, **no new markup or CSS**. **NO RECORD RENDERS NOTHING — absent means UNKNOWN, never current** (R-166 applied to an observation; red-proved). **No version number reaches the customer** and **no registry is queried**. `catalog_since` is tolerant in the `lifecycle` style — absent/empty/malformed/**future** all degrade to a badge with no age + one WARN. LIMITATION: for the **10** floating pins (recounted 2026-09-21; the old "23" matched no definition the catalog supports) the ref can match while the image has moved — 6 of the 7 measurable ones HAVE moved — so those read „Naprakész" when they may not be |
|
||||
| `Manager.logPostStartStatus` | controller/internal/stacks/manager.go | `(name, stackDir, env)` | Async post-start verification | compose up exits 0 on crash-loops; this is the detection. Goroutine + 3s, never blocks |
|
||||
| `Manager.EnsureBaseStack` | controller/internal/stacks/infra.go | `() error` | Traefik/cloudflared/FileBrowser infra convergence | Renders from `internal/infra` templates |
|
||||
| `appbackup.ClassifyBinds` / `ValidateBackupSpec` | controller/internal/appbackup/classify.go | `(spec, binds) ([]ClassifiedBind, bool)` / `(spec, binds) error` | Backup-classification (Task 2, referential coupling) — pure | Two-level default: explicit wins over `:ro`; unlisted writable→mandatory, unlisted `:ro`→excluded; nil spec→legacy/false. Validate REJECTS the WHOLE block on any defect (whole-block semantics). INERT — no tier consumes it yet |
|
||||
|
||||
@@ -95,7 +95,11 @@ func CatalogOrder(s Stack) UpdateOrder {
|
||||
// - two references to DIFFERENT images (`alpine:3.20` against `…/bookstack:26.05.2`) — the numbers
|
||||
// are comparable and the comparison is meaningless, which is the worst kind of false positive;
|
||||
// - any tag that is not plain digits and dots: `16-alpine`, `latest`, `26.05.2-ls310`, `stable`,
|
||||
// a date stamp, a git sha. 23 of the catalog's 66 pins float exactly like this (§8.1).
|
||||
// a date stamp, a git sha. **10 of the catalog's 66 pins float like this**, RECOUNTED
|
||||
// 2026-09-21 — 6 two-part lines (mariadb:11.4/11.6/12.3, claper:2.5, opengist:1.13,
|
||||
// wger/server:2.6) and 4 major lines (postgres:15/16-alpine, redis:7-alpine,
|
||||
// postgis:16-3.5-alpine). The "23" this comment used to carry matched no definition the
|
||||
// catalog supports today; see 09 §8.1.
|
||||
//
|
||||
// THE ORDER ITSELF IS util.Version.Compare AND NOTHING ELSE. The house rule is one comparator in
|
||||
// this repo; this function is a tag NORMALISER in front of it, never a second implementation.
|
||||
|
||||
@@ -42,9 +42,11 @@ const (
|
||||
// once the catalog moves past it, so installed would equal template and this function would
|
||||
// answer „Naprakész" on precisely the apps that are behind — with every test still green,
|
||||
// because the two fields have the same type and shape.
|
||||
// - KNOWN LIMITATION: 23 of the catalog's 66 distinct pins FLOAT (postgres:16-alpine,
|
||||
// mariadb:11.6, …). For those the reference can be identical while the image behind it has moved
|
||||
// upstream. Those apps read „Naprakész" when they may not be — R-446.
|
||||
// - KNOWN LIMITATION: **10 of the catalog's 66 distinct pins FLOAT** (recounted 2026-09-21;
|
||||
// the "23" this comment carried since v0.233.0 matched no definition the catalog supports
|
||||
// today). For those the reference can be identical while the image behind it has moved
|
||||
// upstream, and 6 of the 7 measurable ones HAVE moved since the catalog set them. Those apps
|
||||
// read „Naprakész" when they may not be — R-446.
|
||||
func compareInstalledToTemplate(s stacks.Stack) updateState {
|
||||
switch stacks.CatalogOrder(s) {
|
||||
case stacks.UpdateOrderCurrent:
|
||||
|
||||
Reference in New Issue
Block a user