fix(display): #148 — enforce dock intent without the auto-profiles toggle
All checks were successful
Check / eval (push) Successful in 3m37s

Every nomarchy-home re-lit eDP-1 inside the shut lid: HM activation fires
hyprctl reload, the catch-all monitor rule re-enables the panel, and the
tick invariant built for exactly this (#142, enforce_dock_intent) never
fired — it was gated on settings.displayProfileAuto, the *named-profile*
auto-switch toggle, which is unrelated to dock intent and off unless
opted into. Confirmed live on the incident machine: dockMode=true,
displayProfileAuto unset, watcher journal empty — every tick returned at
that guard.

Fix: drop the auto_on gate. settings.display.dockMode alone is the
user's recorded intent (written by the menu and the dock transition);
the profile-active check remains as the real guard (a named profile
outranks the dock heuristic). No fight with the #142 undock hammer:
undock clears the intent before its rescue reload, so the enforcer
stands down by ordering.

tools/monitor-fallback.nix grows the reload-mid-dock scenario: fake lid
closed, hyprctl reload, panel re-disabled within ~4s, action=re-dock
logged — under displayProfileAuto's default false, i.e. the exact
condition the bug shipped under.

Verification: V2 — nix flake check --no-build green, checks.docking-ux
green, monitor-fallback harness green three times (implementer twice,
reviewer once, ~71s VM). V3 pending: relogin + nomarchy-home while
docked-clamshell (HARDWARE-QUEUE; the watcher is exec-once, the fix
arms at next login). Implementation by a Sonnet subagent; diagnosis,
design and review on Fable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 07:48:05 +01:00
parent e324876eb7
commit 5479ade702
6 changed files with 78 additions and 34 deletions

View File

@@ -600,12 +600,21 @@ ${lib.concatStringsSep "\n" (lib.mapAttrsToList
#
# `auto_dock` is already idempotent it returns early unless the internal
# is enabled so this costs one `hyprctl monitors` per tick and does
# nothing in the overwhelmingly common case. Gated on the same display
# profile check as the event path: a profile is a deliberate, more specific
# layout and outranks the dock heuristic.
# nothing in the overwhelmingly common case. `settings.display.dockMode`
# is the user's recorded intent written by the menu and by the dock
# transition itself (`set_dock_intent`, which `auto_dock` above reaches
# via `$TRANSITION dock`) and that alone is what this is gated on. It
# used to also require `auto_on` (`settings.displayProfileAuto`), but that
# toggle only controls *named-profile* auto-switching; gating the dock
# invariant on it meant enforcement never fired for anyone who had not
# separately opted into auto profiles, which is most users. A
# `home-manager` activation's `hyprctl reload` re-applies the catch-all
# monitor rule regardless, so the panel inside a shut lid came back on
# every rebuild while docked (#148). The profile-active check right below
# is the real, narrower guard this needs: a profile is a deliberate, more
# specific layout and outranks the dock heuristic.
enforce_dock_intent() {
[ "$(${sync} get settings.display.dockMode 2>/dev/null)" = true ] || return
auto_on || return
[ "$(nomarchy-display-profile active 2>/dev/null || echo none)" = "none" ] || return
internal=$(internal_output)
[ -n "$internal" ] || return