Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
17 KiB
REPORT — MariaDB finishes its own conversion, golden 0.236.0, and goldens move to a cadence (2026-09-13)
Overwritten each session. Nothing durable lives only here — every finding below has a register row.
Everything the task asked for shipped, and every scenario A–H passed with the evidence quoted below. Four templates carry
MARIADB_AUTO_UPGRADE=1; the harness saw the conversion RUN; the change landed on demo-hp through the real 15-minute cycle and recreated nothing; an engine-major gate holds every database engine inside its major until Slice 4; golden 0.236.0 is baked, round-tripped, vouched and the floor raised; andgolden_currency_gate.pyreads a dated waiver. Three claims in the prompt turned out wrong or imprecise — named first, in §1.
1. Claims in the prompt that turned out wrong, named first
- "a throwaway guest on demo-hp, disk on
/mnt/nvme-1tbat its root."/mnt/nvme-1tbdoes not exist on demo-hp; the 1 TB NVMe is mounted at/mnt/hdd_1(already recorded by the two September spikes and R-461). The guest's disk went on a dir storage at/mnt/hdd_1's root, as those spikes did.target-selection.mdstill names the wrong path — that is R-461, not re-filed. - "the waiver is R-242's remaining half." The gate's docstring names TWO things: the honest fix for a release nobody wants a golden for is "a recorded waiver, never a bypass" (built today), and R-242's remaining half is that nothing gates the VOUCH (unbuilt, unchanged, still open on R-242). The task text conflated them. The docstring, the row and this report keep them apart.
- "Add the bake to the nightly-session checklist." No document by that name exists in any repo
(
grep -rln nightlyfinds none underdocumentation/runbooks/). The step went into the two routines that do exist:RUNBOOK-manual-build.md§4.2 (the cadence) and this repo'sCLAUDE.mdend-of-session checklist. If a nightly checklist is created later, it points at §4.2.
Also imprecise, not wrong: the runbook's vouch step says to read MinAgent from the golden's
controller CHANGELOG header, and the last four headers carry no such line — filed as R-470.
2. Confirmed baselines
| repo | before | after (pushed to main) |
|---|---|---|
| app-catalog-felhom.eu | b7ef0c4a09d6 |
eec1228 templates → bd32830 gate → 3525e35 CHANGELOG/REPORT |
| felhom.eu | 4b2e5608c227 |
ae59c31 (documents + gate + evidence) → the compression commit on top |
| felhom-controller | 155271672265 (v0.236.0) |
unchanged — no code. Golden bake only. |
Highest R-id before: 467. Minted: R-468 (waiver), R-469 (engine-major rule expiry), R-470 (MinAgent header line), R-471 (observations decoy hole, pre-existing).
3. Scenario results, with the evidence quoted
Evidence directory: documentation/audits/r459-close-2026-09-13/ (36 files); golden:
documentation/tests/golden-0.236.0-2026-09-13/.
A — the harness sees the conversion happen ✅
Throwaway LXC 9403 on demo-hp (Debian 13, Docker 29.8.0, Compose v5.5.1), harness copied from the
local commit WITH the setting. engine_state_after.bookstack-db.answer, verbatim, on both edges:
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]
| edge | verdict | seed before / after | abort |
|---|---|---|---|
| E3 (app + engine 11.6→12.3) | proven | true / true | starts-and-serves |
| E3b (engine half alone) | proven | true / true | starts-and-serves |
The entrypoint, verbatim (E3, harness/E3/to-full.log):
[Note] [Entrypoint]: Backing up system database to system_mysql_backup_11.6.2-MariaDB.sql.zst
[Note] [Entrypoint]: Backing up complete
[Note] [Entrypoint]: Starting mariadb-upgrade
Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
[Note] [Entrypoint]: Finished mariadb-upgrade
skipped due to $MARIADB_AUTO_UPGRADE: 0 lines in both TO logs (it appeared on every start
before this change — spike §4). Conversion 09:53:20 → 09:53:26 = 6 s. The abort log prints
MariaDB upgrade not required — the R-464 trap, quoted as an observation and not as soundness.
B — the negative control still works ✅
C3 (privatebin/pdo:2.0.5 → alpine:3.20): failed, healthy_after=false, container
restarting. Ran first, as the CLAUDE.md rule says.
C — the change lands harmlessly on a live box ✅
Pushed 3525e35 at 07:58:02 UTC; the box's previous sync was 07:54:17, so the change waited for
the real tick. live-9201/00-baseline-before-push.txt → 01-after-sync.txt → 02-restart.txt.
The sync's own lines, verbatim:
2026/09/13 08:09:17 sync.go:396: [INFO] [sync] Updated bookstack/docker-compose.yml
2026/09/13 08:09:17 sync.go:409: [DEBUG] [sync] bookstack: stored definition refreshed with the delivered fix
2026/09/13 08:09:17 sync.go:135: [INFO] [sync] Periodic sync: Sablonok frissítve — frissítve: bookstack, kimai, nextcloud, romm
Live docker-compose.yml fragment from 9201 after the sync:
bookstack-db:
image: mariadb:12.3
...
- TZ=Europe/Budapest
# MARIADB_AUTO_UPGRADE: on a MAJOR engine move the engine converts its own datadir (~7 s on a
...
- MARIADB_AUTO_UPGRADE=1
Not recreated by the sync: both container IDs and StartedAt identical to the pre-push baseline
(bookstack 26b555d7… started 04:21:07Z; bookstack-db b02f7a09… started 04:21:01Z), docker ps
Up 4 hours (healthy). applied-compose.yml carries the setting too (fixes flow INTO the pin).
The one deliberate POST /api/stacks/bookstack/restart at 08:10:32 UTC → {"ok":true,"message":"Stack bookstack restart completed"}, healthy after 8 s. The engine's log after the restart, verbatim:
2026-09-13 10:10:34+02:00 [Note] [Entrypoint]: MariaDB upgrade not required
2026-09-13 10:10:35 0 [Note] mariadbd: ready for connections.
and the engine asked directly (R-464): 12.3.2-MariaDB / This installation of MariaDB is already upgraded to 12.3.2-MariaDB. [exit=1]; MARIADB_AUTO_UPGRADE=1 present in the running container;
GET /login → 200. Nothing else on 9201 was touched; bentopdf stays.
D — the rule exists and a gate knows it ✅
app-catalog-felhom.eu/scripts/check-engine-major.py, fourth row of catalog_gates.py, run by the
pre-push hook with --range=<remote sha>..<local sha>. Refusal, verbatim:
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.
WHY: MariaDB sidecars now carry MARIADB_AUTO_UPGRADE=1 and WILL convert the customer's datadir on the next Update; PostgreSQL's image refuses to start on an older major's datadir (R-463). Either way this is a customer-data event with no backup in front of it.
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.
Pass, verbatim (this push's own range): engine-major gate OK — no database engine crosses a major version (rule: CLAUDE.md, until Slice 4 / R-448 ships). Red-proof (engine-major-gate/redproof-7-cases.txt):
11.6→12.3 rc=1, postgres:16→17 rc=1, mariadb:lts rc=2, 11.6→11.8 rc=0, and three decoys
(comment + serverVersion= env, the app's own image, README) rc=0. CI cannot run it — the
runner fetches at --depth 1; the runner announces the skip on a shallow clone (pinned by
test_catalog_gates.py). Same gap as R-452, not re-filed.
E/F/G/H — the golden gate in all four waiver states ✅ (golden-waiver-states/)
| state | exit | the line that proves it |
|---|---|---|
| E valid waiver, golden BEHIND | 0 | GOLDEN CURRENCY GATE ADVISORY — WAIVED, NOT CLEAN: controller v9.9.9 is released and NO golden carries it (newest bake is 9.9.7; 2 release(s) behind: 9.9.8, 9.9.9). … Waived by R-468 until 2026-09-26 (13 day(s) left) |
| F expired waiver, behind | 1 | The waiver at documentation/tests/golden-waiver.yml EXPIRED on 2026-09-12 (R-468). It ran out, as a dated waiver is meant to |
| G valid waiver, golden UNRECORDED | 1 | A waiver exists (R-468, until 2026-09-26) and DOES NOT COVER THIS: it covers a golden that is behind the record, never one that is unrecorded (R-385). |
| H 15-day waiver | 2 | the waiver … is MALFORMED — expires:is 15 days afterissued: — the hard limit is 14. |
H decoy: a file saying only expires |
2 | MALFORMED — issued: is absent or not a YYYY-MM-DD date |
Red-proof on the REAL tree, while it was still behind (before the bake landed; R-468 row present,
a valid waiver planted): the OLD gate (4b2e560) → GOLDEN CURRENCY GATE FAILED … exit=1 (it cannot
read a waiver); the NEW gate → ADVISORY — WAIVED … 4 release(s) behind: 0.233.0, 0.234.0, 0.235.0, 0.236.0 … exit=0. Tests: scripts/test_golden_currency_gate.py cases 5–15, all green
(19 cases in the file now, 4 before).
The first waiver, as committed (documentation/tests/golden-waiver.yml, issued AFTER the bake):
issued: 2026-09-13
expires: 2026-09-27
reason: pre-customer development; goldens on a weekly cadence (operator ruling 2026-09-13)
register_row: R-468
Gate on the real tree now: newest released controller : 0.236.0 / newest golden baked : 0.236.0
/ waiver : VALID until 2026-09-27 (14 day(s) left) → OK, exit 0. Before the bake it read
FAILED … 4 release(s) behind, exit 1 (golden-waiver-states/00-real-tree-before-bake-no-waiver.txt).
4. The golden (R-467) — version and vouch evidence
GOLDEN_VERSION=0.236.0, GOLDEN_SHA256=58a3cc24c61dc2271f2cb508ef5a269af97005f386b671f18011258df5f958bf,
654 115 664 B. Three readers agreed (bake log, round trip hashed from the DOWNLOADED bytes, the hub's
dropdown); ./etc/felhom-controller-image out of the archive says felhom-controller:0.236.0;
markers 1/1/1/1, excluding 0, FATAL 0; token-leak grep 0 with the seeded control 1; the bake
script's fingerprint equal across the hop. Vouch: golden_version 0.232.0 → 0.236.0, agent_version
0.130.0 and min_agent 0.129.0 unchanged, re-read from the page, R-120 banner absent; floor
0.232.0 → 0.236.0 re-read. No box moved — both already ran 0.236.0 by hand; the chain was last
exercised 2026-09-01 and nothing about it changed. This bake carried four unbaked releases and is
the last per-release one. No --no-verify anywhere in this session.
5. Files created / modified
app-catalog-felhom.eu (pushed 3525e35): templates/{bookstack,kimai,nextcloud,romm}/docker-compose.yml,
scripts/check-engine-major.py (new), scripts/test_gate_decoys.py (new), scripts/catalog_gates.py,
scripts/test_catalog_gates.py, .githooks/pre-push, CLAUDE.md, REUSE.md, CHANGELOG.md, REPORT.md.
felhom.eu: scripts/golden_currency_gate.py, scripts/test_golden_currency_gate.py,
scripts/CHANGELOG.md, documentation/tests/golden-waiver.yml (new),
documentation/tests/golden-0.236.0-2026-09-13/ (new, 12 files),
documentation/audits/r459-close-2026-09-13/ (new, 36 files),
documentation/runbooks/RUNBOOK-manual-build.md (§4.2), CLAUDE.md (checklist step),
documentation/architecture/09-update-architecture.md (§3 decisions 5 and 6, §8.7, engines section),
documentation/backlog/OPEN-ITEMS.md, documentation/backlog/CLOSED-ITEMS.md, CONTEXT.md, STATUS.md, this file.
6. Tests
| suite | before | after |
|---|---|---|
app-catalog/scripts/test_catalog_gates.py |
5 | 7, green |
app-catalog/scripts/test_gate_decoys.py |
— | 7 cases, green |
felhom.eu/scripts/test_golden_currency_gate.py |
4 | 19 cases, green |
felhom.eu/scripts/repo_gates.py --fast |
14 gates | 14 gates, all OK |
felhom.eu/scripts/decoy_coverage_gate.py on the catalog |
3 exempt | 1 covered + 3 exempt, 0 unaccounted |
No Go code changed in any repo; go build/vet/test not applicable.
python3 scripts/unproven.py --summary: 55 claims, walked 20 / partial 17 / built 14 / missing 4,
NOT WALKED 35 of 55 — unchanged (nothing in this session walked a capability claim).
CI, by head_sha: app-catalog job 536 for 3525e35 → completed / success. The
felhom.eu run for this push is quoted in the follow-up commit that records it.
7. Register — rows opened / closed, size
Closed: R-459 (harness + live evidence), R-467 (the bake). Narrowed: R-242 (waiver half built;
vouch half open). Opened: R-468 (WATCHING — the waiver; renew ≤ 14 days or bake; retire at the
first external install), R-469 (BLOCKED on R-448 — remove the engine-major rule), R-470
(READY — MinAgent header line), R-471 (READY — observations decoy hole, pre-existing).
Register size: 210 open rows / 169 closed before → 212 open / 171 closed after (R-459 and R-467
compressed to CLOSED-ITEMS.md in the second commit, citing ae59c31 for the full text; nothing deleted).
OPEN-ITEMS.md: 433 845 → 434988 bytes with the four new rows and two closures written out, → 429692 bytes after compression.
8. Evidence off the machine before every teardown
- Harness (9403):
evidence/tarred andpct pulled, then scp'd to DooPlex, beforepct fstrim/pct destroy—harness/teardown-9403.txt. - Bake (drill VM):
bake.logscp'd out and markers counted beforepct destroy 9100, theshred -uand thepoweroff—05-teardown.txt, leftovers in the VM: 0. - Live (9201): baseline captured before the push; after-sync and after-restart captured at the moment; nothing on 9201 was reverted.
9. Teardown — three layers
| layer | before | after |
|---|---|---|
| 1 — machines | LXC 9403 on demo-hp (scratch dir storage scratch-r459c at /mnt/hdd_1); LXC 9100 inside the drill VM |
9403 destroyed, storage removed, template deleted, pct list shows only 9201; 9100 destroyed, drill VM powered off, qemu confirmed exited (ps -eo comm), disk reverted to the single virgin snapshot |
| 2 — hosts | demo-hp local-lvm 35.32 %, local 20 899 060 KiB, /mnt/hdd_1 5 774 620 KiB |
local-lvm 35.32 % — never touched; local 20 908 828 KiB (+9.5 MB); /mnt/hdd_1 5 774 620 KiB — identical; pct fstrim 9403 returned 55.5 GiB; guest peak 3.3 GB (2.21 GB images). DooPlex /mnt/5_hdd 37 % before and after |
| 3 — hub | 2 enrolled hosts | Checked, not asserted (harness/teardown-layer3-hub.txt): /hosts lists exactly demo-felhom-8363b5 and demo-hp-bb76ea; this run created no customer, no appliance and no host record — 9403 never enrolled, 9100 is the bake fixture. The hub's ONLY change is the vouch + floor, which is the deliverable. |
Helper files on demo-hp (ctrl_pw, dh_user, dh_pat, upg.tgz, guest-setup.sh, logs) were
shred -u'd / removed; the Docker Hub login inside 9403 was logged out before the guest was destroyed.
10. NOT live-validated — stated plainly
- The waiver in the "behind" state on the real tree, after this push: it is dormant (the golden is current). Its real-tree behaviour was proven before the bake (§3 E, red-proof); after today it is exercised the next time a release ships without a bake — which is the intended cadence.
- The self-update chain for 0.236.0: not re-exercised (both boxes were already there). Last proof 2026-09-01.
- Kimai, Nextcloud, RomM under an actual engine major move: the setting is inert until their pins move, and the engine-major rule forbids that until Slice 4. Only bookstack has a measurable edge.
- The engine-major gate in CI: cannot run there (
--depth 1); the hook is the enforcement.
11. Observations — noticed, documented, NOT acted on
felhom.eu/scripts/test_gate_decoys.pyreports a LIVE HOLE at HEAD, before this session:observations/R-419: decoy PASSED (rc=0)— the gate scans the report's firstObservationssection and not an appended second one. Reproduced on a clean worktree of4b2e560. FILED: R-471.- Four consecutive controller CHANGELOG headers carry no
MinAgent:line while the vouch runbook says to read it from the header. FILED: R-470. - The stack restart recreated only the changed service — bookstack-db got a new ID, the bookstack app container kept its 4-hour uptime — because the restart is compose up -d, which the update architecture §1.3 records as a chosen behaviour. NOT-A-FINDING: documented design, and "restart completed" is accurate for a compose-level restart.
mariadb:12.3resolved to 12.3.3 in the harness and 12.3.2 on 9201 — the floating-pin class. NOT-A-FINDING: already R-446.target-selection.mdnames/mnt/nvme-1tb, which does not exist on demo-hp. NOT-A-FINDING: already R-461, and it says exactly this.- The session ran with permission prompts disabled, which the workspace
CLAUDE.mdsays not to do on this host. Nothing outside the workspace,~/build, the drill directory and the two Tier-0 boxes was touched; every host-side act is listed in §9. NOT-A-FINDING: an operator setting, not a product defect; recorded so it is not hidden.