Upgrade harness: four fixtures and seven real upstream edges from the update night (R-462)
gates / gates (push) Successful in 1s
gates / gates (push) Successful in 1s
Test code only — no template changed and no image: line moved. The update night walked real within-a-major upstream edges on scratch guest 9202 through the product's own guarded Update, against a PRIVATE DRILL CATALOG; the live catalog was never touched. This brings the expensive half of that work — the seed routes — back into the harness so the same edges can be run here WITH their ABORT step, which the box deliberately does not offer (09 6.1: whether the old image starts on migrated data is per-app and unpredictable). - upgrade_fixtures.py: ActualBudget, Navidrome, AudiobookShelf, Vikunja. Each seeds through the app's OWN interface (R-156); each carries a negative control run on every verify(), so a readback that has broken into always succeeding fails instead of passing everything. - upgrade-test.py: edges U1..U7, all real upstream moves existing 2026-09-21 that this catalog has NOT made, each holding its database engine constant. - Limitations kept: Navidrome and AudiobookShelf seed the DATABASE half only, and say so. OWED, stated so it is not mistaken for done: the U1..U7 harness RUNS, and with them the per-app ABORT answers. The code is in; the runs are not. Gates: catalog_gates.py --fast — image-pins, engine-major, catalog-since, copy-i18n all OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -1,70 +1,40 @@
|
||||
# REPORT — the engine-major rule, half lifted (R-469 + R-450)
|
||||
# REPORT — upgrade harness widened from the update night (2026-09-21)
|
||||
|
||||
**Base `18a6d2d8243e` → `5ff36d098cbc`.** No template moved. Architecture read first and named:
|
||||
`felhom.eu/documentation/architecture/09-update-architecture.md` §3 decision 5 (the 2026-09-13 ruling
|
||||
and its three precautions) and §6.1 (slice 4 as shipped).
|
||||
**Test code only.** No template was changed; no `image:` line moved; `git diff` touches exactly
|
||||
`scripts/upgrade_fixtures.py` and `scripts/upgrade-test.py`.
|
||||
|
||||
## Why now
|
||||
## What was done
|
||||
|
||||
The rule of 2026-09-13 named its own expiry — *until the Update button takes a verified backup as its
|
||||
precondition* — **precisely so it would be removed deliberately rather than forgotten.** That
|
||||
condition was met the same day: update arc Slice 4 shipped (controller v0.237.0/v0.238.0; any backup
|
||||
tier since v0.239.0). R-469 exists to make the removal a reviewed diff. This is it, and it removes
|
||||
exactly half.
|
||||
The update night (`felhom.eu/documentation/audits/DRILL-update-night-2026-09-21.md`) walked real
|
||||
within-a-major upstream edges on scratch guest 9202 **through the product's own guarded Update**,
|
||||
each app seeded and read back through its own front door, against a **private drill catalog** — the
|
||||
live catalog was never touched. This commit brings the expensive half of that work (the seed routes)
|
||||
back into the harness, so the same edges can also be run here **with their ABORT step**, which the
|
||||
box deliberately does not offer.
|
||||
|
||||
## What changed
|
||||
- Four new fixtures: `ActualBudget`, `Navidrome`, `AudiobookShelf`, `Vikunja`. Each seeds through the
|
||||
app's OWN interface (R-156) and each carries a negative control that runs on every `verify()`.
|
||||
- Seven new edges `U1`–`U7`, all real upstream moves that existed on 2026-09-21 and that this catalog
|
||||
has NOT made. Every one holds its database engine constant.
|
||||
|
||||
- **LIFTED — MariaDB.** The four `mariadb:` sidecars (`bookstack-db`, `kimai-db`, `nextcloud-db`,
|
||||
`romm-db`) may cross a major. They now have both halves: a verified backup in front of the Update,
|
||||
and `MARIADB_AUTO_UPGRADE=1` (R-459) whose conversion the harness WATCHED run on the E3/E3b edges
|
||||
with the seeded data read back after.
|
||||
- **NOT LIFTED — PostgreSQL and MySQL.** Postgres performs no `pg_upgrade` and REFUSES to start on an
|
||||
older major's datadir, across eleven templates (R-463). **A backup is a route BACK, not a
|
||||
conversion** — the app would simply not come up. MySQL has nothing measured at all. The refusal
|
||||
text now says this, instead of citing the shipped R-448.
|
||||
- **NEW — one edge, one migration (R-450's second half, recorded 2026-09-02, enforced now).** A
|
||||
MariaDB major must be the ONLY image move in its template in that commit. bookstack's `0b73e5e`
|
||||
moved the application 25.02.2 → 26.05.2 **and** MariaDB 11.6 → 12.3 together: two migrations behind
|
||||
one edge, and an unreadable failure when it breaks. The refusal names what it was bundled with.
|
||||
**Within a major is unaffected** and may still ride with anything.
|
||||
- **The gate PRINTS what it allowed**, by name and with the reason, rather than passing in silence. A
|
||||
lifted rule that goes quiet is a lifted rule nobody can audit.
|
||||
## What was proven, and where
|
||||
|
||||
## Red-proofs — two, each SEEN to fail
|
||||
Box-side, through the guarded Update, with the data read back before and after — evidence per app in
|
||||
`felhom.eu/documentation/audits/update-night-2026-09-21/apps/<app>/`. The harness-side runs of
|
||||
U1–U7 are **owed**: the code is in, the runs are not.
|
||||
|
||||
| the mutation | what failed |
|
||||
|---|---|
|
||||
| drop the own-edge check (`others = []`) | *FACT: kimai-db 11.6 → 12.3 BUNDLED with the kimai app bump: rc=0 expected 1* — the bookstack shape passes |
|
||||
| empty `LIFTED` | *GENUINE: kimai-db 11.6 → 12.3 ALONE (the R-469 lift): rc=1 expected 0* — the lift is undone |
|
||||
## Gates
|
||||
|
||||
Both reverted; **40 decoy cases green**. The old *"a lone MariaDB major is REFUSED"* case is now the
|
||||
*"…is ALLOWED"* case — that inversion is the lift itself. A third case pins the bundled PostgreSQL
|
||||
shape.
|
||||
`python3 scripts/catalog_gates.py --fast` — image-pins OK, engine-major OK, catalog-since OK,
|
||||
copy-i18n OK. `all catalog gates OK`, exit 0. The two slow gates need a container runtime and were
|
||||
not run; no template changed, which is what they inspect.
|
||||
|
||||
`catalog_gates.py --fast`: image-pins, engine-major, catalog-since, copy-i18n — **all OK**. The
|
||||
pre-push hook ran and passed; no `--no-verify`.
|
||||
## A catalog defect found by the night, filed and NOT fixed here
|
||||
|
||||
## Measured while here, and it belongs in this repo's record
|
||||
|
||||
A read-only sweep of all 66 unique pins against the public registries, from DooPlex, never from a box
|
||||
(`felhom.eu/documentation/audits/UPDATE-ARC-STATE-2026-09-21.md` §3):
|
||||
|
||||
- **46 of 58 exact pins are behind upstream today**; 39 within a major, 7 across one. The seven were
|
||||
all pinned 2026-07-18: nextcloud 34→35, paperless-ngx 2.20→3.2, claper 2.5→3.0, gokapi 1.9→2.2,
|
||||
homepage 1.13→2.4, sparkyfitness 0.17→1.7 (two images).
|
||||
- **6 of the 7 measurable floating pins have been repushed upstream since the catalog set them** —
|
||||
`postgres:16-alpine`, `postgres:15-alpine`, `redis:7-alpine`, `mariadb:11.4`, `mariadb:12.3`,
|
||||
`postgis:16-3.5-alpine`. Only `mariadb:11.6` has not. This is R-446 measured rather than theorised,
|
||||
and it is the case for recording digests at push time (put to the operator as `09` §3b Q6).
|
||||
- **`msdeluise/plant-it:0.10.0` is gone upstream.** The repo's own gate says INCONCLUSIVE (it refuses
|
||||
to read a `denied` stderr as "dead", correctly); two independent signals say it really is gone —
|
||||
Docker Hub's catalog API answers `object not found` for the whole repository, and the upstream
|
||||
GitHub repo 404s. The app is already `lifecycle: abandoned`, so this is the expected end state, not
|
||||
an incident. A deployed plant-it survives; it can never be redeployed.
|
||||
|
||||
## Rows
|
||||
|
||||
**R-469 PARTLY CLOSED** (MariaDB lifted; the PostgreSQL half stands until R-463).
|
||||
**R-450 half SHIPPED** (the own-edge clause; the automatic-within-a-major half is Slice 6 and awaits
|
||||
`09` §3b Q1–Q4). **R-605 filed** — a catalog gate that refused to run and one that ran and could not
|
||||
decide print the same word.
|
||||
Two apps are presented to the household as UNHEALTHY while working perfectly: `tandoor`'s
|
||||
`.felhom.yml` probe names port 8080 where the container listens only on 80, and `zipline`'s names
|
||||
`/api/health` where that app answers 404 — **while the compose healthcheck in the same file uses
|
||||
`/api/healthcheck` and is correct**. A static sweep of all 53 templates comparing the two health
|
||||
checks against each other finds both, plus `wger` and `home-assistant` as unmeasured candidates and
|
||||
`adventurelog` as a false positive. Filed as **R-618** with the proposed gate; deliberately not
|
||||
fixed in the same commit as the harness change.
|
||||
|
||||
Reference in New Issue
Block a user