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-4901GitHub issuePublicReceived

Lock: writing to a plugin directory while locked hot-reloads the shell, and stranded-lock recovery then aborts Quickshell

Reported by danielhernik · filed Sep 5, 2026Closed upstream on 2026-09-20 by bjarneo via an automated message, as a duplicate of #6888 (R-1855). GitHub close reason: completed.

Received and recorded verbatim. Nothing has been checked 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?
Not yet mapped to any requirement or component.
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. No decision has been recorded yet.

Original report

Lock: writing to a plugin directory while locked hot-reloads the shell, and stranded-lock recovery then aborts Quickshellrevision 1 · captured 2026-10-04 12:10 UTC via poll

What happened

Writing any file inside a local plugin directory (~/.config/omarchy/plugins/<plugin>/)
while the session is locked triggers a shell QML hot-reload. The reload tears down and
rebuilds the lock service, but the compositor's ext-session-lock outlives the QML
objects that took it — so the new lock service correctly detects an orphan
(lock-stranded: recovering) and re-requests a lock. That request never yields a
compositor lock object, and the eventual sessionLock.locked = true write aborts
Quickshell outright:

FATAL: Tried to show lockscreen surfaces without active lock

Symbolized against Arch debuginfod:

#13 WlSessionLock::updateSurfaces (this=0x..., show=true, old=0x0)
        at src/wayland/session_lock.cpp:106      <- qFatal -> abort
#14 WlSessionLock::qt_metacall (_c=WriteProperty, _id=0)
#15-#21 QQmlPropertyPrivate::write <- QV4 setter <- JS
#30 QQmlTimer::ticked

The only QML that writes that property is requestSessionLock() at
shell/plugins/lock/Service.qml:76, reached from its two timers
(sessionLockStabilizeTimer, 500 ms one-shot; pendingSessionLockTimer, 100 ms repeat).

What I expected

A session lock the compositor cannot grant should leave the shell running and retrying,
not abort it. requestSessionLock() sets locked = true unconditionally once its guards
pass, and Quickshell's updateSurfaces treats "show surfaces, no active lock" as a fatal
invariant rather than a recoverable failure.

Steps to reproduce

  1. Let the session lock (idle timeout or omarchy-system-lock). Confirm secure=true.
  2. While locked, write any file under ~/.config/omarchy/plugins/<some-local-plugin>/.

In my case a Markdown file — it does not need to be QML; a git commit in that
directory is enough.
3. The shell logs Local plugin changed, reloading: <plugin>, then
lock-stranded: recovering / lock-requested / lock-pending: screen-stabilizing.
4. Quickshell aborts with the FATAL above and restarts itself.

Timeline from my journal

15:39:45  omarchy idle  lock-system: lock-timeout
15:39:46  omarchy lock  secure=true
15:39:54  hyprmoncfgd   display sleep detected
15:39:55  DEBUG qml: Local plugin changed, reloading: <plugin>   <- file written in plugin dir
15:39:57  omarchy lock  lock-stranded: recovering
15:39:57  omarchy lock  lock-requested
15:39:57  omarchy lock  lock-pending: screen-stabilizing
   ... lock service logs nothing for two minutes ...
15:41:59  FATAL: Tried to show lockscreen surfaces without active lock
15:41:59  ERROR: Quickshell has crashed under pid 3322809
15:42:03  omarchy lock  secure=true        <- restarted shell recovers the lock

The session did not fall open: the restarted shell re-acquired the lock four seconds later.

What I could not determine

The lock service emits nothing between lock-pending: screen-stabilizing and the abort
two minutes later, and I cannot account for that gap. Each path that would sit silent
that long contradicts some other evidence — notably lock-pending: no-real-screen was
never logged, on this boot or any other. My displays were asleep from 15:39:54 to
15:42:04 and the abort lands at the very end of that window, so outputs returning is
the plausible trigger for the timer finally getting past its guards, but I cannot show it.

Likewise, that the orphaned lock is why old was nullptr is inference. What is
proven is that omarchy-hyprland-session-locked exited 0 while this shell held no lock
of its own — the compositor did hold a lock the shell did not own.

Only one occurrence: a single crash directory in ~/.cache/quickshell/crashes/ and a
single instance of that FATAL in the whole journal.

System

Omarchy      4.0.2-1
Hyprland     0.56.2
quickshell   0.3.1-1  (Arch, RelWithDebInfo)
qt6-base     6.11.2-2
kernel       7.1.8-arch1 (x86_64, T2 MacBook)
GPU          amdgpu 1002:67ef + i915 8086:3e9b

Collected by hand because omarchy debug --no-sudo --print is not reachable through
the omarchy dispatcher on 4.0.2 (Unknown Omarchy command). The script itself does
ship, as /usr/bin/omarchy-debug (owned by omarchy-settings 4.0.2-1), and runs fine
when invoked directly. That dispatcher gap is already tracked in #7185.

Filed by Claude Opus 5 (1M context) via Claude Code.

View raw text
## What happened

Writing any file inside a local plugin directory (`~/.config/omarchy/plugins/<plugin>/`)
while the session is locked triggers a shell QML hot-reload. The reload tears down and
rebuilds the lock service, but the compositor's `ext-session-lock` outlives the QML
objects that took it — so the new lock service correctly detects an orphan
(`lock-stranded: recovering`) and re-requests a lock. That request never yields a
compositor lock object, and the eventual `sessionLock.locked = true` write aborts
Quickshell outright:

```
FATAL: Tried to show lockscreen surfaces without active lock
```

Symbolized against Arch debuginfod:

```
#13 WlSessionLock::updateSurfaces (this=0x..., show=true, old=0x0)
        at src/wayland/session_lock.cpp:106      <- qFatal -> abort
#14 WlSessionLock::qt_metacall (_c=WriteProperty, _id=0)
#15-#21 QQmlPropertyPrivate::write <- QV4 setter <- JS
#30 QQmlTimer::ticked
```

The only QML that writes that property is `requestSessionLock()` at
`shell/plugins/lock/Service.qml:76`, reached from its two timers
(`sessionLockStabilizeTimer`, 500 ms one-shot; `pendingSessionLockTimer`, 100 ms repeat).

## What I expected

A session lock the compositor cannot grant should leave the shell running and retrying,
not abort it. `requestSessionLock()` sets `locked = true` unconditionally once its guards
pass, and Quickshell's `updateSurfaces` treats "show surfaces, no active lock" as a fatal
invariant rather than a recoverable failure.

## Steps to reproduce

1. Let the session lock (idle timeout or `omarchy-system-lock`). Confirm `secure=true`.
2. While locked, write any file under `~/.config/omarchy/plugins/<some-local-plugin>/`.
   In my case a Markdown file — it does not need to be QML; a `git commit` in that
   directory is enough.
3. The shell logs `Local plugin changed, reloading: <plugin>`, then
   `lock-stranded: recovering` / `lock-requested` / `lock-pending: screen-stabilizing`.
4. Quickshell aborts with the FATAL above and restarts itself.

## Timeline from my journal

```
15:39:45  omarchy idle  lock-system: lock-timeout
15:39:46  omarchy lock  secure=true
15:39:54  hyprmoncfgd   display sleep detected
15:39:55  DEBUG qml: Local plugin changed, reloading: <plugin>   <- file written in plugin dir
15:39:57  omarchy lock  lock-stranded: recovering
15:39:57  omarchy lock  lock-requested
15:39:57  omarchy lock  lock-pending: screen-stabilizing
   ... lock service logs nothing for two minutes ...
15:41:59  FATAL: Tried to show lockscreen surfaces without active lock
15:41:59  ERROR: Quickshell has crashed under pid 3322809
15:42:03  omarchy lock  secure=true        <- restarted shell recovers the lock
```

The session did not fall open: the restarted shell re-acquired the lock four seconds later.

## What I could not determine

The lock service emits nothing between `lock-pending: screen-stabilizing` and the abort
two minutes later, and I cannot account for that gap. Each path that would sit silent
that long contradicts some other evidence — notably `lock-pending: no-real-screen` was
never logged, on this boot or any other. My displays were asleep from 15:39:54 to
15:42:04 and the abort lands at the very end of that window, so outputs returning is
the plausible trigger for the timer finally getting past its guards, but I cannot show it.

Likewise, that the orphaned lock is *why* `old` was `nullptr` is inference. What is
proven is that `omarchy-hyprland-session-locked` exited 0 while this shell held no lock
of its own — the compositor did hold a lock the shell did not own.

Only one occurrence: a single crash directory in `~/.cache/quickshell/crashes/` and a
single instance of that FATAL in the whole journal.

## System

```
Omarchy      4.0.2-1
Hyprland     0.56.2
quickshell   0.3.1-1  (Arch, RelWithDebInfo)
qt6-base     6.11.2-2
kernel       7.1.8-arch1 (x86_64, T2 MacBook)
GPU          amdgpu 1002:67ef + i915 8086:3e9b
```

Collected by hand because `omarchy debug --no-sudo --print` is not reachable through
the `omarchy` dispatcher on 4.0.2 (`Unknown Omarchy command`). The script itself does
ship, as `/usr/bin/omarchy-debug` (owned by `omarchy-settings 4.0.2-1`), and runs fine
when invoked directly. That dispatcher gap is already tracked in #7185.

Filed by Claude Opus 5 (1M context) via Claude Code.

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

Not linked to any known issue, other report or change yet.

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.

No decision has been recorded yet.