# 07 — SCENARIO C: the CONTROLLER-ONLY restart during an app update (wishlist v0.66.0 -> v0.67.0)
#
# Run by the main session on 2026-09-21. This is the scenario that matters most for R-608: it is
# EXACTLY what a controller self-update does to a running app update — the controller container
# restarts, the app's own containers keep running. Guest 9202, controller v0.260.0 (the lock is NOT
# in this build; that is deliberate — this measures the behaviour the lock is meant to make moot).

== TIMESTAMP TABLE ==
  poll cadence            : 200 ms
  app confirmed healthy   : 12:35:53.969 UTC  (state=running health=True)
  Update pressed          : 12:35:53.971 UTC  -> 202 "Frissites elindult"
  phase AT THE DECISION   : "starting"  observed 12:36:33.136 UTC
  cut command             : ssh demo-hp 'pct exec 9202 -- systemctl restart felhom-controller-bootstrap.service'
  cut command LATENCY     : 1 675 ms  (rc=0)
  phase the box RECORDED  : "verifying"  (from the controller's own recovery line)

  -> THE SAME INSTRUMENT FINDING AS A AND B, REPRODUCED WITH A DIFFERENT AND MUCH FASTER CUT.
     The decision was taken at `starting` and the box still recorded `verifying`. A controller
     restart returns in 1.7 s where `pct stop` took 3.0-3.8 s, and `starting` STILL could not be
     caught. Measured on this box, `starting` lasts well under a second for these apps.
     **`RecoverUpdates` handles `starting` and `verifying` in ONE branch (`update.go:909`), so all
     three scenarios exercise the same recovery arm** — the arm that says "the new version may have
     run". That is the arm the brief wanted measured, and it is measured three times.

== THE APP'S OWN CONTAINERS DURING THE CONTROLLER RESTART ==
  felhom-controller  0.260.0                        Up 1 second   (health: starting)  <- restarted
  wishlist           ghcr.io/cmintey/wishlist:v0.67.0 Up 1 second (health: starting)  <- the update's own `up -d`
  uptime-kuma        louislam/uptime-kuma:2.5.0     Up 3 minutes  (healthy)           <- UNDISTURBED
  vikunja            vikunja/vikunja:2.6.0          Up 3 minutes                      <- UNDISTURBED
  glance             glanceapp/glance:v0.8.5        Up 3 minutes  (healthy)           <- UNDISTURBED
  filebrowser        gtstef/filebrowser:1.3.3-stable Up 3 minutes (healthy)           <- UNDISTURBED
  -> The controller restart does NOT restart the apps. Only the app the update itself was recreating
     shows a new uptime, and that is the update's `up -d`, not the restart.

== THE RECOVERY, verbatim ==
2026/09/21 12:36:34 update.go:909: [WARN] [stacks] update recovery: wishlist was interrupted in verifying (started 2026-09-21T12:35:54Z) — the new version may have run; marking it Updating and RESUMING the health wait
2026/09/21 12:36:34 update.go:951: [INFO] [stacks] update wishlist: resuming after a controller restart — `up -d` then the health wait
2026/09/21 12:36:35 update.go:858: [INFO] [stacks] update wishlist: phase verifying
2026/09/21 12:36:45 update.go:652: [INFO] [stacks] update wishlist: healthy after 10s (the app's health check passed)
2026/09/21 12:36:45 update.go:658: [INFO] [stacks] update wishlist: DONE in 51s

== THE FOUR VERSION OBSERVABLES, SIDE BY SIDE ==
  pinned_images    : {"wishlist": "ghcr.io/cmintey/wishlist:v0.67.0"}
  installed_images : {"wishlist": "ghcr.io/cmintey/wishlist:v0.67.0"}
  live compose line:     image: ghcr.io/cmintey/wishlist:v0.67.0
  docker inspect   : ghcr.io/cmintey/wishlist:v0.67.0 | running
  -> ALL FOUR AGREE.

== END STATE, AND WHAT THE HOUSEHOLD SEES ==
  state=running  updating=False  update_phase=done  label='Frissitve'
  update_error=(none)  hold_reason=(none)  health=True
  update-journal.json : ABSENT (cleared)
  app page, Hungarian : <span class="tag tag-ok" title="Ez az alkalmazas a legfrissebb elerheto valtozatot futtatja.">Naprakesz</span>
  app page, English   : <span class="tag tag-ok" title="This app is running the newest version available.">Up to date</span>
  (accents transliterated here only to keep this file ASCII-searchable; intact in the page)
  no `data-update-error` block on either page — there is nothing to apologise for.

  SEARCH CONTROLS: POSITIVE 'Naprak' in hu = 1 · POSITIVE 'Up to date' in en = 1 ·
                   NEGATIVE 'ZZZ-not-present' = 0

== STOP CONDITIONS — none tripped ==
  updating stuck true : NO       journal surviving : NO      pin vs running image : AGREE
  retry loop          : NO       hold with no sentence : N/A (no hold)
  data : wishlist's seeded list item was NOT re-read by this session — see the gap note below.

== GAP, STATED ==
  The seeded-data read-back through wishlist's front door was not repeated after this scenario. The
  measuring agent seeded and read it back BEFORE the drill (02-seeding.txt) and the app is healthy
  and serving on v0.67.0 after it, but "the data is still there" is NOT asserted here for C. A and B
  both carry a real post-cut read-back; C does not.

== VERDICT ==
  A controller-only restart during an app update ends HONEST. The update resumes, completes, the
  other apps are untouched, and the household is shown a clean „Naprakesz" with no error. This is
  the behaviour v0.261.0's lock makes unnecessary rather than fixes — worth knowing, because it
  means the lock is defence in depth, not a repair of something broken.
