Skip to content

Desktop-mode external display blackout diagnosis and remediation - #30

Merged
deucebucket merged 2 commits into
masterfrom
feature/external-display-blackout
Jul 31, 2026
Merged

Desktop-mode external display blackout diagnosis and remediation#30
deucebucket merged 2 commits into
masterfrom
feature/external-display-blackout

Conversation

@deucebucket

Copy link
Copy Markdown
Owner

Closes #29.

What

  • display_blackout.sh: new read-only "External display / desktop session" section. Enumerates connected non-eDP connectors, detects an active KWin session, matches each display's EDID md5 against the stored policy in kwinoutputconfig.json, and reports the per-display vrrPolicy. With --display-black declared and a healthy deck-side path it emits:
    • EXTERNAL_BLACKOUT_SIGNATURE: FULLSCREEN_SYNC_POLICY_GAP — adaptive sync not pinned; recommends the new remediation
    • EXTERNAL_BLACKOUT_SIGNATURE: SYNC_POLICY_ALREADY_PINNED — routes investigation to the dock segment/cable/monitor
  • rem_external_display.sh (new, behind --fix-external-display-blackout): prechecks (symptom declared, KWin active, Wayland socket, external connector present), backs up kwinoutputconfig.json, applies kscreen-doctor output.<name>.vrrpolicy.never through the session user, verifies both live state and per-EDID persistence. Same safety contract as the internal-panel remediation — no power/brightness/clock writes.
  • Fix: a connected-but-disabled eDP (docked/external-only setups) reports a lit backlight and previously claimed LIVE_RENDER_TO_PHYSICAL_SCANOUT_GAP, shadowing the external diagnosis. The internal signature now requires the panel to be enabled in the output configuration.

Testing

  • tests/test_runner.sh: all 33 tests pass. New Test 32 (fixture-driven signature classification incl. the disabled-panel shadowing regression) and Test 33 (remediation safety contract + flag wiring). Module count check updated 21→22.
  • Field-validated on real hardware (Jupiter LCD, SteamOS holo, docked KWin desktop session, 1080p60 monitor via VIA Labs dock): diagnostic correctly EDID-matched the live monitor and classified SYNC_POLICY_ALREADY_PINNED; remediation ran its full precheck→backup→execute→verify lifecycle as root with session routing via runuser. This is the same unit where fullscreen VRR engagement caused repeated monitor blanking with provably zero deck-side DRM events (DP+KMS debug enabled during active blanking), and where vrrpolicy.never stopped it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V1n18hseXF7gTZPEPbWJhH

Covers the docked desktop-mode gap from #29: display_blackout.sh now
inspects connected non-eDP connectors in a KWin session, matches each
display's EDID md5 against the stored KWin output policy, and classifies
a declared physical-black symptom with a healthy deck-side link as
FULLSCREEN_SYNC_POLICY_GAP or SYNC_POLICY_ALREADY_PINNED. A guarded
rem_external_display.sh behind --fix-external-display-blackout pins
adaptive sync off via kscreen-doctor with config backup and live plus
persisted verification. A connected-but-disabled internal panel no
longer shadows the external diagnosis.

Field-validated on a Jupiter LCD in a docked KWin session where
fullscreen VRR engagement blanked a 1080p60 monitor with zero deck-side
DRM events; vrrpolicy.never stopped the blanking and persisted per-EDID.

Closes #29

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1n18hseXF7gTZPEPbWJhH
@deucebucket

Copy link
Copy Markdown
Owner Author

Code review

Found 2 issues:

  1. An undetermined VRR policy is misreported as already fixed. EXTERNAL_VRR_PINNED is tri-state (unknown/true/false) but the assessment only branches on "false", so unknown — no kwinoutputconfig.json, no matching edidHash entry, or md5sum unavailable, i.e. exactly the first occurrence on a fresh setup — falls into the else and prints SYNC_POLICY_ALREADY_PINNED, steering the user away from --fix-external-display-blackout. Reproduced with a synthetic sysfs and a nonexistent config path; Test 32 only covers the Automatic/Never matched cases.

https://github.com/deucebucket/deckdoc/blob/94ed69c66023bc11cbe3f4a52dd83b4ee9161fb1/modules/display_blackout.sh#L218-L228

  1. VERIFY is unscoped and can false-pass (repeat of the scoping feedback from PR v3.0-remediation: rem_audio_sof, --fix flag, and remediation lifecycle framework #12's adversarial review). Both checks grep the entire kscreen-doctor -o output for Vrr: Never and the entire kwinoutputconfig.json for "vrrPolicy": "Never" rather than the specific outputs in $APPLIED. Any other display already pinned — including a stale per-EDID entry from a previously fixed monitor, which persists in the config indefinitely — satisfies both greps even if the targeted output's change failed. On a config that already contains one Never entry, every future run against a different monitor false-passes the persistence check immediately.

https://github.com/deucebucket/deckdoc/blob/94ed69c66023bc11cbe3f4a52dd83b4ee9161fb1/modules/rem_external_display.sh#L106-L118

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

An unreadable KWin output policy (missing config, no matching edidHash,
no md5sum) was classified as SYNC_POLICY_ALREADY_PINNED, steering users
away from the fix exactly on first occurrence; it now reports a distinct
SYNC_POLICY_UNDETERMINED that recommends the reversible remediation.
Per-connector policy state is tracked across all external displays so a
pinned display cannot mask an unpinned one, and the internal and
external assessments are independent so an extended desktop reports
both. rem_external_display.sh VERIFY is scoped to each targeted output
(live per-Output stanza, persistence per-EDID) so a policy already
pinned elsewhere or a stale entry can no longer false-pass. Tests cover
the undetermined path and the scoping contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1n18hseXF7gTZPEPbWJhH
@deucebucket

Copy link
Copy Markdown
Owner Author

Both review findings addressed in 91b14b4: an unreadable policy now reports SYNC_POLICY_UNDETERMINED (recommending the remediation, never claiming it's fixed), per-connector state is tracked so displays can't mask each other, assessments are independent for extended desktops, and VERIFY is scoped per targeted output (live per-Output stanza + per-EDID persistence). All 33 tests pass; re-validated on the field Jupiter unit (correct SYNC_POLICY_ALREADY_PINNED via per-EDID match, remediation lifecycle green).

@deucebucket
deucebucket merged commit bc8f8e6 into master Jul 31, 2026
2 checks passed
@deucebucket
deucebucket deleted the feature/external-display-blackout branch July 31, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop-mode external display blackout: no diagnosis or remediation coverage

1 participant