# Phase 2.1 — MariaDB across a major, through the REAL Update button. PROVEN. **The first time this has ever been pressed through the product.** R-459 measured the conversion on the harness; R-469 lifted the gate on 2026-09-21 morning; the BUTTON had never been used for it. **Venue:** guest 9202, controller v0.261.0. **App:** `nextcloud` — the app image does NOT move (`nextcloud:34.0.1-apache` throughout); only the `mariadb:` sidecar, **11.6 → 12.3**. **Seeded and read back through the app's own CLI:** `occ user:add` created `drillb9a9d0` ("The account … was created successfully"), `occ user:info` found it **before** the move (control C1) and **after** it, each time with the fixture's own negative control — a uid that cannot exist must read as absent — passing. ## The four observables of `SPIKE-r459-mariadb-upgrade-2026-09-06` | # | observable | before | after | |---|---|---|---| | 1 | `mariadb_upgrade_info` — the datadir's own record | `11.6.2-MariaDB` | **`12.3.3-MariaDB`** | | 2 | the engine's OWN check (R-464: never the log line) | *"Access denied"* — the probe was unauthenticated, see the note below | **„This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again."** | | 3 | the entrypoint's own lines | — | **„Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!"** → „Starting mariadb-upgrade" → „Finished mariadb-upgrade" | | 4 | the engine's own pre-upgrade backup | absent | **`/var/lib/mysql/system_mysql_backup_11.6.2-MariaDB.sql.zst`, 631 905 bytes** | | 5 | the version actually running | `11.6.2-MariaDB-ubu2404` | **`12.3.3-MariaDB-ubu2404`** | | 6 | container | `mariadb:11.6` running, 0 restarts | **`mariadb:12.3` running, 0 restarts** | **Observable 3 is the one that matters most**, because R-459's whole finding was that MariaDB can apply a major and *skip* the conversion quietly, printing `skipped due to $MARIADB_AUTO_UPGRADE`. That line is **absent**; the entrypoint says the conversion was **detected, started and finished**. **And observable 2 confirms R-464's own warning about the instrument:** the command exits **1** when it means *no upgrade is needed*. The exit code carries no usable information here; the SENTENCE is the discriminator, which is exactly what R-464 says. ## The four version observables, all agreeing pinned nextcloud:34.0.1-apache · mariadb:12.3 · redis:7-alpine installed nextcloud:34.0.1-apache · mariadb:12.3 · redis:7-alpine compose nextcloud:34.0.1-apache · mariadb:12.3 · redis:7-alpine inspect nextcloud-db mariadb:12.3 running=true restarts=0 nextcloud nextcloud:34.0.1-apache running=true restarts=0 nextcloud-redis redis:7-alpine running=true restarts=0 ## Two honest notes on the instrument 1. **The first `engine-before.txt` capture asked the engine without credentials** and got `ERROR 1045 Access denied` where observable 2 should be. The probe was corrected mid-run to pass `--user=root --password=$MARIADB_ROOT_PASSWORD` (the recipe `upgrade-test.py`'s own `ENGINE_PROBES` already uses) and the AFTER capture above was retaken with it. The BEFORE value for observable 2 is therefore **not measured**, and is stated as such rather than inferred from observable 1. 2. The `ls` in observable 4 printed `(no pre-upgrade backup file present)` **after** listing the file, because it globs two patterns and only one matched. The file is there; the fallback text is the shell's, not the engine's. Noted so nobody reads the line as a contradiction. ## What this settles The MariaDB half of `09` §3 decision 5 and R-469's lift is now proven **through the button a household presses**, not only on the harness — conversion detected, run, finished, the engine's own pre-upgrade backup taken, and the customer's account still there afterwards.