Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.

Inbox
ReportR-731GitHub issuePublicFixed

Idle service wakes the display while the session is locked

Reported by kurtome · filed Sep 10, 2026 · open at source

Partly in the audited area (lock) — decided by agents · verified. Not validated yet.

At a glance

1 · What is claimed?
Only the report text below — no validator has restated it as checkable claims yet.
2 · What does it affect?
AreaPartly in the audited arealock· Decided by agents · verified
3 · Is it reproducible?
No validation has been run yet.
4 · What remains unclear?
  • Not validated yet: no claims, no reproducer.
5 · What happens next?
Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. So far: matches no known issue — decided by arbitration; partly in the audited area (lock) — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.

Original report

Idle service wakes the display while the session is lockedrevision 1 · captured 2026-09-28 21:23 UTC via poll

What happened

While the session is locked and the display is blanked, the idle service turns the display back on. The lock screen then stays lit — often for 15–25 minutes, sometimes indefinitely — with nobody at the machine.

Expected

A locked session stays dark. Nothing but real interaction with the lock screen should power the display back on.

Root cause

shell/plugins/services/idle/Service.qml — cancelIdleCycle() runs omarchy-system-wake (→ omarchy-brightness-display on → hl.dsp.dpms enable) without checking whether the session is locked:

if (root.idledThisCycle) runProcess(wakeProcess, "wake", "omarchy-system-wake")

There is a guard in handleActiveSignal() that is meant to keep the cycle armed rather than cancel it:

if (root.screensaverStartedThisCycle && (root.screensaverWindowCount > 0 || screensaverLaunchGraceTimer.running)) {
  logEvent("idle-monitor-active", "screensaver cycle remains armed")
  return
}

but it requires a live screensaver window — and launchScreensaver() deliberately skips the screensaver when the session is locked:

runProcess(screensaverProcess, "screensaver", "[[ $(omarchy-shell lock isLocked 2>/dev/null) == \"true\" ]] || omarchy-launch-screensaver")

So while locked, screensaverWindowCount is always 0, the grace timer has long expired, and every activity signal inside an idle cycle falls straight through to cancelIdleCycle("activity") → wake.

Compounding it, nothing re-blanks promptly. shell/plugins/lock/Service.qml's 5-second idleBlankTimer only re-arms from real lock-screen input (LockView.wakeRequested → runWake()) or a fresh beginLock(). A wake originating in the idle service does not re-arm it, so the display stays lit until the next complete idle cycle re-locks.

Note that IdleMonitor has respectInhibitors: true, so an app taking or releasing a Wayland idle inhibitor produces an isIdleChanged → "active" transition with zero user input. That path never touches LockView, so the lock screen never learns it should re-blank.

Steps to reproduce

  1. Set idle.screensaver and idle.lock in ~/.config/omarchy/shell.json (e.g. 600 / 900).
  2. Let the machine idle until it locks and the display blanks.
  3. Leave it locked and untouched. Wait for any activity signal to land between idle-cycle-start and the lock deadline — an idle-inhibitor toggle from a background app is enough; no input required.
  4. The display powers on at the lock screen and stays on.

Evidence

journalctl --user | grep "omarchy idle", machine locked and unattended overnight:

01:19:22  lock-system: lock-timeout          <- locked, display blanked
01:20:15  idle-monitor: active
01:30:21  idle-monitor: idle
01:30:21  idle-cycle-start: screensaver=600 lock=900
01:30:21  process-start: screensaver [[ $(omarchy-shell lock isLocked) == "true" ]] || omarchy-launch-screensaver
01:30:21  process-exit: screensaver exitCode=0          <- correctly skipped, so no screensaver window
01:30:32  idle-monitor: active                          <- 11s in, nobody present
01:30:32  idle-cycle-cancel: activity
01:30:32  process-start: wake omarchy-system-wake        <- display ON, still locked

Another instance where the screen stayed lit for 20 minutes until the user walked up:

23:13:32  idle-monitor: idle
23:13:41  idle-monitor: active
23:13:41  idle-cycle-cancel: activity
23:13:41  process-start: wake omarchy-system-wake
...
23:33:22  unlocked                                       <- lit the whole time

18 such wake events in 7 days on this machine.

Suggested fix

Guard the wake the same way launchScreensaver() already guards itself, so the idiom stays consistent within the file:

if (root.idledThisCycle) runProcess(wakeProcess, "wake", "[[ $(omarchy-shell lock isLocked 2>/dev/null) == \"true\" ]] || omarchy-system-wake")

This is running locally as a omarchy plugin clone omarchy.idle patch and resolves the symptom. Unlocking still wakes correctly, since lock/Service.qml's finishUnlock() calls its own runWake().

Happy to open a PR if the approach looks right.

System

Omarchy4.0.2-1
Kernel7.1.9-arch1-2
Hyprland0.56.2
Quickshell0.3.1-1
CPUAMD Ryzen AI MAX+ 395 w/ Radeon 8060S
GPUAMD Strix Halo (Radeon 8060S)
View raw text
## What happened

While the session is locked and the display is blanked, the idle service turns the display back on. The lock screen then stays lit — often for 15–25 minutes, sometimes indefinitely — with nobody at the machine.

## Expected

A locked session stays dark. Nothing but real interaction with the lock screen should power the display back on.

## Root cause

`shell/plugins/services/idle/Service.qml` — `cancelIdleCycle()` runs `omarchy-system-wake` (→ `omarchy-brightness-display on` → `hl.dsp.dpms enable`) without checking whether the session is locked:

```qml
if (root.idledThisCycle) runProcess(wakeProcess, "wake", "omarchy-system-wake")
```

There is a guard in `handleActiveSignal()` that is meant to keep the cycle armed rather than cancel it:

```qml
if (root.screensaverStartedThisCycle && (root.screensaverWindowCount > 0 || screensaverLaunchGraceTimer.running)) {
  logEvent("idle-monitor-active", "screensaver cycle remains armed")
  return
}
```

but it requires a live screensaver window — and `launchScreensaver()` deliberately skips the screensaver when the session is locked:

```qml
runProcess(screensaverProcess, "screensaver", "[[ $(omarchy-shell lock isLocked 2>/dev/null) == \"true\" ]] || omarchy-launch-screensaver")
```

So while locked, `screensaverWindowCount` is always 0, the grace timer has long expired, and **every** activity signal inside an idle cycle falls straight through to `cancelIdleCycle("activity")` → wake.

Compounding it, nothing re-blanks promptly. `shell/plugins/lock/Service.qml`'s 5-second `idleBlankTimer` only re-arms from real lock-screen input (`LockView.wakeRequested` → `runWake()`) or a fresh `beginLock()`. A wake originating in the idle service does not re-arm it, so the display stays lit until the *next* complete idle cycle re-locks.

Note that `IdleMonitor` has `respectInhibitors: true`, so an app taking or releasing a Wayland idle inhibitor produces an `isIdleChanged` → "active" transition with **zero** user input. That path never touches `LockView`, so the lock screen never learns it should re-blank.

## Steps to reproduce

1. Set `idle.screensaver` and `idle.lock` in `~/.config/omarchy/shell.json` (e.g. `600` / `900`).
2. Let the machine idle until it locks and the display blanks.
3. Leave it locked and untouched. Wait for any activity signal to land between `idle-cycle-start` and the lock deadline — an idle-inhibitor toggle from a background app is enough; no input required.
4. The display powers on at the lock screen and stays on.

## Evidence

`journalctl --user | grep "omarchy idle"`, machine locked and unattended overnight:

```
01:19:22  lock-system: lock-timeout          <- locked, display blanked
01:20:15  idle-monitor: active
01:30:21  idle-monitor: idle
01:30:21  idle-cycle-start: screensaver=600 lock=900
01:30:21  process-start: screensaver [[ $(omarchy-shell lock isLocked) == "true" ]] || omarchy-launch-screensaver
01:30:21  process-exit: screensaver exitCode=0          <- correctly skipped, so no screensaver window
01:30:32  idle-monitor: active                          <- 11s in, nobody present
01:30:32  idle-cycle-cancel: activity
01:30:32  process-start: wake omarchy-system-wake        <- display ON, still locked
```

Another instance where the screen stayed lit for 20 minutes until the user walked up:

```
23:13:32  idle-monitor: idle
23:13:41  idle-monitor: active
23:13:41  idle-cycle-cancel: activity
23:13:41  process-start: wake omarchy-system-wake
...
23:33:22  unlocked                                       <- lit the whole time
```

18 such wake events in 7 days on this machine.

## Suggested fix

Guard the wake the same way `launchScreensaver()` already guards itself, so the idiom stays consistent within the file:

```qml
if (root.idledThisCycle) runProcess(wakeProcess, "wake", "[[ $(omarchy-shell lock isLocked 2>/dev/null) == \"true\" ]] || omarchy-system-wake")
```

This is running locally as a `omarchy plugin clone omarchy.idle` patch and resolves the symptom. Unlocking still wakes correctly, since `lock/Service.qml`'s `finishUnlock()` calls its own `runWake()`.

Happy to open a PR if the approach looks right.

## System

| | |
|---|---|
| Omarchy | 4.0.2-1 |
| Kernel | 7.1.9-arch1-2 |
| Hyprland | 0.56.2 |
| Quickshell | 0.3.1-1 |
| CPU | AMD Ryzen AI MAX+ 395 w/ Radeon 8060S |
| GPU | AMD Strix Halo (Radeon 8060S) |

Also reported as

This defect is tracked under R-731 (filed Sep 10, 2026). 1 other report describes the same defect.

Earliest report of this defect: R-1050 (filed Sep 2, 2026).

  1. Sep 10, 2026R-731 · this report— tracked here
  2. ·Decided by agents · verified

    Open upstream.

Validation

Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.

Linked

  • Fixed bypr/9152· confirmed

Decision

Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.

  • Decided by arbitration: matches no known issue
  • Decided by agents · verified: partly in the audited area (lock)

The outcome (valid, duplicate, not a bug, …) is not decided yet.