fix(docking): the undock keyword is inert, not raced; escalate to reload
All checks were successful
Check / eval (push) Successful in 3m10s

Round 7 FAILED: 5-6 consecutive unplugs on the dev box, the panel never came
back. Round 6's retry could not have worked, because round 6's diagnosis was
wrong — and so was the "phantom re-add" theory I brought to this session.

With ZERO enabled outputs — panel disabled by the dock, external gone —
Hyprland 0.55.4 accepts `hyprctl keyword monitor eDP-1,preferred,auto,1`,
prints `ok`, exits 0, and never flushes it: the rule waits for a DRM event
that is not coming. It is inert, not raced. Retrying it 25x/poll for 6 polls
bought 30s of black screen and nothing else.

What made this survive two rounds is worth more than the fix: every
`transition=undock result=ok` in the round-6 journal was Bernardo plugging
the cable back in because the screen was black. His hotplug flushed the
queued rule and the next poll took the credit. Success was indistinguishable
from the user working around the failure, so the logs confirmed whichever
story we brought to them. Ten minutes of probing on hardware killed both.

Probed live 2026-07-14 (dev box, cable out): keyword inert across 4s and 5s
in two runs; `dispatch forcerendererreload` inert too; only `hyprctl reload`
escapes — 99ms and 289ms. So: issue the keyword (enough whenever another
output is still enabled, e.g. the menu's Dock mode), and escalate to reload
only when `monitors` proves it inert. After a reload, re-assert the rule so a
config that parks the panel off cannot undo the undock, and restore per-device
keyboard layouts — a reload re-reads the config and drops the runtime
`device[<name>]:kb_layout` an external board depends on. The menu's `enable`
now proves itself against `monitors` too, instead of cheering for an exit code.

Verified V3 (partial): the fixed transition driven through a real unplug —
panel on, workspaces 1-3 home, 1.8s, `keyword=inert escalate=reload` ->
`enable=via-reload` -> `result=ok`. V2: nix flake check --no-build; docking-ux
+ dock-audio; monitor-fallback.nix; shellcheck clean on display-transition.

NOT proven, and queued as HARDWARE-QUEUE round 8: the watcher-driven path
(exec-once + baked store path = relogin required, the round-5 trap) and
repetition. The keyboard-restore path is untested — the dev box's keyboard
hangs off the dock's hub, so it leaves with the cable; round 8 adds a check
with a keyboard in the laptop. The VM harness still cannot reach any of this:
QEMU aborts Hyprland if the last active output is deleted while the DRM output
is disabled, which is the same zero-output degeneracy from the other side.

New BACKLOG #114 (PROPOSED): tuigreet ignores per-device keyboard layouts —
a VT has one keymap, so the greeter cannot honour `device[]:kb_layout`.
Bernardo hit it logging out while docked; not a regression, a design gap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 12:28:06 +01:00
parent 28be6779a7
commit bec826baf0
5 changed files with 172 additions and 56 deletions

View File

@@ -19,7 +19,42 @@ Template:
---
## 2026-07-14 — docking round 6: undock left the laptop panel black (this commit)
## 2026-07-14 — docking round 7 FAILED; the keyword was inert, not raced (this commit)
- **Task:** HARDWARE-QUEUE docking round 7 result (Bernardo, AMD dev box) —
56 consecutive unplugs, the panel never came back. Round 6's fix did
nothing because round 6's diagnosis was wrong.
- **Did:** Probed it live instead of re-reading the journal. With ZERO enabled
outputs, `hyprctl keyword monitor eDP-1,preferred,auto,1` is *inert*
accepted, `ok`, exit 0, and never flushed until a DRM event arrives. Not a
race: inert across 4s and 5s in two runs. `dispatch forcerendererreload` is
inert too; only `hyprctl reload` escapes (99ms / 289ms, cable out). So the
transition issues the keyword, and escalates to `reload` only when
`monitors` proves it inert — then re-asserts the rule and calls
`nomarchy-keyboard-layout restore` per device, since a reload drops runtime
`device[…]:kb_layout` keywords. Also made the menu's `enable` prove itself.
- **The lesson, worth more than the fix:** round 6 built its race theory on
`enable-timeout` ×2 + `result=ok` ×1, and I extended it with a "phantom
re-add" theory from the `added= DP-2` lines. Both were the same artifact:
**every `result=ok` was Bernardo replugging the cable** because the screen
was black — his hotplug flushed the queued rule and the next poll claimed
the win. A watcher whose success is indistinguishable from the user working
around it will keep confirming whatever story you bring. The probe cost 10
minutes and killed two rounds of theory.
- **Verified:** V3 (partial) — the fixed `nomarchy-display-transition undock
eDP-1` driven through a real unplug on the dev box: panel on, workspaces
13 home, 1.8s, `keyword=inert escalate=reload` → `enable=via-reload` →
`result=ok`. Plus V2: `nix flake check --no-build` all pass;
`checks.docking-ux` + `checks.dock-audio` pass; shellcheck clean on
display-transition (profile-watch's lone SC2125 is pre-existing).
- **Pending:** HARDWARE-QUEUE round 8 — the *watcher-driven* path needs a
relogin (exec-once, baked store path) and repetition; the keyboard-restore
path is entirely untested (dev box's keyboard hangs off the dock hub, so it
leaves with the cable). New BACKLOG #114 (PROPOSED): tuigreet ignores
per-device layouts.
- **Next suggestion:** #114, or the dock direction's unverified panel-off
(it disables the panel trusting hyprctl's exit code — same class, latent).
## 2026-07-14 — docking round 6: undock left the laptop panel black (superseded by round 7 above)
- **Task:** HARDWARE-QUEUE docking round 6 result (Bernardo, AMD dev box).
Plug side all passed (auto-dock, audio to the monitor, wallpaper on the
external); unplugging left the panel black until re-docked.