--- name: niri-monitor-flap-refresh-triage description: "Diagnose niri/amdgpu monitor failures on blankpad — panel dropping, wrong refresh rate, black/flickering output — and fix the refresh without reloading the compositor or logging Frank out." --- # niri monitor triage: flaps, phantom modes, 60 Hz fallback Frank's reaction to a broken display is immediate and loud; the fix must never log him out. Box: `blankpad` (Cezanne APU, `card1`, s2idle only), compositor `niri --session` as a user unit. ## Step 1 — separate three different diseases (do this before touching anything) ```bash niri msg outputs # name, mode, position, full mode list per output for c in /sys/class/drm/card*-*/status; do echo "$(basename $(dirname $c)) $(cat $c)"; done systemctl --user show niri.service -p ExecMainStartTimestamp -p NRestarts # did niri restart? journalctl --user -b --no-pager | grep -iE 'disconnecting connector|new connector|Page flip commit failed|DMCUB|falling back to preferred' journalctl -k -b --no-pager | grep -iE 'amdgpu|PM: suspend exit|\*ERROR\*' ``` Reading it: - `disconnecting connector: "X"` then `new connector: X` → **link-level drop** (cable/dock/monitor/driver). Count them: `grep -c 'disconnecting connector'`. - `Page flip commit failed … EINVAL` bursts + kernel `dc_dmub_srv_log_diagnostic_data: DMCUB error` right after `PM: suspend exit` → **amdgpu display-engine state broken by s2idle resume**. Only a reboot clears it; never reboot without an explicit per-run OK. - `configured mode M could not be found, falling back to preferred` → **config asks for a mode the panel does not offer**; the panel silently runs at EDID-preferred (usually 60 Hz). This is the one you can fix instantly. - Config mtimes older than the symptom + no `niri` restart ⇒ the config did not cause it. Say so explicitly; agents get blamed for display problems they didn't cause. ## Step 2 — the phantom-mode trap (niri fails soft, so it looks like hardware) `output { mode "…" }` with a refresh the EDID never advertised does **not** error. niri logs a WARN and drops to `preferred` on every connect → janky 60 Hz ultrawide, blamed on the monitor. Compare the requested mode against the panel's real list: ```bash niri msg outputs | sed -n '//,/^$/p' | grep -E '^\s+[0-9]+' ``` ## Step 3 — apply the mode WITHOUT a config reload (the no-logout path) ```bash niri msg output "" mode "WxH@refresh" niri msg outputs | grep -A1 '' # confirm the new mode pgrep -c niri # compositor still alive, same PID ``` `niri msg output …` is documented as temporary ("not saved into the config file") — it changes one output and cannot log the session out. Avoid `niri action reload-config` when Frank is working: it reloads his whole config. ## Step 4 — persist, and check you edited the file that is actually live Live niri config is a symlink chain: `~/.config/niri/config.kdl` → `cfg/display.kdl` → `cfg/desk-.kdl`, and `cfg/desk-*.kdl` are symlinks into the repo: ```bash ls -l ~/.config/niri/cfg/display.kdl # -> desk-1-home.kdl grep -n '239\|180' ~/.config/niri/cfg/display.kdl # prove YOUR edit is in the live chain niri validate # parses the live config; touches nothing ``` `dotfiles/` is on the `live-sync.sh` heartbeat allowlist, so an edited tracked dotfile stays dirty until the hourly heartbeat commits it — that is the designed state, do not commit it on `main` yourself. Desk config law (its own header): panels keyed by **EDID identity**, not connector name — but connector-keyed leftovers (`DP-1`, `DP-2`) survive in `desk-1-home.kdl`, and a reconnect that comes back without an EDID string hits those instead. Align them to the same mode, or a flap silently changes your refresh rate. ## Escalate honestly Reboot clears DMCUB/EINVAL state → schedule it, don't take it. Persistent drops after a reboot → cable / dock / monitor DP-version setting, i.e. hardware, not config. Report which of the three diseases you found and which you did **not** fix.