diff --git a/CHANGELOG.md b/CHANGELOG.md index 07c06ef..037de77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +## MariaDB finishes its own conversion — `MARIADB_AUTO_UPGRADE=1` on four db services, and an engine-major gate (2026-09-13, R-459 / R-469) + +**Templates changed: bookstack, kimai, nextcloud, romm — the db service's `environment:` list only. +No `image:` line moved, so `catalog_since` does NOT move** (the rule ties it to an image change). + +**The ruling** (operator, 2026-09-13, on `SPIKE-r459-mariadb-upgrade-2026-09-06.md`): an unconverted +MariaDB datadir is stable but never heals — the engine says `Check required!` on every start forever — +and the conversion costs ~7 s and takes its own system-table backup first. So every `mariadb:` sidecar +now carries `MARIADB_AUTO_UPGRADE=1`; `MARIADB_DISABLE_UPGRADE_BACKUP` stays unset — that backup is the +precaution. The setting is inert until an engine major actually moves. + +**Proven by the harness before it shipped** (throwaway LXC 9403 on demo-hp, destroyed after; +evidence `felhom.eu/documentation/audits/r459-close-2026-09-13/harness/`): + +| edge | verdict | the engine's own view AFTER (`engine_state_after`) | +|---|---|---| +| C3 (negative control) | **`failed`** — the harness still says no | — | +| E3 (app + engine 11.6 → 12.3) | `proven`, readback after = true | `12.3.3-MariaDB \| This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]` | +| E3b (engine half alone) | `proven`, readback after = true | same | + +The entrypoint, verbatim: `Backing up system database to system_mysql_backup_11.6.2-MariaDB.sql.zst` +→ `Starting mariadb-upgrade` → `Finished mariadb-upgrade` (6 s). `skipped due to $MARIADB_AUTO_UPGRADE` +appears **0** times in either TO log — it appeared on every start before this change. The abort still +starts and serves (spike §5.3), and the abort log still prints `MariaDB upgrade not required`, which +is the R-464 trap and not a soundness claim. + +**And a rule with a gate, because the Update button still takes no backup** (R-448 not shipped): +*until Slice 4 ships, no template may move a database-engine image across a MAJOR version* — four +MariaDB, eleven PostgreSQL services, found by image name. `scripts/check-engine-major.py` is the +fourth row of `catalog_gates.py` (`--fast`, git reads only); `.githooks/pre-push` now hands it the +push range. **It needs a parent commit and CI fetches at `--depth 1`** (the R-452 gap, not re-filed), +so on a shallow clone the runner skips it out loud; the hook is where it bites. Red-proof +(`scripts/test_gate_decoys.py`, 7 cases): `mariadb:11.6 → 12.3` and `postgres:16 → 17` REFUSED naming +the rule and its expiry; `11.6 → 11.8` passes; the version moving only in a comment, in kimai's +`serverVersion=` env, in README, or on the app's own image passes. The rule's removal is tracked as +`felhom.eu` R-469 so it is a deliberate act, not a lapse. + ## upgrade-test.py records the ENGINE's own view of itself (2026-09-06, R-459) — NOT A RELEASE **Scripts only. No template changed, and deliberately so** — nothing with `MARIADB_` in it is diff --git a/REPORT.md b/REPORT.md index bf04c2c..c414188 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,56 +1,51 @@ -# REPORT — `catalog_since` backfill (2026-09-02) +# REPORT — MariaDB finishes its own conversion, and the engine-major gate (2026-09-13) -*Overwritten each run. This records the most recent implementation only.* +*Overwritten each run. This records the most recent implementation only. The full run — golden bake, +the golden waiver, the live observation on demo-hp — is in `felhom.eu/REPORT.md`.* -## What was done +## What changed in this repo -One new optional key, `catalog_since`, in all **53** `.felhom.yml` files, plus the rule that keeps it -true in `CLAUDE.md`. **No `docker-compose.yml` was touched and no image pin moved.** +- `templates/{bookstack,kimai,nextcloud,romm}/docker-compose.yml` — `MARIADB_AUTO_UPGRADE=1` on the db + service, with a comment pointing at the spike. **No image line moved; `catalog_since` untouched.** +- `scripts/check-engine-major.py` (new) — refuses a `mariadb:`/`postgres:` pin that crosses a major + between the two ends of a push range. Exit 0 / 1 REFUSED / 2 INCONCLUSIVE. +- `scripts/catalog_gates.py` — fourth row, `--fast`, `--range=` passthrough, announced skip on a shallow + clone. `scripts/test_catalog_gates.py` pins the table and the skip (7 tests green). +- `scripts/test_gate_decoys.py` (new) — the `COVERS` literal for `felhom.eu`'s decoy-coverage gate and + seven cases (three facts refused / inconclusive, one genuine and three decoys passing). +- `.githooks/pre-push` — computes `--range=..` from git's stdin refs. +- `CLAUDE.md` — the engine-major rule with its expiry condition (R-448 → remove, tracked as R-469). +- `REUSE.md` — one convention row for the MariaDB sidecar env; the gates row now lists four. -This is slice 2 of the update arc opened by -`felhom.eu/documentation/audits/SPIKE-app-update-2026-09-01.md`. The consumer is felhom-controller -**v0.233.0**, which renders one Hungarian badge from it — *"Naprakész"* or -*"Frissítés elérhető — N napja"*. **No version number reaches the customer**, by operator ruling. +## Harness verdicts (Scenario A and B) — engine-state field quoted -## Baseline +| edge | verdict | `engine_state_after.bookstack-db.answer` | +|---|---|---| +| C3 | `failed` (negative control intact) | — | +| E3 | `proven` | `12.3.3-MariaDB \| This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again. [exit=1]` | +| E3b | `proven` | same | -| | | -|---|---| -| repo at start | `5d8f25f61189` — matched the task's stated baseline, had not moved | -| commit pushed | `69761cf91bfc` | +`skipped due to $MARIADB_AUTO_UPGRADE`: **0** lines in both TO logs. Conversion lines, verbatim: +`[Entrypoint]: Starting mariadb-upgrade` at 09:53:20 → `Finished mariadb-upgrade` at 09:53:26 (E3). -## How the dates were derived +## Gate texts (Scenario D) -A throwaway script walked each app's `templates//docker-compose.yml` history newest→oldest and -took the newest commit whose **set of `image:` values differs from its parent's**. A YAML parse, not a -line scan. +Refusal, verbatim (from the red-proof on a scratch clone): -**Two commits excluded by hash:** `214d448` and `30bd892` — the bentopdf pin move and its same-hour -revert. This repo's own CHANGELOG records them as a spike measurement, not a release; counting them -would date bentopdf 2026-09-02 for a pin that has not moved since `71828a8` (2026-07-12). +``` +ENGINE-MAJOR GATE FAILED: templates/kimai/docker-compose.yml service kimai-db moves mariadb 11 -> 12 (mariadb:11.6 -> mariadb:12.3). +RULE (app-catalog CLAUDE.md, operator ruling 2026-09-13): until the Update button takes a VERIFIED BACKUP as its precondition (Slice 4, felhom.eu OPEN-ITEMS.md R-448), no template may move a database-engine image across a MAJOR version. +EXPIRY: this rule is removed DELIBERATELY when R-448 ships — the removal is its own register row, not a silent edit. Until then, keep the engine within its major. +``` -**Verification.** The four multi-major anchors from the spike all reproduced exactly — nextcloud -`5e2c1ae`, grafana `b789acc`, calcom `147cee7`, vikunja `3fa63cd`, all **2026-07-18**. Six further -apps were re-checked against `git log` **by hand**: bentopdf, plex, crafty-controller, homebox, -bookstack, wanderer. All six correct. +Pass, verbatim (this push's own range, 4 compose files, 4 engine pins compared): -Range: **2026-02-15** (plex, still on the pin it was added with) to **2026-07-21**. Thirty-eight of 53 -sit on 2026-07-18, the catalog-wide bump. +``` +engine-major gate OK — no database engine crosses a major version (rule: CLAUDE.md, until Slice 4 / R-448 ships) +``` -Every file was re-parsed with `yaml.safe_load` after the edit and the key read back — 53/53 clean. +## Honest limits -## Gates - -| gate | result | -|---|---| -| `image-pins` | **OK** (exit 0) | -| `image-resolvable` | INCONCLUSIVE — Docker Hub throttled 6 of 65 unauthenticated manifest lookups. 59 verified, none dead. **Not caused by this change.** | -| `volume-persistence` | INCONCLUSIVE — the prober refused to report because its own canary came back UNDETERMINED on this host. **Not caused by this change** (it needs a scratch Docker host). | - -Entry point: `python3 scripts/catalog_gates.py`, overall exit **0**. - -## Known gap, filed rather than left implicit - -There is **no gate** asserting that a commit which changes an `image:` line also moves that app's -`catalog_since`. The gates runner fetches at `--depth 1` and has no parent commit to diff against, so -the gate needs a deeper fetch. Filed as a row in `felhom.eu/documentation/backlog/OPEN-ITEMS.md`. +- **CI cannot run the engine-major gate yet** — `.gitea/workflows/gates.yml` fetches at `--depth 1`. + The runner announces the skip; enforcement is the pre-push hook. Same gap as R-452, not re-filed. +- The harness proves the DATABASE half of bookstack only (R-460); the file half needs a browser.