# REPORT — the undo reaches the fleet, and the household is TOLD (controller v0.264.0, hub v0.120.0) 2026-09-23 (evening). Repos touched: **felhom-controller** (v0.264.0, `bc27894`), **felhom.eu** (hub v0.120.0 `d3b2848`, manifest `7caa24f`, docs, register, evidence), **admin/app-catalog-drill** (drill commits, reset to live `main` at the end). **felhom-agent, app-catalog-felhom.eu: untouched.** Architecture read and named: `documentation/architecture/09-update-architecture.md` (§3 decision 15, §6.4). Evidence: `documentation/audits/undo-fleet-2026-09-23/README.md`. **Method: endpoint-level** (the endpoints the UI invokes; mails read from the catch-all mailbox; no browser). --- ## 1. Not done, or changed | item | state | |---|---| | Part 0 — floor 0.263.2 | **done**, read back (`00-floor-0.263.2.txt`) | | Part 1 — R-646 startup pass | **done**; live: 3 recorded on 9202, 10 on 9201, 0 skipped | | Part 2 — two events, hub v0.120.0 first | **done**; hub deployed 11:49Z, before any box ran v0.264.0 | | Part 3 — R-606 | **done, with leftovers → R-647** (a reader in the other language than the box sees a HELD sentence in the box's language; the English mail's raw `Note:` line keeps the Hungarian `copy_holds` phrase) | | Part 4 — R-620 | **done** | | Part 5 — live proof | **done**; all proofs passed → **floor raised to 0.264.0** | | "standing apps untouched" | **NOT fully met.** The harness's „Mentés most" is a whole-box backup: on 9201 it stopped and restarted 9 of the 10 standing apps for their volume dumps, twice (a few seconds each). All healthy afterwards, files byte-identical. **R-648** filed. | | the brief's Hungarian undone line | **changed:** it ends „semmi nem veszett el, nincs teendőd." — the brief's „teendője nincs" is the formal „ön" voice; the product speaks „te" everywhere and the voice gate ratchets the formal form (18). | | the customer cooldown | **changed shape:** a NEW customer-side register (`perAppCustomerCooldownEvents`) instead of putting the customer leg of R-389's register to work — that would also have changed `app_start_failed`'s household grain, which nobody asked for (pinned by a test). | | a box-side seed + two page toggles | **added, not in the brief:** the box pushes its own `EnabledEvents` to the hub on every notifications-page save, so the hub migration alone would be undone by the next save. | **Claims in the brief, checked:** 1. *"No app-update event exists today"* — **true for apps.** The only near-misses: `controller_updated` is the controller's OWN self-update, and `update_available` exists only as a debug-page test option — never emitted, and not in the hub's allow-list (it would 400). 2. *"`enabled_events` is the household's only mail gate"* — **wrong.** Four more gates stand in the way: the `operatorOnlyEvents` register, a blocked customer, a missing e-mail address, and the cooldown — keyed `customer:type`, which would have merged two apps' mails into one. And the box OVERWRITES the hub's list on each page save (above). 3. *"9201 can follow the drill catalog without disturbing its standing apps"* — **true for the catalog, false for the harness.** Measured: the 10 standing apps' compose + `.felhom.yml` were byte-identical before, during and after (the drill differed from live only in the throwaway apps). The disturbance came from the harness's whole-box backup press (R-648), not from the catalog. ## 2. Floor raises, read back from the hub | raise | read back | fleet | |---|---|---| | 0.263.2 / MinAgent 0.131.0 | `min_controller_version=0.263.2`, `min_agent=0.131.0` | `00-floor-0.263.2.txt` | | **0.264.0 / MinAgent 0.131.0** (14:35:08) | `min_controller_version=0.264.0`, `min_agent=0.131.0`; hub log `managed floor SERVED for demo-felhom: floor 0.264.0 … from declared` | demo-felhom 0.263.2 → **0.264.0 in 20 s**; demo-hp 9201 already 0.264.0 (`50-floor-0.264.0.txt`) | ## 3. The hub migration — rows changed (one-time, add-only) | customer | before | after | |---|---|---| | `demo-felhom` | 10 types | the same 10 + `app_update_undone`, `app_update_held` | | `demo-hp` | `null` (nothing on) | `["app_update_undone","app_update_held"]` | | `_resend-rotation-test` | `[]` | `["app_update_undone","app_update_held"]` | Guard row `seed_app_update_events_v1 = 2026-09-23T11:49:03Z`. E-mail column never selected. ## 4. The mails (9201, customer demo-hp) and their `notification_log` rows | row | time (UTC) | type | leg | subject as received | |---|---|---|---|---| | 937 / 938 | 12:15 | undone | op / household | op `⚠️ demo-hp: app_update_undone` · hh **`Figyelmeztetés: vikunja: a frissítés nem sikerült, az alkalmazás a korábbi változattal fut`** | | 939 / 940 | 12:16 | held | op / household | op `🔴 demo-hp: app_update_held` · hh **`Hiba: vikunja: az alkalmazás leállítva, visszaállítás szükséges`** | | 942 / 943 | 12:29 | undone | op / household | hh **`Warning: glance: the update did not work; the app runs on its previous version`** | | 944 / 946 | 12:31 | held | op / household | hh **`Error: glance: the app is stopped and needs a restore`** | All 8 `sent`. The box language was switched to English once, between the two rounds (the hub had two reports in English before the second round). Household bodies carry the box's own sentence (the undone line / the hold sentence) in the household's language; the operator's carry the Hungarian wire text. Verbatim bodies: `41-mails-as-received.md`. **Per-app cooldown, live:** row 943 went out 14 minutes after row 938, inside the 6-hour customer cooldown. ## 5. The pages in both languages (9202, then 9201) - undone: hu *„A(z) vikunja frissítése 2026-09-23 14:00-kor nem sikerült. A doboz automatikusan visszaállította az előző változatot és az adatokat — semmi nem veszett el."* · en *"The update of vikunja at 2026-09-23 14:00 did not succeed. The box put back the previous version and its data automatically — nothing was lost."* - held, box hu: `?lang=hu` → the Hungarian hold sentence; `?lang=en` → *"The update did not succeed, and the automatic undo did not either. … own drive, 2026-09-23 13:58 — this copy holds the settings, the database and the data volumes."* — the whole sentence English now (before v0.264.0 only the prefix was). - `GET /api/stacks/vikunja?lang=en`: label *"The update did not succeed"*, error and hold in English. - **Leftover (R-647):** box en + `?lang=hu` showed the English hold. ## 6. Red-proofs (each seen failing, then restored) **Controller (9):** backfill stores nothing → `TestR646_…` *"the current app must have its record"*; `dropped()` not called → `TestR620_…` *"want exactly TWO WARN lines, got 0"*; undone event not emitted → *"exactly ONE app_update_undone event, got []"*; held event not emitted → *"…got []"*; page `updateErrorText` not localised → `TestR606_UpdateSentencesFollowTheReader`; hold sentence ignores the language → `undo_hold_test.go:62`; `UpdateErrorIn` returns the Hungarian → *"English = „A frissítés nem indult el…""*; box seed not run → *"got [backup_failed offbox_enlarge_blocked]"*; sink not wired in main → *"SetUpdateEventSink is never called"* (first mutation did not compile; retried with a compiling one). **Hub (5):** seed call does nothing → *"c1: enabled_events = [backup_failed], want […]"*; customer per-app suffix removed → *"want 3 household mails …, got 2"*; allow-list entry removed → *"must be in allowedEventTypes"*; app-named headline removed → subject *"%s: a frissítés…"*; seed default removed → *"defaultSeedEvents must include app_update_undone"* (first attempt matched no test; retried with a new test that names both types). Every `-run` was checked for `=== RUN`. Gates: controller `go test ./...` rc=0, `controller_gates.py` rc=0; hub `go build/vet/test` rc=0, `repo_gates.py` rc=0. The R-389 fence test was widened on purpose, with its reason; the settings-page parity fixture was regenerated (measured diff: exactly the two new toggles). ## 7. Rows **Closed (3, moved to `CLOSED-ITEMS.md`):** R-606, R-620, R-646. **Opened (2):** R-647 (the three leftovers), R-648 (the whole-box backup press). **Open rows 335 → 334.** `09` §6.4 parts 2 and 3 marked SHIPPED; the capability map's undo row now carries the mail; STATUS rewritten. ## 8. Teardown — three layers - **machine:** 9202 and 9201 — throwaway apps removed through the product (0 volumes, 0 undo copies), test images removed, `controller.yaml` identical to the saved copy, catalog on live `cfcfe52`, 9201's language back to `hu`, standing apps deployed and healthy. Both on 0.264.0 (= the floor). - **host:** nothing provisioned on demo-hp. - **hub:** v0.120.0 (planned); floor 0.264.0. Scratchpad password files deleted; hub DB copies deleted. - **drill repo:** reset to live `main`, read back from the remote.