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 service can wedge forever in "lock-pending: screen-stabilizing" when a plugin reload races the stranded-lock retake; restart-shell still refuses in that state
Reported by vinceferro · filed Sep 16, 2026 · open at source
R-1855 is the earliest report of this defect — filed Aug 14, 2026, open upstream. This report (filed Sep 16, 2026) describes the same defect, so it is folded under it in the inbox.
Decided by agents · verified· Oct 9, 2026
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?
- 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?
- Decided by agents and verified by a second, independent agent — see the decision.
Original report
Environment
- Omarchy 4.0.2-2 (installed 2026-09-09), Apple Silicon (Asahi), stock shell
- Third-party plugin present: a locally-installed bar-widget (
vinceferro.gateway-stats) whose files change occasionally while the shell runs
What happened (2026-09-14, journal receipts)
The shell's local-plugin watcher fired a service re-init while a stranded-lock retake was mid-transition, and the lock service wedged forever in the stabilize wait:
16:31:02 qml: Local plugin changed, reloading: vinceferro.gateway-stats (×16)
16:31:02 omarchy idle … service-ready / polkit agent registered (service re-init storm)
16:31:03 omarchy lock lock-stranded: recovering
16:31:03 omarchy lock lock-requested
16:31:03 omarchy lock lock-pending: screen-stabilizing
… silence forever — no secure=true, no session-locked=true from this shell, ever
16:31:03 omarchy idle stay-awake: enabled / idle-cycle-cancel: stay-awake (user active, desktop visible)
16:32:31 (manual recovery: TERM the shell, relaunch)
16:32:32 fresh shell: lock-stranded: recovering → secure=true in 0.6s (the retake works perfectly when not racing)During the wedge the state was: lockRequested: true + pendingSessionLock + sessionLock.locked: false, while the compositor reported a lock (the failsafe left by the killed client). In that state omarchy-restart-shell refuses:
omarchy-hyprland-session-locked → exit 0 (compositor locked)
omarchy-shell lock status | jq '.secure or .requested' → "true" (requested:true)
→ "Refusing to restart Omarchy shell while the session is locked."So the only recovery is an unsanctioned TERM of the shell process. The #6692 retake machinery itself is excellent — we observed three clean 0.4–0.6s retakes and normal PAM cycles after recovery — the problem is only the state it can leave behind when it races a service re-init.
Where the code stands (quattro tip)
shell/plugins/lock/Service.qml— the screen-stabilizing wait has no timeout: a 500ms one-shot plus a 100ms repeating retry that is a no-op when the compositor never acknowledges, solock-pendingcan hold forever.checkStrandedLock()still treats "we requested a lock" as nobody's orphan (it returns early whenlocked || lockRequested), so a wedged requester never self-heals.bin/omarchy-restart-shellcomposites.secure or .requestedbehind the compositor-locked gate — correct for real locks, but it also refuses for a stuck request that never became a lock while the compositor holds a failsafe.
Proposal (narrow)
- Timeout the stabilize wait: if
lock-requested/lock-pending: screen-stabilizingpersists past a deadline (say 30s) withoutsecure, treat the request as failed — tear it down (sessionLock.unlock()path) and re-arm, instead of holding forever. - Optionally, feed
omarchy-hyprland-session-lockedinto that watchdog as a veto/input: a compositor lock that appears while our request is stuck-and-unacknowledged is distinguishable from our own lock, and a restart in that state should be permissible since the fresh shell's retake recovers it cleanly (observed).
Happy to provide the full journal window, the exact reload-storm trigger (plugin file changes under ~/.config/omarchy/plugins/), or test a patch — I have a reproducible pattern (file changes while a lock transition is in flight).
View raw text
## Environment
- Omarchy 4.0.2-2 (installed 2026-09-09), Apple Silicon (Asahi), stock shell
- Third-party plugin present: a locally-installed bar-widget (`vinceferro.gateway-stats`) whose files change occasionally while the shell runs
## What happened (2026-09-14, journal receipts)
The shell's local-plugin watcher fired a service re-init while a stranded-lock retake was mid-transition, and the lock service wedged forever in the stabilize wait:
```
16:31:02 qml: Local plugin changed, reloading: vinceferro.gateway-stats (×16)
16:31:02 omarchy idle … service-ready / polkit agent registered (service re-init storm)
16:31:03 omarchy lock lock-stranded: recovering
16:31:03 omarchy lock lock-requested
16:31:03 omarchy lock lock-pending: screen-stabilizing
… silence forever — no secure=true, no session-locked=true from this shell, ever
16:31:03 omarchy idle stay-awake: enabled / idle-cycle-cancel: stay-awake (user active, desktop visible)
16:32:31 (manual recovery: TERM the shell, relaunch)
16:32:32 fresh shell: lock-stranded: recovering → secure=true in 0.6s (the retake works perfectly when not racing)
```
During the wedge the state was: `lockRequested: true` + `pendingSessionLock` + `sessionLock.locked: false`, while the compositor reported a lock (the failsafe left by the killed client). In that state `omarchy-restart-shell` refuses:
```
omarchy-hyprland-session-locked → exit 0 (compositor locked)
omarchy-shell lock status | jq '.secure or .requested' → "true" (requested:true)
→ "Refusing to restart Omarchy shell while the session is locked."
```
So the only recovery is an unsanctioned `TERM` of the shell process. The #6692 retake machinery itself is excellent — we observed three clean 0.4–0.6s retakes and normal PAM cycles after recovery — the problem is only the state it can leave behind when it races a service re-init.
## Where the code stands (quattro tip)
- `shell/plugins/lock/Service.qml` — the screen-stabilizing wait has **no timeout**: a 500ms one-shot plus a 100ms repeating retry that is a no-op when the compositor never acknowledges, so `lock-pending` can hold forever.
- `checkStrandedLock()` still treats "we requested a lock" as nobody's orphan (it returns early when `locked || lockRequested`), so a wedged requester never self-heals.
- `bin/omarchy-restart-shell` composites `.secure or .requested` behind the compositor-locked gate — correct for real locks, but it also refuses for a *stuck request that never became a lock* while the compositor holds a failsafe.
## Proposal (narrow)
1. **Timeout the stabilize wait**: if `lock-requested`/`lock-pending: screen-stabilizing` persists past a deadline (say 30s) without `secure`, treat the request as failed — tear it down (`sessionLock.unlock()` path) and re-arm, instead of holding forever.
2. Optionally, feed `omarchy-hyprland-session-locked` into that watchdog as a veto/input: a compositor lock that appears while our request is stuck-and-unacknowledged is distinguishable from our own lock, and a restart in that state should be permissible since the fresh shell's retake recovers it cleanly (observed).
Happy to provide the full journal window, the exact reload-storm trigger (plugin file changes under `~/.config/omarchy/plugins/`), or test a patch — I have a reproducible pattern (file changes while a lock transition is in flight).
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/7169· confirmed
- Related PRpr/11240· confirmed
- Duplicate ofR-1855Stranded-lock recovery never completes: requestSessionLock() early-returns on sessionLock.secure, so the session stays on "lockscreen app died" forever· decided by agents · verified
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.