fix(docking): make workspace and lid transitions atomic
All checks were successful
Check / eval (push) Successful in 3m19s

Restore the old flake's external-only workspace handoff, protect closed-lid cable removal with a verified low-level logind inhibitor, and use monitor-added as the settled audio reprobe boundary.

Verified: V2 — nix flake check --no-build; docking-ux, option-docs, and template-sot checks; KVM tools/monitor-fallback.nix lifecycle test.

V3 pending: closed-lid BenQ round 4 in agent/HARDWARE-QUEUE.md.
This commit is contained in:
2026-07-13 13:56:54 +01:00
parent cffe432912
commit 2a34c7398b
12 changed files with 654 additions and 292 deletions

View File

@@ -11,27 +11,32 @@ the **T14s** (webcam case).
## Any machine (dev box is fine)
- [ ] **Docking correction round 3 (2026-07-13 hardware-QA follow-up)**
prior result: lid-close stayed awake, but SUPER+M rendered on the
hidden laptop workspace; the keyboard never prompted; audio stayed
on speakers; the old laptop-off action lost some applications and
abrupt undock left eDP black. After updating to the commit carrying
this entry and rebuilding Home Manager, start with the laptop open
and play audio. Close the lid and plug the monitor/keyboard: SUPER+M
must render on the external focused workspace; the new keyboard must
ask for a searchable XKB layout (or set/change it at System
Keyboard); audio must toast and move to the monitor. System Audio
Output device remains the manual fallback; the current wallpaper must
cover the external output/workspaces within ~2s. Open Display and choose
**Dock mode**: all eDP workspaces/windows move intact to the external
and focus follows; no terminal/application exits. Yank the cable: the
internal panel is already the logical fallback, lights immediately,
and the moved workspaces/windows survive. Replug then manually choose
speakers: volume/mute changes must not force the monitor again; only a
fresh replug should. If audio still misses, capture `pactl subscribe`
during plug plus `pactl --format=json list sinks` while docked. If the
menu is still misplaced, capture `hyprctl monitors -j` immediately
before SUPER+M (the `.focused` field is the discriminator).
- [ ] **Docking recovery round 4 (#100, closed-lid BenQ sequence)** after
updating to the commit carrying this entry, run `nomarchy-home` and
relogin. With the lid open, put visible windows on at least workspaces
1, 2, and 3 and start audio; plug the BenQ GW2790QT. **Pass on plug:**
wallpaper appears, audio toasts and moves to a BenQ/GPU HDMI sink, and
`journalctl --user -t nomarchy-dock-audio -n 10` shows
`trigger=monitoradded` plus `selected=…` (not only a card event). Open
System Display **Dock mode**. **Pass on dock:** every workspace and
window is on the BenQ, focus follows, eDP is disabled in `hyprctl
monitors all -j`, and `systemd-inhibit --list` shows Nomarchy holding
`handle-lid-switch` for “Safe dock/undock display transition”; there is
no dangling laptop workspace and no application exits. Close the lid;
audio/session must stay awake. While still docked, manually choose the
speakers and change volume/mute: it must stay on speakers. Unplug the
BenQ **with the lid still closed**, wait 10 seconds, then open it.
**Pass on undock:** no suspend/crash/hard reset, eDP is already active,
workspaces 1/2/3 and their windows are intact on it, and the inhibitor
disappears only after lid-open. `journalctl -b --since '-5 min'` must
contain no cable-removal `systemd-logind: Suspending...`. Finally close
the undocked lid normally: the laptop must suspend, proving policy was
restored. Wake it, replug the BenQ: that fresh plug must move audio back
to HDMI (manual speakers do not survive a physical replug). If any step
fails, capture `journalctl --user -t nomarchy-display-watch -t
nomarchy-display-transition -t nomarchy-dock-audio --since '-10 min'`,
`systemd-inhibit --list`, `hyprctl monitors all -j`, `hyprctl workspaces
-j`, and `pactl --format=json list sinks` before changing state again.
- [ ] **Parallel fingerprint-or-password on the real reader** (AMD dev
box, 2026-07-12) — with a finger enrolled and
`fingerprint.pam = true` (parallel is the default): `sudo -k true`

View File

@@ -19,6 +19,28 @@ Template:
---
## 2026-07-13 — #100 dock transition recovery (this commit)
- **Task:** Fix the real T14s dangling workspace, closed-lid cable-removal
suspend/apparent crash, and BenQ audio miss after 7ebfab4.
- **Did:** Added one atomic dock/internal-first undock helper; the IPC watcher
owns a verified low-level lid inhibitor through physical lid-open, with
validated stale-process cleanup. `monitoradded` now debounces a settled
PipeWire/WirePlumber reprobe then picks an available dock sink; generic
audio events never override a manual choice. Named clamshell profiles share
the safe transition. Replaced the fallback VM tool with a bounded hotplug
workspace/output/inhibitor test and strengthened generated-artifact guards.
- **Verified:** V2 — `nix flake check --no-build`; targeted docking-UX,
option-docs, and template-SoT builds; and
`nix build --impure -f tools/monitor-fallback.nix --no-link -L` all green.
The KVM guest proved atomic workspace handoff, internal-first restoration,
inhibitor retention with the simulated lid closed, and release on lid-open.
The harness restores the DRM output before deleting QEMU's last synthetic
headless output because that backend-specific zero-output state aborts
Hyprland before a usable removal lifecycle is delivered.
- **Pending:** V3 exact closed-lid BenQ round 4 in HARDWARE-QUEUE. No
real-hardware or real monitor-audio claim.
- **Next suggestion:** run V3 round 4, then take installer #91.
## 2026-07-13 — Live ISO hardware findings queued (#91#99)
- **Task:** Transcribe Bernardo's Acer Aspire M5-481T install/session notes
and installer failure photo into the executable queue.

View File

@@ -110,6 +110,15 @@ iteration would otherwise rediscover.
check onto the whole return set or lazy attr access skips it.
## Design invariants
- **Dock transitions are ordered safety operations (#100):** dock in one
`hyprctl --batch` (external on → every internal workspace moved → focus
external → internal off); undock enables internal *before* moving anything.
The Hyprland watcher, not a shell-pipeline subshell, owns a low-level
`handle-lid-switch` inhibitor until the lid is physically open; startup
cleans a validated stale process group. HDMI availability may appear only
as `change:sink`, so fresh `monitoradded` is the intent boundary for the
settled PipeWire/WirePlumber reprobe + sink pick; generic audio changes must
not override a manual in-dock speaker choice.
- **Waybar status is never color-only** (item 28 sweep, iteration #69):
every status module must distinguish its states by SHAPE (glyph) or
presence (self-hide), never color alone — good/warn/bad collapse under