# 08 — THE v0.261.0 LOCK, PROVEN LIVE (R-608), with a positive AND a negative control
#
# Guest 9202, controller v0.261.0, 2026-09-21. The question is not "is the callback wired" but
# "does the swap actually refuse, and is it OUR refusal doing it".

== WHY A CONTROL WAS NEEDED, AND WHY THIS ONE IS THE RIGHT ONE ==
Guest 9202 has NO host agent wired, so `TriggerUpdate` refuses anyway — with the AGENT's sentence.
That makes it the perfect negative control: in `TriggerUpdate` the new app-update check sits BEFORE
the agent check, so if the lock fires we see OUR sentence and if it does not we see the AGENT's.
Two different sentences, one probe. It also makes the probe SAFE: no swap can reach a machine.

(Self-update is `enabled: false` on this guest by design. It was turned on for this probe and
turned back OFF immediately afterwards — the original file was copied to
/root/controller.yaml.pre-lock-probe first and restored from it. Verified reverted below.)

== CONTROL A — self-update status, no app update running ==
  GET /api/selfupdate/status  ->  {"ok":true,"data":{"running":false}}

== CONTROL B (NEGATIVE) — the MANUAL trigger with NO app update running ==
  12:39:55.917  POST /api/selfupdate/update
  -> {"ok":false,"error":"A frissites nem erheto el (nincs gazda-ugynok)"}
     i.e. the AGENT refusal. The lock is NOT firing, because nothing is in flight. Correct.

== THE PROBE (POSITIVE) — the MANUAL trigger WHILE an app update is in flight ==
  12:39:55.941  POST /api/stacks/uptime-kuma/update -> 202 "Frissites elindult"
  12:39:56.241  app update IN FLIGHT, phase = safety-dump
  12:39:56.244  POST /api/selfupdate/update
  -> {"ok":false,"error":"Egy alkalmazas frissitese eppen folyamatban van. A vezerlo frissitese utana inditható."}
     THE SENTENCE CHANGED. That is the v0.261.0 gate firing, and firing BEFORE the agent check.
  12:39:56.270  GET /api/selfupdate/status -> {"running":false}  — nothing was started.

  (accents transliterated here only to keep this file ASCII-searchable; intact on the wire)

== WHAT THE PHASE TELLS US, and it is worth stating ==
The probe landed in `safety-dump`. That phase is NOT covered by the pre-existing `backupRunning`
gate — only `backing-up` is, because that one takes the backup single-flight. So the probe hit
exactly the window the new lock exists for, rather than a window that was already protected.

== THE REVERSE DIRECTION — NOT staged live, and why ==
`UpdatePreflight` refusing an app update with reason `self_updating` while the controller swaps is
covered by `TestR608_PreflightRefusesWhileTheControllerSwaps` (a consequence test, red-proofed by
deleting the block). Staging it LIVE would need a real controller swap in flight, which needs a host
agent this guest does not have and a genuine image swap mid-measurement. **Not measured live. Stated
as a gap rather than implied.**

== CONFIG RESTORED ==
  self_update.enabled is back to `false` on guest 9202, verified by re-reading the file after the
  restore, and the controller was restarted on the restored config.
