From bdc86d0f41142282d170074d02a919f891e5841d Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Thu, 16 Jul 2026 19:39:27 +0100 Subject: [PATCH] =?UTF-8?q?docs:=20RETRACT=20the=20#127=20cause=20?= =?UTF-8?q?=E2=80=94=20the=20diagnosis=20was=20a=20measurement=20artifact;?= =?UTF-8?q?=20fix=20stands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "blank + hypridle deadlock = the brick" cause published this morning is retracted everywhere it was asserted: ROADMAP, hyprland.nix's comment, #146, HARDWARE-QUEUE and two commit messages (a2151f4, 321ccf7). It rested on two pieces of evidence and both were artifacts of how I measured: - "19 disconnects in 11 days": 18 of 19 are hypridle's noisy goodbye while systemd DELIBERATELY stops it — each bracketed by `Stopped hypridle` / `Started hypridle`, healthy replacement seconds later. - "Jul 14: CRITICAL at 13:08:17, then eleven hours of silence": an artifact of grepping ONE PID. systemd logged `Stopped hypridle` 13:08:17 and `Started hypridle` 13:09:00; PID 1678 ran fine all evening, invisible to the filter. Decisive: at the incident (before the 12:51:34 mitigation commit) hypridle had NO disconnect at all and had been healthy since Jul 14 13:09. It should have fired on-resume normally. The deadlock theory never had a case. The FIX stands on its own evidence and is untouched: the options really were off, hypridle's on-resume really was the only caller of `dpms on`, and input now wakes the screen with hypridle deliberately stopped (4s docked / 6s clamshell). Removing a proven single point of failure never needed a story about why the daemon might fail — which is exactly why it survived the story being wrong. #127 is RESTORED to NOW (deleted this morning as "shipped"; only the fix shipped). Its cause is open and the lead is the dead Ctrl+Alt+F3 — neither DPMS-off nor a dead hypridle can break VT switching, so something was wedged below the compositor holding DRM master. Every theory so far has been about the wake path, i.e. a symptom of the screen already being black. #146 → [watch], near-dead: one genuine zombie ever (verified by ps/ss, not grep); upstream #171 is real but its impact path closed when #147's stray linger went. Re-measure properly before building anything; close unbuilt if zero. Also closes the #127 hardware-queue entry: Bernardo ran the real 10-min idle — 19:27:58 dpms off (genuine timeout, shipped config, and it BLANKED = the revert working) -> mouse -> 19:32:14 `done enabled=1 first=DP-2`, clamshell, the incident geometry, one healthy hypridle throughout. All 4 steps pass. Co-Authored-By: Claude Opus 4.8 --- agent/BACKLOG.md | 110 +++++++++++++++++++++++++------------- agent/HARDWARE-QUEUE.md | 33 +++++++----- agent/JOURNAL.md | 51 ++++++++++++++++++ docs/ROADMAP.md | 75 ++++++++++++++++++-------- modules/home/hyprland.nix | 28 ++++++---- 5 files changed, 214 insertions(+), 83 deletions(-) diff --git a/agent/BACKLOG.md b/agent/BACKLOG.md index 0a1bc3f..b4482c2 100644 --- a/agent/BACKLOG.md +++ b/agent/BACKLOG.md @@ -31,6 +31,44 @@ post-boot sessions). Preserve separation: the installer bake failure and the flake pin are different root causes even when they show up on the same machine. (Terminal / Ghostty-on-Acer → shipped as Kitty-only, #95.) +### 127. `[blocked:hw]` The idle brick's CAUSE — reopened; the fix shipped, the diagnosis did not + +**Restored 2026-07-16, same day it was deleted.** It was deleted as shipped on +the strength of a diagnosis that was retracted hours later. The **fix** is real, +proven and stays (`misc:{key_press,mouse_move}_enables_dpms`, a2151f4): the wake +path had a genuine single point of failure — hypridle's `on-resume` was the only +caller of `dpms on` in the session — and it is gone. Verified end-to-end on the +incident hardware, including a real 10-min idle blanking and waking clamshell +(`done enabled=1 first=DP-2`, 19:32). **None of that explains the brick.** + +**What the incident was:** docked clamshell, idle → black, external keyboard and +mouse woke nothing, unplugging the external did not light the panel, Caps Lock +LED still toggled (machine alive), and **Ctrl+Alt+F3 gave no usable TTY**. + +**What has been ruled OUT (all on hardware, 2026-07-16):** +- *DPMS-off is not the trigger.* With hypridle driving the blank, input wakes it + in every geometry — undocked lid-open, clamshell-docked on the sole live + output, docked with both outputs live. The clamshell skip was reverted on this. +- *The hypridle deadlock is not the trigger.* This was the published cause and it + was wrong: at the incident (before 12:51:34 on Jul 15) hypridle had **no + disconnect at all** and had been healthy since Jul 14 13:09. The "19 in 11 + days" rate that made the theory look strong was normal-teardown noise + (see #146). It should have fired `on-resume`. +- *The #100 undock latch* explains symptom 2 (unplugging not restoring the panel) + and is fixed (ff5017e) — but not the blackout itself. + +**The lead is Ctrl+Alt+F3, and it always was.** Neither DPMS-off nor a dead +hypridle can break VT switching; a wedged compositor still holding DRM master +can, and it would explain the black screen *and* the dead TTY in one stroke. +Every theory so far has been about the wake path — i.e. about a *symptom of the +screen being black* — and none of them touch the TTY. Start there instead. +Bernardo does not recall whether the machine had been relogged-in between Jul 14 +13:09 and the incident; that would say whether hypridle was ever a suspect. + +**Note the fix makes this much harder to reproduce** (input now wakes the screen +compositor-side), which is a good trade but means a repro may need the options +temporarily off. It has not recurred since. + ## NEXT ### 143. Rofi keeps the selected *row number* when the search changes, so the highlight lands on an unrelated entry @@ -165,48 +203,44 @@ settles. Note the ordering trap — the reload is also the undock rescue, so whatever re-asserts must not fight the rescue that deliberately re-enables eDP when the lid is open. -### 146. hypridle dies silently and systemd calls it healthy -Fell out of the #127 diagnosis (2026-07-16, ROADMAP § "#127 solved"), and it is -the bug that was actually behind the brick — the DPMS gate we just reverted was -only ever a symptom shield. hypridle hits an unfixed upstream deadlock -(hyprwm/hypridle#171, "Disconnected from pollfd id 1" — open since 2025-09, in -neither 0.1.7 nor main; upstream's own reporter saw 10-hour hangs) and then -**hangs without exiting**, so `Restart=always` never fires and `systemctl ---user is-active` still says `active (running)`. On the dev box: **19 times in -11 days**, including the incident day. Jul 14 is the clean example — locking -normally at 13:02, `CRITICAL` at 13:08:17, then eleven hours of silence, with -D-Bus receive queues visibly backing up (48 KB unread) because nothing was -reading them. The compositor is *fine* through all of it (no other session unit -dies in the same second), so this is not the compositor's death taking it down. +### 146. `[watch]` hypridle hangs instead of exiting when its compositor vanishes -Now that input wakes the screen compositor-side this is no longer brick-class, -but it is not cosmetic either: a silently dead hypridle means **no auto-lock** -(Bernardo sat unlocked for 25 min on 2026-07-16 without knowing), no blank, no -idle suspend. Cost: systemd can't see it — `WatchdogSec` needs `sd_notify` -hypridle doesn't implement, and the process never exits — so detection has to be -external: a guard watching for the `CRITICAL` line, or a liveness probe on its -Wayland fd, then `systemctl --user restart hypridle`. +**Rewritten 2026-07-16, hours after filing: the premise was disproven and almost +nothing survives.** As filed it said hypridle "dies silently ~2×/week — 19 times +in 11 days", with Jul 14 as the clean example ("`CRITICAL` at 13:08:17, then +eleven hours of silence"). Every number there was an artifact of the +measurement. **18 of the 19** `Disconnected from pollfd id 1` lines are +hypridle's noisy goodbye while systemd *deliberately stops it* — each bracketed +by `Stopped hypridle` / `Started hypridle` with a healthy replacement seconds +later. The "eleven hours of silence" came from grepping **one PID**: systemd +logged `Stopped hypridle` 13:08:17, `Started hypridle` 13:09:00, and PID 1678 +ran fine all evening where the filter could not see it. Do not re-derive the old +rate; it counts normal shutdown noise. -**Correction 2026-07-16:** this entry originally said "sequence it after #147, -because restarting hypridle into a dead compositor fail-loops into -`start-limit-hit` exactly as cliphist does". **That reasoning is void** — #147 -turned out not to be a distro bug at all (it needed stray pre-migration linger, -since removed), and cliphist's fail-loop was a symptom of that same stray state. -There is no ordering constraint: **#146 stands on its own and can be taken -first.** The underlying caution survives in weaker form and is still worth -designing for — a guard must not restart hypridle into a session whose -compositor is gone — but that is ordinary care in the guard, not a dependency -on another item. +**What is real, and it is small.** Exactly one genuine zombie has been observed +(2026-07-16 17:25:39), verified by process inspection rather than logs — `ps` +(alive 9h36m), `ss` (48 KB unread on its D-Bus socket, i.e. not reading), +`NRestarts=0` while `systemctl --user is-active` said `active (running)`. That +is upstream hyprwm/hypridle#171 ("Possible deadlock in pollThr when compositor +exits?", open since 2025-09, in neither 0.1.7 nor main) and it is a true bug: +hypridle hangs rather than exits, so `Restart=always` never fires. -The trigger is still unknown — *not* dock/undock (no display-watch activity in -the 3 min before any of them) and *not* a D-Bus quota disconnect (no broker -errors). **Note the 19-in-11-days figure was measured on a box with linger on**; -whether the rate holds now that the user manager is torn down at logout is -itself unknown, and re-measuring is the cheap first step before building a guard. -Worth a reproducer + an upstream comment on #171: it has sat ten months on -vaxerski's "that will cause a segfault no?", and we have better data than the -reporter did. +**But its impact path is closed, which is why this is `[watch]` and not work.** +The zombie only *persists* if systemd never stops the unit — which needed the +user manager to survive logout, i.e. the stray pre-migration linger removed in +#147. Without linger, logout tears the manager down and takes any zombie with +it; the Jul 14 trace above shows the healthy path (`Stopped` → `Started`) doing +exactly that. A Hyprland death ends the logind session anyway (2026-07-16: +`Session 2 logged out` → greeter), so there is no known route left to a +surviving zombie. + +**Do before building anything:** re-measure. Count only disconnects with **no** +`Stopped/Started hypridle` around them and **no** logind session event, and +confirm by `ps`/`ss` that the process is really wedged — not by grepping a PID. +If that count is zero over a few weeks post-linger, **close this unbuilt**. If it +is not, the upstream issue has sat ten months on vaxerski's "that will cause a +segfault no?" and a reproducer would be worth more than a local guard. ### 147. Nomarchy cannot see machine state it never set — and it bit the QA box for 6 months diff --git a/agent/HARDWARE-QUEUE.md b/agent/HARDWARE-QUEUE.md index 09b2ccd..557279b 100644 --- a/agent/HARDWARE-QUEUE.md +++ b/agent/HARDWARE-QUEUE.md @@ -16,7 +16,7 @@ Run these first when you have a laptop session (AMD dev box unless noted): | # | Item | Why first | |---|------|-----------| | 1 | **#115** suspend-then-hibernate | Just shipped; quick path is minutes (battery vs AC + `nomarchy-suspend`) | -| 2 | **#127** fix from config (not runtime keyword) | Cause found + fixed 2026-07-16; confirm our Nix emits it and it survives a relogin | +| 2 | **#127** the brick's *cause* (reopened) | Wake path fixed + fully verified 2026-07-16; cause unknown — start at the dead Ctrl+Alt+F3, not the wake path | | 3 | **Docking recovery round 8** + headphone jack-follow | Same dock session; relogin once, then undock ×5 + jack | | 4 | **#104** airplane mode | Fast radio smoke after `nomarchy-home` | | 5 | **#101** charge-limit USB-C burst | Dock plug/unplug storm; pairs with #3 | @@ -54,14 +54,17 @@ Everything else below stays open; order is convenience, not a gate. blank, input woke it in all three configurations including clamshell-docked on the sole live output. The mitigation was reverted and #135 retired unbuilt, which also voids TEST A (its premise — the skip — no longer - exists). Real cause: Hyprland's input→DPMS options ship off, so hypridle's - `on-resume` was the only caller of `dpms on`, and hypridle deadlocks - (hyprwm/hypridle#171) without exiting. Full record: ROADMAP § "#127 - solved"; the surviving bug is BACKLOG #146. **Method note worth keeping:** + exists). What that leaves: Hyprland's input→DPMS options ship off, so + hypridle's `on-resume` was the only caller of `dpms on` — a real single + point of failure, now removed. It is **not** known to be what bricked the + machine: the "hypridle deadlock" cause published on the day was retracted + the same day (hypridle had no disconnect at the incident). #127 is reopened + for its cause; ROADMAP § "#127's WAKE PATH fixed" is the full record. + **Method note worth keeping:** the *first* TEST B was invalid — it ran `dpms off` by hand, which no wake path watches, so its "input did not wake it" was guaranteed regardless of hardware. Drive the real code path or measure nothing. -- [ ] **#127 fix from config — steps 1+4 PASSED 2026-07-16, 2+3 open (AMD dev box)** +- [x] **#127 fix from config — ALL 4 STEPS PASSED 2026-07-16 (AMD dev box)** — a2151f4 applied via `nomarchy-pull` + `nomarchy-home` on the dev box. **Done, no need to redo:** 1. ✓ Both options read `int: 1` after `hyprctl reload` (which discards @@ -83,16 +86,20 @@ Everything else below stays open; order is convenience, not a gate. of anything: `/proc/acpi/button/lid/*/state` is, and it was never checked. Docked-lid-open remains **untested** — moot now that the gate is gone, but not tested. Do not read output lists as lid positions. - **Still open — needs real idle time, cannot be rushed:** - 2. Docked, **lid closed**, idle 10+ min → the external **blanks**. This is - the revert working; nothing to date has watched a genuine 10-min - hypridle timeout fire, only forced blanks. - 3. Then press a key → it returns, and `journalctl --user -t - nomarchy-display-wake --since '-15 min'` has a `done enabled=…` line - (i.e. hypridle's on-resume ran *as well as* the compositor waking it). + 2+3. ✓ **The real idle path, no scaffolding** (Bernardo, 19:22–19:32, + docked **lid closed**): `19:22:58 Executing pidof hyprlock || hyprlock` + (5 min) → `19:27:58 Executing hyprctl dispatch dpms off` — the genuine + 10-min timeout firing from the shipped config, and it **blanked**, which + is the revert working → mouse move → `19:32:13 Resumed: … Executing + …/nomarchy-display-wake` → `19:32:14 done enabled=1 first=DP-2`. + `enabled=1 first=DP-2` = clamshell, the incident's exact geometry. One + healthy hypridle (PID 683145) throughout. Note: step 1 used `hyprctl reload` rather than a full relogin — a reload re-reads the same on-disk config a new session would, so the gap is cosmetic, but a relogin has not literally been done. + **Entry closed. #127 itself is reopened for its CAUSE (see BACKLOG) — + the wake path is fixed and verified; what bricked the machine is not + known, and the dead Ctrl+Alt+F3 is the lead.** - [ ] **#96 battery-limit row on threshold-less firmware (Acer M5-481T)** — on the Acer (live or installed session with the commit carrying this entry): open System (`SUPER+CTRL+I`). **Pass:** a "Battery limit" row diff --git a/agent/JOURNAL.md b/agent/JOURNAL.md index 129d9c6..9900eb5 100644 --- a/agent/JOURNAL.md +++ b/agent/JOURNAL.md @@ -19,7 +19,58 @@ Template: --- +## 2026-07-16 — RETRACTION: the #127 diagnosis was as wrong as the test it replaced +- **Task:** Bernardo said "do #147 first, then #146". Both dissolved under + investigation, and taking them apart disproved the #127 *cause* I had shipped + hours earlier. The fix stands; the story around it did not. Sweep = retract. +- **The retraction, and it is the whole entry.** I published "blank + hypridle + deadlock = the brick" into ROADMAP, hyprland.nix's comment, #146 and two commit + messages, on two pieces of evidence: "19 disconnects in 11 days" and Jul 14's + "eleven hours of silence". **Both were artifacts of my own measurement.** 18 of + 19 disconnects are hypridle's goodbye while systemd *deliberately stops it* + (each bracketed by `Stopped`/`Started`, healthy replacement seconds later). The + "silence" was a grep for **one PID** — systemd logged `Started hypridle` 43s + later and PID 1678 ran fine all evening, invisible to my filter. **Decisive:** + at the incident hypridle had **no disconnect at all**, healthy since Jul 14 + 13:09. The theory never had a case. +- **The irony is the lesson.** I spent the morning writing up "the invalid TEST B + measured its own scaffolding — put the mechanism inside the circuit", then did + exactly that twice more: inferred a lid position from an output list, inferred + "the session wiring is missing" from a grep that never covered HM's generated + conf. Three wrong claims, all from inferring where checking was cheap; Bernardo + caught all three. **Verify the instrument before believing the reading — + hardest when the reading agrees with you.** +- **#147 → not a distro bug.** The wiring exists (HM's `systemd.enable`, line 1 of + the generated conf) and fired; systemd *refused* the `graphical-session.target` + stop because portal-gtk had a start job queued ("destructive transaction"). It + needs `Linger=yes` to matter — an empty marker dated **2026-01-24, pre-dating + Bernardo's migration to Nomarchy**. Never shipped by us, ever. Removed. The item + is rewritten to the class that IS real: `/var/lib` survives a migration, so the + primary V3 box ran 6 months on a setting the distro never chose and cannot see. +- **#146 → `[watch]`, near-dead.** One genuine zombie ever (verified by `ps`/`ss`, + not grep); upstream #171 is real but its impact path closed with linger. +- **Did:** retracted the cause in place everywhere it was asserted; **restored + #127** (deleted this morning as "shipped" — only the fix shipped); rewrote #146 + and #147; filed #148 (a reload re-lights the panel in a shut lid — found because + my own `hyprctl reload` did it to Bernardo, twice). +- **Verified: V3, the fix survives all of it.** The real 10-min idle, no + scaffolding: `19:27:58 Executing hyprctl dispatch dpms off` → blanked (the + revert working) → mouse → `19:32:14 done enabled=1 first=DP-2` — clamshell, the + incident geometry, from shipped config. Plus the brick condition on purpose + (hypridle stopped): keypress woke it 4s docked / 6s clamshell. **The fix needed + no story about why the daemon fails — which is why it survived the story being + wrong.** V1: flake check + option-docs/clamshell-logind/docking-ux green. +- **Next suggestion:** #127's **cause**, and start at the **dead Ctrl+Alt+F3** — + the one symptom no theory yet has touched. DPMS-off cannot break VT switching + and neither can a dead hypridle; a compositor wedged holding DRM master + explains the black screen and the dead TTY together. Every theory so far has + been about the wake path, i.e. a symptom of the screen already being black. + ## 2026-07-16 — #127 SOLVED: the brick was never DPMS, and TEST B's first run lied +- **⚠ SUPERSEDED — read the entry above first.** Its headline ("SOLVED") and its + cause ("hypridle deadlock", "19× in 11 days") were **retracted the same day**: + the evidence was a measurement artifact and hypridle had no disconnect at the + incident. What survives: the invalid-TEST-B analysis, and the fix itself. - **Task:** #127 TEST B (Bernardo ran it), then the whole sweep his result forced. - **Read this if nothing else — the test measured nothing, and said so.** His run reported "input never wakes it" **docked *and* undocked alike**, and that tie is diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index b061d8a..ce3ea89 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -622,10 +622,12 @@ Design/decision records and a running log of shipped work (items marked that could not run — two mundane bugs, not one exotic DRM state. The shipped DPMS mitigation may therefore be treating the wrong cause; #127's TEST B is what decides, and it is far safer now that an undock works again. - **It did decide, the same day: the hunch was right on both counts** — the - mitigation *was* treating the wrong cause and is now reverted, and the brick - *was* two mundane bugs (an ordinary blank + a wake path that had died). See - "#127 solved" below. + **It did decide, the same day — half of the hunch was right.** The mitigation + *was* treating the wrong cause and is now reverted (blanking while clamshell + is provably innocent). But "an ordinary blank + a recovery that could not run" + is **not** established: the wake path had a real single point of failure, which + is fixed, yet nothing shows it had failed *during the incident*. The brick's + cause is still open. See "#127's WAKE PATH fixed" below. - ✓ **The #107 compat shim is pinned (#133, 2026-07-16):** `mkFlake` prefers `state.json` and still accepts the legacy `theme-state.json`, because a machine only migrates on its next menu write — but nothing evaluated a @@ -727,19 +729,19 @@ Design/decision records and a running log of shipped work (items marked `nomarchy-suspend` (hypridle + Power menu). LUKS: unlock session before encrypted hibernate phase so resume is LUKS-only (not hyprlock twice). Menu row self-gates on CanHibernate. V3: HARDWARE-QUEUE bag-carry test. -- ✓ **#127 solved — the idle brick was never DPMS (2026-07-16, AMD dev box):** - the clamshell DPMS-off skip (060bf52, and its #136 wording fix) is - **reverted**; idle blanks again in every dock/lid state. Hyprland ships - `misc:key_press_enables_dpms` and `misc:mouse_move_enables_dpms` **off**, and - nothing else in the session calls `dpms on` — so hypridle's `on-resume` was - the *only* thing that could un-blank a screen: an idle daemon with the - display held hostage behind it. hypridle then deadlocks on an unfixed - upstream bug (hyprwm/hypridle#171, "Disconnected from pollfd id 1", open - since 2025-09, absent from 0.1.7 *and* main) and hangs **without exiting**, - so `Restart=always` never fires and systemd still reports it - `active (running)`. Blank + deadlock = a black seat no keypress can escape. - That is the brick: two mundane bugs, not one exotic DRM state — the same - shape as the #100 latch above. +- ✓ **#127's WAKE PATH fixed — but the brick's cause is still open (2026-07-16, + AMD dev box).** Read the title carefully: the fix below is proven and shipped; + the *diagnosis* published alongside it on the day was **wrong and is retracted + in place** (see "What was claimed, and why it was wrong" below). The clamshell + DPMS-off skip (060bf52, and its #136 wording fix) is **reverted**; idle blanks + again in every dock/lid state. + **What is true and load-bearing.** Hyprland ships `misc:key_press_enables_dpms` + and `misc:mouse_move_enables_dpms` **off**, and nothing else in the session + calls `dpms on` — so hypridle's `on-resume` was the *only* thing that could + un-blank a screen: an idle daemon with the display held hostage behind it. + That is a real single point of failure, and it is now gone. It needed no story + about *why* the daemon might fail to justify removing it — which is exactly + why the fix survived its own diagnosis being wrong. **Evidence, on the incident hardware.** With hypridle driving the blank, input woke it in all three configurations — including clamshell-docked on the sole live output (`nomarchy-display-wake done enabled=1 first=DP-2`), which @@ -760,10 +762,41 @@ Design/decision records and a running log of shipped work (items marked (an option to opt back into DPMS-when-clamshell) is therefore **retired unbuilt** — there is nothing left to opt into, and the 27" panels stop being held lit all night under a static lock screen. - **Still open:** hypridle's silent death is its own bug (~2×/week on this box; - no auto-lock is a security cost even when the screen is recoverable), and the - incident's dead Ctrl+Alt+F3 remains unexplained — a known co-symptom upstream - (hyprwm/hypridle#145) but not accounted for here. Tooling kept: + **What was claimed, and why it was wrong (retracted the same day).** This + entry, `hyprland.nix`'s comment and two commit messages all said the brick was + "blank + hypridle deadlock": hypridle hitting hyprwm/hypridle#171, hanging + without exiting so `Restart=always` never fires, leaving a blanked seat with + nothing left to call `dpms on`. Cited evidence: "19 disconnects in 11 days", + and Jul 14 as the clean example — "locking normally at 13:02, `CRITICAL` at + 13:08:17, then eleven hours of silence". **All of it was an artifact of how it + was measured.** 18 of the 19 `Disconnected from pollfd id 1` lines are + hypridle's noisy goodbye while systemd *deliberately stops it* — each one + bracketed by `Stopped hypridle` / `Started hypridle`, with a healthy + replacement seconds later. The "eleven hours of silence" came from grepping a + single PID (`hypridle[1679]`): systemd logged `Stopped hypridle` at 13:08:17 + and `Started hypridle` at 13:09:00, and PID 1678 ran fine all evening where + the filter could not see it. Only **one** genuine zombie has ever existed here + (2026-07-16 17:25:39, verified by process inspection — `ps`, `ss` showing + unread D-Bus queues, `NRestarts=0` — not by grep), and it needed stray + pre-migration linger to persist (BACKLOG #147). **The decisive fact:** at the + time of the brick (before the 12:51:34 mitigation commit) hypridle had **no + disconnect at all** — it had been running healthily since Jul 14 13:09. It + should have fired `on-resume` normally. The deadlock theory never had a case. + **The lesson is the same one this entry teaches about TEST B**, which is what + makes it worth the space: the invalid test measured its own scaffolding, and + so did the diagnosis that replaced it — a grep that could only see one PID, + and a rate that counted normal shutdown noise. Both times the error was + inferring where checking was available and cheap. Verify the instrument before + believing the reading, *especially* when the reading confirms the theory. + **Still open — #127's cause, now the lead question (BACKLOG #127).** We know + the wake path had a single point of failure and we removed it; we do **not** + know what actually bricked the machine. The **dead Ctrl+Alt+F3 is the strongest + clue and points away from everything examined so far**: neither DPMS-off nor a + dead hypridle can break VT switching, so something was wedged below the + compositor — DRM master, most likely. Also unknown: whether the machine had + been relogged-in between Jul 14 13:09 and the incident (Bernardo does not + recall), which is the one fact that would say whether hypridle was ever a + suspect worth having. Tooling kept: `nomarchy-display-wake` / `nomarchy-display-dump`. - ✓ **Generation prune (#128):** weekly `nomarchy-gen-prune` (system + HM): delete only gens older than **14 days** that are also beyond the **3 most diff --git a/modules/home/hyprland.nix b/modules/home/hyprland.nix index c0902da..fb4000e 100644 --- a/modules/home/hyprland.nix +++ b/modules/home/hyprland.nix @@ -1098,17 +1098,23 @@ in # reads as a glitch (item 28c, seen in the capture harness). background_color = rgb c.base; - # #127, and the whole answer to it. Hyprland ships both of these - # OFF, so nothing in the compositor turned a blanked screen back - # on: hypridle's on-resume was the ONLY caller of `dpms on` in the - # session — an idle daemon as a single point of failure with the - # display behind it. hypridle then deadlocks on an unfixed upstream - # bug (hyprwm/hypridle#171: "Disconnected from pollfd id 1", open, - # not in 0.1.7 or main) and hangs WITHOUT exiting, so Restart=always - # never fires and systemd still reports it active. Blank + deadlock - # = a black seat no keypress could escape. That was the brick. - # These put the wake in the compositor, where no daemon can lose it; - # a dead hypridle now costs auto-lock, not the machine. + # #127. Hyprland ships both of these OFF, so nothing in the + # compositor turned a blanked screen back on: hypridle's on-resume + # was the ONLY caller of `dpms on` anywhere in the session — an idle + # daemon as a single point of failure with the display behind it. + # Both facts verified on hardware 2026-07-16, as is the cure: with + # hypridle deliberately stopped and the screen blanked (i.e. the + # brick condition manufactured on purpose), a keypress woke it in 4s + # docked / 6s clamshell. The wake now lives in the compositor where + # no daemon can lose it; a dead hypridle costs auto-lock, not the seat. + # + # Deliberately NOT claimed: that this is what caused the original + # brick. An earlier version of this comment said so, citing a hypridle + # deadlock (hyprwm/hypridle#171) — that was wrong and is retracted: + # hypridle had no disconnect at all around the incident. Removing a + # proven single point of failure needs no such story. The cause is + # still open (BACKLOG #127); the dead Ctrl+Alt+F3 is its lead symptom, + # and neither DPMS-off nor a dead hypridle can produce that. key_press_enables_dpms = lib.mkDefault true; mouse_move_enables_dpms = lib.mkDefault true; };