From e4404e016292c29d1c6cdd07d793495f3fc6591c Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Tue, 14 Jul 2026 12:35:23 +0100 Subject: [PATCH] test(docking): prove the keyboard-restore path; the reload clobber is real MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last unproven half of bec826b, and it needed a keyboard that stays behind when the dock leaves. The dev box's by-tech hangs off the BenQ's own hub, so it always walked away with the cable — a Logitech K400 with its receiver in the laptop is the real case, and Bernardo happened to plug one in. Both halves measured on hardware (dev box, 2026-07-14): - PRIMARY: across a real unplug + the fixed transition, the K400 kept its remembered `us`. Panel back, layout intact. - CONTROL: a bare `hyprctl reload` with no restore took the same board from `us` to the session's `gb`. The control matters more than the pass. That reload clobbers runtime `device[]:kb_layout` was an inference from the mechanism when restore_keyboards was written — reasonable, but unmeasured, and this session has already shown what unmeasured reasoning about this watcher is worth. It holds: without that call the undock's escalation would have silently broken the layout of every keyboard that outlives the cable, trading a black panel for a keyboard that types the wrong characters at the password prompt. Verified V2 again after the comment/doc edits: nix flake check --no-build, checks.docking-ux, shellcheck clean. Round 8 now asks only for what is still open: relogin (the watcher calls the transition by baked store path, so exec-once means it runs the old build until then) and repetition — 5+ unplugs, one with the lid shut. Co-Authored-By: Claude Opus 4.8 --- agent/HARDWARE-QUEUE.md | 20 +++++++++++--------- agent/JOURNAL.md | 17 ++++++++++------- modules/home/hyprland.nix | 11 +++++++---- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/agent/HARDWARE-QUEUE.md b/agent/HARDWARE-QUEUE.md index 4db869a..ee9d21c 100644 --- a/agent/HARDWARE-QUEUE.md +++ b/agent/HARDWARE-QUEUE.md @@ -49,19 +49,21 @@ the **T14s** (webcam case). The transition now escalates to `reload` when the keyword proves inert, re-asserts the rule, and restores per-device keyboard layouts (a reload drops runtime `device[…]:kb_layout` keywords). - **Already proven on hardware:** one real unplug, invoking the fixed - `nomarchy-display-transition undock eDP-1` directly with the watcher - paused — panel on and workspaces 1–3 home in 1.8s, journal - `keyword=inert escalate=reload` → `enable=via-reload` → `result=ok`. + **Already proven on hardware** (2026-07-14, two real unplugs invoking + the fixed `nomarchy-display-transition undock eDP-1` directly with the + watcher paused): panel on and workspaces 1–3 home in 1.8s, journal + `keyword=inert escalate=reload` → `enable=via-reload` → `result=ok`; and + the keyboard-restore path with a Logitech K400 whose receiver is in the + laptop — it kept its remembered `us` across the undock, while a control + `hyprctl reload` with no restore dropped it to the session's `gb` + (so `restore_keyboards` is load-bearing, not insurance). **What is NOT proven, and is this round:** the *watcher-driven* path (the running watcher calls the transition by baked store path, so it only picks the fix up at relogin — the round-5 trap), and repetition. **Re-check:** relogin (mandatory — see below), dock, then unplug the - cable **at least five times**, plus once with the lid shut and once - while an external keyboard is plugged into the *laptop* (not the dock's - hub) — that last one is the only check on the keyboard-restore path. - **Pass:** the panel comes back every time with workspaces intact, the - external keyboard keeps its remembered layout, and `journalctl --user + cable **at least five times**, plus once with the lid shut. + **Pass:** the panel comes back every time with workspaces intact, and + `journalctl --user -t nomarchy-display-transition -t nomarchy-display-watch --since '-10 min'` shows `result=ok` for each undock with **no** `result=enable-failed`. `keyword=inert escalate=reload` on every undock diff --git a/agent/JOURNAL.md b/agent/JOURNAL.md index 0572946..70a605f 100644 --- a/agent/JOURNAL.md +++ b/agent/JOURNAL.md @@ -41,16 +41,19 @@ Template: 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 + eDP-1` driven through two real unplugs on the dev box: panel on, workspaces 1–3 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 + `result=ok`. Keyboard-restore proven too, on a K400 whose receiver is in the + laptop (so it survives the cable): kept its remembered `us`, while a control + bare `hyprctl reload` dropped it to the session `gb` — the clobber was an + inference until measured, and it makes `restore_keyboards` load-bearing. + Plus V2: `nix flake check --no-build` all pass; `checks.docking-ux` + + `checks.dock-audio` + `monitor-fallback.nix` 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. + relogin (exec-once, baked store path) and repetition (5+ unplugs, one with + the lid shut). 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). diff --git a/modules/home/hyprland.nix b/modules/home/hyprland.nix index d844c75..0f6b7c0 100644 --- a/modules/home/hyprland.nix +++ b/modules/home/hyprland.nix @@ -170,10 +170,13 @@ let } # `hyprctl reload` re-reads the config, and every runtime keyword dies # with it — including the `device[]:kb_layout` the keyboard watcher - # applies to an external board. The remembered choices live in the - # in-flake state, so `restore` is authoritative and cheap; without this a - # reload would silently drop an external keyboard back to the session - # layout mid-undock. + # applies to an external board. Measured on hardware 2026-07-14: a bare + # reload took a keyboard remembered as `us` straight back to the session's + # `gb`, so without this the undock's escalation would silently break the + # layout of any keyboard that stays behind when the dock leaves (a + # wireless receiver in the laptop; only boards on the dock's own hub are + # unaffected, and only because they leave with it). The remembered + # choices live in the in-flake state, so `restore` is authoritative. restore_keyboards() { hyprctl devices -j 2>/dev/null | jq -r '.keyboards[].name' 2>/dev/null \ | while IFS= read -r kb; do