Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA 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.
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.
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
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 lockSymbolized 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::tickedThe only QML that writes that property is requestSessionLock() atshell/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
- Let the session lock (idle timeout or
omarchy-system-lock). Confirmsecure=true. - 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 lockThe 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:3e9bCollected 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.