← Commit history

background doctrine: the always-on focus sentinel (0.9.113-0.9.115)

John Lauer ·b7da1c5181 ·2mo ago ·parent 32e9e7d
1 file changed +26
skills/kicad-bridge-background/SKILL.md+26
@@ -212,3 +212,29 @@ minimized, it just has to be in the background.") The passive `fg_assert` (fail the verb if KiCad is z:0) still runs as the regression check; `_keep_background()` is the active remedy that keeps the user's screen clean during the run.++## The always-on focus sentinel (0.9.113-0.9.115)++The per-verb guardians PREVENT steals while a verb runs, but they expire, and KiCad+windows self-raise LATE (3D render / footprint-library load finishing minutes after+the verb). John caught those flashes live (2026-08-16: "i just saw some of your+windows auto-foreground"). The sentinel is a process-lifetime thread that watches+every foreground transition:++- KiCad window reaches the foreground with no user cause: minimize-bounce it back+  (never touching any user window) and log it.+- User cause means a mouse CLICK landing with the cursor ON that window, or an+  in-progress alt-tab. RECENT INPUT ALONE IS NOT USER INTENT: the user typing in+  another app is exactly when a self-raise is most disruptive (the 0.9.113 bless+  test got this wrong; 0.9.115 fixed it, verified adversarially).+- Sanctioned bridge foregrounds (bring_to_user / announce_foreground paths) call+  expect_foreground() so the sentinel never fights foreground:true or the+  announced allowForeground last resorts.+- Every transition lands in kicad_state.focusEvents and stderr (bridge-logs), so+  background compliance is PROVABLE from the log. Callers sampling z-order every+  few seconds CANNOT see sub-second flashes; the sentinel can.++Adversarial verification recipe: open a surface, wait out every guard tier, force+the window to the foreground via ab desktop_set_window_state {activate:true}, and+assert the sentinel logs self-raise (bounced) and the user's window is back in+front within ~300ms.