Files
felhom.eu/REPORT.md
T

272 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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; and `golden_currency_gate.py` reads 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
1. **"a throwaway guest on demo-hp, disk on `/mnt/nvme-1tb` at its root."** `/mnt/nvme-1tb` does 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.md` still names the wrong path — that is R-461, not re-filed.
2. **"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.
3. **"Add the bake to the nightly-session checklist."** No document by that name exists in any repo
(`grep -rln nightly` finds none under `documentation/runbooks/`). The step went into the two
routines that do exist: `RUNBOOK-manual-build.md` §4.2 (the cadence) and this repo's
`CLAUDE.md` end-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 after `issued:` — 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):
```yaml
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 and `pct pull`ed, then scp'd to DooPlex, **before**
`pct fstrim` / `pct destroy` — `harness/teardown-9403.txt`.
- **Bake (drill VM):** `bake.log` scp'd out and markers counted **before** `pct destroy 9100`, the
`shred -u` and the `poweroff` — `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
1. **`felhom.eu/scripts/test_gate_decoys.py` reports a LIVE HOLE at HEAD, before this session:**
`observations/R-419: decoy PASSED (rc=0)` — the gate scans the report's first `Observations`
section and not an appended second one. Reproduced on a clean worktree of `4b2e560`. **FILED: R-471.**
2. **Four consecutive controller CHANGELOG headers carry no `MinAgent:` line** while the vouch runbook
says to read it from the header. **FILED: R-470.**
3. **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.**
4. **`mariadb:12.3` resolved to 12.3.3 in the harness and 12.3.2 on 9201** — the floating-pin class.
**NOT-A-FINDING: already R-446.**
5. **`target-selection.md` names `/mnt/nvme-1tb`, which does not exist on demo-hp.** **NOT-A-FINDING:
already R-461, and it says exactly this.**
6. **The session ran with permission prompts disabled**, which the workspace `CLAUDE.md` says 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.**