v0.235.0: freeze the version, keep the fixes flowing (operator ruling 2026-09-06)
gates / gates (push) Successful in 12s

Slice 3. R-447 was BLOCKED because R-438 established that RestartStack's use of
up -d to pick up template changes was CHOSEN and written down in its own comment.
The operator ruled Option 1, and this implements it.

The rule: while the catalog offers the same version you run, its fixes flow to
you; the moment it moves to a newer version you are frozen until you update.

NOTHING was added to any of the thirteen compose up -d call sites. Most of them
are repairs - the boot reconciler, the drive-return gate, the app-stop guard -
and a repair path that refuses to repair leaves a customer's app down, which is
worse than the problem. They are made safe by removing the reason.

app.yaml gains pinned_images: what the app is SUPPOSED to run. It is NOT
installed_images, which is an observation; letting a reading become a deployment
is the R-166 category error one field over. Four writers, each also storing the
exact definition as applied-compose.yml. UpdateStack advances the pin and
re-renders BEFORE the pull, because pull and up -d act on the file on disk, and a
pin set afterwards would pull the frozen version and report success.

The syncer renders instead of copying, through one nil-safe seam. Catalog images
equal the pin -> verbatim, so fixes and self-healing both survive; they differ ->
the WHOLE stored definition, never a substitution of refs into a newer template
(wger 2.6 needs a DB config the older template cannot supply). This is
deliberately not 'skip deployed apps', which was option B and was rejected.

AdoptPins runs once at boot after the backfill, files only, and skips loudly
rather than inventing a pin. syncer.Start() moved to after it: the initial sync
would otherwise run while every app was unpinned and overwrite a deployed app's
version once per boot.

THE BADGE HAD TO CHANGE OR SLICE 2 WOULD HAVE INVERTED SILENTLY. TemplateImages
reads the LIVE compose file, which is now the frozen one, so the comparison would
have answered Naprakesz on exactly the apps that are behind - with every test
green, because the new field has the same type. It now reads CatalogImages.

+16 tests (1729 -> 1745), 28 packages green. Three red-proofs run and reverted.
A test also caught the syncer writing an empty compose file over a live app.
This commit is contained in:
2026-09-06 09:45:34 +02:00
parent 998aa31958
commit 8a0e0a59ad
13 changed files with 1437 additions and 17 deletions
+27 -1
View File
@@ -7,7 +7,33 @@
>
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
Last updated: 2026-09-03 (v0.234.0 — the installed-images backfill, so the badge appears on an app nobody touched)
Last updated: 2026-09-06 (v0.235.0 — the version freeze: slice 3, on the operator's ruling)
> **2026-09-06 — v0.235.0. THE RULING, AND THE TRAP IT SET.**
>
> **1. OPERATOR RULING: freeze the version, keep the fixes flowing.** R-447 sat `BLOCKED` because
> R-438 established that `RestartStack`'s `up -d` was a CHOSEN behaviour with its reason in its own
> comment. Option 1 was taken: an app's version is frozen to what the customer has and only a
> deliberate Update moves it, while health-check fixes, memory limits and self-healing keep arriving
> on the 15-minute cycle. **Both halves of the old behaviour were examined; only the version change
> was unwanted.**
>
> **2. THE MECHANISM IS A RENDER, NOT A GATE — and that distinction is the whole design.** Nothing was
> added to any of the thirteen `compose up -d` call sites. Most of them are REPAIRS (boot reconciler,
> drive-return gate, app-stop guard), and a repair that refuses to repair leaves a customer's app
> down. They are made safe by removing the reason: the file they act on no longer changes version.
>
> **3. `pinned_images` IS INTENT; `installed_images` IS AN OBSERVATION. NEVER FEED ONE FROM THE
> OTHER.** Letting a reading become a deployment is the R-166 category error one field over. They will
> normally agree; when they disagree that is a signal.
>
> **4. THE TRAP THIS RELEASE SET FOR ITSELF, and it would have shipped silently.** `Stack.TemplateImages`
> is read from the app's LIVE compose file — which is now the RENDERED one. On a frozen app that file
> names the OLD version, so the update badge would have found installed == template and answered
> **„Naprakész" on exactly the apps that are behind**, with every test still green, because the new
> field has the same type and shape. The badge now reads `Stack.CatalogImages`, from the syncer's own
> clone. **A feature that silently inverts a previous feature is the failure mode to look for whenever
> a file changes meaning.**
> **2026-09-03 — v0.234.0. ONE GAP CLOSED, ONE TEST DEFECT OF MY OWN.**
>