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.
Superseding a pending omarchy-menu-select leaks a permanent 20 fork/sec busy-wait loop
Reported by designgears · filed Aug 22, 2026 · open at source
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
What happened
Any omarchy-menu-select request that is superseded rather than answered leaks abash process that spins in a sleep 0.05 busy-wait forever. Each leaked
process forks 20 times per second for the life of the session.
These accumulate silently and without bound. On my machine 15 had built up over
~30 minutes of normal use, driving the system to 273 forks/sec at idle
(baseline is ~2). A further one leaked during the hour I spent writing this
report — I found it parented to Hyprland, still spinning after 560s — so the rate
of accumulation tracks ordinary menu use.
Each leak also strands a mktemp file in /tmp (see below), so this is an
unbounded process and fd/inode leak for the life of the session.
On measured impact, to be straight about it: I could not measure an app-launch
penalty from this on a 16-core Core Ultra 9. Timing foot to first mapped window,
3 runs each, gave 81/94/88 ms at a clean 22 forks/sec and 78/98/83 ms under a
synthetic 312 forks/sec — no meaningful difference, and nautilus was 647 ms vs
660 ms. So I am not claiming this causes general desktop sluggishness; on this
hardware it does not. The bug I am reporting is the unbounded leak itself. On a
2–4 core machine the same 273 forks/sec would be a far larger share of the CPU,
and the leak grows without limit regardless of core count.
What I expected
A superseded or dismissed menu request should write its done_file so the
waiting script exits.
Root cause
bin/omarchy-menu-select:91 waits for the shell to signal completion:
omarchy-shell shell summon omarchy.menu "$payload" >/dev/null
while [[ ! -e $done_file ]]; do
sleep 0.05
doneThe done_file is only ever written by finishRequest() inshell/plugins/menu/Menu.qml:117. The dismiss path guards that call ondmenuActive (Menu.qml:789):
function cancel() {
if (root.dmenuActive) root.finishRequest(null)
opened = false
filterText = ""
}But openExistingMenu() (Menu.qml:795) unconditionally discards the pending
request and flips the mode the guard depends on:
function openExistingMenu(initialMenu) {
requestSerial += 1
mode = "menu" // dmenuActive (Menu.qml:56) is now false
requestActive = false
selectionFile = ""
doneFile = "" // the waiting script's handle is gone
...Since dmenuActive is mode === "select" || mode === "input" (Menu.qml:56),
setting mode = "menu" makes the cancel() guard false. So the later dismiss
cannot write the done_file, and finishRequest() would early-return anyway
because requestActive is false and doneFile is "".
The bash script has no timeout and no liveness check, so it waits forever.
Note the trap ... EXIT on line 73 never runs either, so the mktempselection_file is leaked to /tmp as well — I had 14 stale ones.
Steps to reproduce
# baseline
b=$(awk '/^processes/{print $2}' /proc/stat); sleep 2
a=$(awk '/^processes/{print $2}' /proc/stat); echo "$(( (a-b)/2 )) forks/sec"
# 1. start a select request
printf 'alpha\nbravo\n' | omarchy-menu-select 'ReproTest' -- --width 400 --height 300 &
# 2. supersede it with the regular menu (SUPER+ALT+SPACE works too)
omarchy-shell shell summon omarchy.menu ''
# 3. dismiss that menu with Escape, then observeResult — the menu is fully gone from hyprctl layers, but:
$ ps -o pid,etimes,stat,wchan:20,args -p <pid>
PID ETIMES STAT WCHAN COMMAND
160400 45 S do_wait /bin/bash .../omarchy-menu-select ReproTest ...
$ for i in $(seq 10); do pgrep -P 160400 -a; done | sort -u
161585 sleep 0.05
161588 sleep 0.05
161591 sleep 0.05
...Measured fork rate, same machine:
| state | forks/sec |
|---|---|
| idle baseline | 2 |
| 1 leaked spinner | 20 |
| 15 leaked spinners (as found) | 273 |
Killing the orphans returns the machine to 2 forks/sec. The leaked processes
never exit on their own — I confirmed one still in do_wait after 45s, and the
in-the-wild one had been spinning for 560s when I found it.
Suggested fix
Two independent problems; the first is the actual bug.
- Don't drop a pending request on the floor. Have
openExistingMenu()(and
any other path that clears doneFile/requestActive) finish the outstanding
request first, e.g. call finishRequest(null) before reassigning mode,
rather than relying on a cancel() guard that the reassignment itself
invalidates.
- Make the wait cheap and bounded. Even once fixed, a 20 Hz fork loop is a
costly way to wait on a file. inotifywait on the parent directory, or a
read on a fifo, would block at zero cost — and a liveness check against the
shell would keep a missed signal from hanging forever instead of leaking
silently.
System details
omarchy 4.0.0-1 (channel: stable, reported up to date)
quickshell-git 0.3.0.r20.g28771c7-2
hyprland 0.56.2-1
kernel 7.1.8-arch1-3
hardware ASUS ROG Zephyrus G16 GU605CRBoth affected files are owned by omarchy 4.0.0-1 per pacman -Qo.
Not a duplicate of #7025
#7025 (omarchy-menu-keybindings hangs forever) also involves a stuck
keybindings menu, but the mechanism is unrelated: there, build_lua_bind_cache()
spins in an infinite ipairs() loop against a permissive hl stub, blocking
before the menu is ever summoned. Here the menu is summoned and displayed
normally, no Lua evaluation is involved (my configs contain no get_monitors /get_windows / get_workspaces calls, and no lua process exists), and the
hang is in the post-summon done_file wait. Fixing #7025 would not address this.
It is the same class as #7124, #7150, #6989, #6881 and #6172 (orphaned helper
processes outliving their owner) but a different site and trigger.
I also checked the closed sluggishness reports: #6768 is Panther Lake / Arc B390
specific and was fixed in omacom-io/omarchy-pkgs#138, and #4336 concerns the
keybindings window's contents, not a leaked process. Neither is this bug, so
this is a new report rather than a regression.
Filed by Claude Opus 5 via Claude Code.
View raw text
## What happened
Any `omarchy-menu-select` request that is superseded rather than answered leaks a
`bash` process that spins in a `sleep 0.05` busy-wait **forever**. Each leaked
process forks 20 times per second for the life of the session.
These accumulate silently and without bound. On my machine 15 had built up over
~30 minutes of normal use, driving the system to **273 forks/sec at idle**
(baseline is ~2). A further one leaked during the hour I spent writing this
report — I found it parented to Hyprland, still spinning after 560s — so the rate
of accumulation tracks ordinary menu use.
Each leak also strands a `mktemp` file in `/tmp` (see below), so this is an
unbounded process **and** fd/inode leak for the life of the session.
**On measured impact, to be straight about it:** I could not measure an app-launch
penalty from this on a 16-core Core Ultra 9. Timing `foot` to first mapped window,
3 runs each, gave 81/94/88 ms at a clean 22 forks/sec and 78/98/83 ms under a
synthetic 312 forks/sec — no meaningful difference, and `nautilus` was 647 ms vs
660 ms. So I am **not** claiming this causes general desktop sluggishness; on this
hardware it does not. The bug I am reporting is the unbounded leak itself. On a
2–4 core machine the same 273 forks/sec would be a far larger share of the CPU,
and the leak grows without limit regardless of core count.
## What I expected
A superseded or dismissed menu request should write its `done_file` so the
waiting script exits.
## Root cause
`bin/omarchy-menu-select:91` waits for the shell to signal completion:
```bash
omarchy-shell shell summon omarchy.menu "$payload" >/dev/null
while [[ ! -e $done_file ]]; do
sleep 0.05
done
```
The `done_file` is only ever written by `finishRequest()` in
`shell/plugins/menu/Menu.qml:117`. The dismiss path guards that call on
`dmenuActive` (`Menu.qml:789`):
```qml
function cancel() {
if (root.dmenuActive) root.finishRequest(null)
opened = false
filterText = ""
}
```
But `openExistingMenu()` (`Menu.qml:795`) unconditionally discards the pending
request *and* flips the mode the guard depends on:
```qml
function openExistingMenu(initialMenu) {
requestSerial += 1
mode = "menu" // dmenuActive (Menu.qml:56) is now false
requestActive = false
selectionFile = ""
doneFile = "" // the waiting script's handle is gone
...
```
Since `dmenuActive` is `mode === "select" || mode === "input"` (`Menu.qml:56`),
setting `mode = "menu"` makes the `cancel()` guard false. So the later dismiss
cannot write the `done_file`, and `finishRequest()` would early-return anyway
because `requestActive` is false and `doneFile` is `""`.
The bash script has no timeout and no liveness check, so it waits forever.
Note the `trap ... EXIT` on line 73 never runs either, so the `mktemp`
`selection_file` is leaked to `/tmp` as well — I had 14 stale ones.
## Steps to reproduce
```bash
# baseline
b=$(awk '/^processes/{print $2}' /proc/stat); sleep 2
a=$(awk '/^processes/{print $2}' /proc/stat); echo "$(( (a-b)/2 )) forks/sec"
# 1. start a select request
printf 'alpha\nbravo\n' | omarchy-menu-select 'ReproTest' -- --width 400 --height 300 &
# 2. supersede it with the regular menu (SUPER+ALT+SPACE works too)
omarchy-shell shell summon omarchy.menu ''
# 3. dismiss that menu with Escape, then observe
```
Result — the menu is fully gone from `hyprctl layers`, but:
```
$ ps -o pid,etimes,stat,wchan:20,args -p <pid>
PID ETIMES STAT WCHAN COMMAND
160400 45 S do_wait /bin/bash .../omarchy-menu-select ReproTest ...
$ for i in $(seq 10); do pgrep -P 160400 -a; done | sort -u
161585 sleep 0.05
161588 sleep 0.05
161591 sleep 0.05
...
```
Measured fork rate, same machine:
| state | forks/sec |
|---|---|
| idle baseline | 2 |
| 1 leaked spinner | 20 |
| 15 leaked spinners (as found) | 273 |
Killing the orphans returns the machine to 2 forks/sec. The leaked processes
never exit on their own — I confirmed one still in `do_wait` after 45s, and the
in-the-wild one had been spinning for 560s when I found it.
## Suggested fix
Two independent problems; the first is the actual bug.
1. **Don't drop a pending request on the floor.** Have `openExistingMenu()` (and
any other path that clears `doneFile`/`requestActive`) finish the outstanding
request first, e.g. call `finishRequest(null)` before reassigning `mode`,
rather than relying on a `cancel()` guard that the reassignment itself
invalidates.
2. **Make the wait cheap and bounded.** Even once fixed, a 20 Hz fork loop is a
costly way to wait on a file. `inotifywait` on the parent directory, or a
`read` on a fifo, would block at zero cost — and a liveness check against the
shell would keep a missed signal from hanging forever instead of leaking
silently.
## System details
```
omarchy 4.0.0-1 (channel: stable, reported up to date)
quickshell-git 0.3.0.r20.g28771c7-2
hyprland 0.56.2-1
kernel 7.1.8-arch1-3
hardware ASUS ROG Zephyrus G16 GU605CR
```
Both affected files are owned by `omarchy 4.0.0-1` per `pacman -Qo`.
## Not a duplicate of #7025
#7025 (`omarchy-menu-keybindings` hangs forever) also involves a stuck
keybindings menu, but the mechanism is unrelated: there, `build_lua_bind_cache()`
spins in an infinite `ipairs()` loop against a permissive `hl` stub, blocking
before the menu is ever summoned. Here the menu is summoned and displayed
normally, no Lua evaluation is involved (my configs contain no `get_monitors` /
`get_windows` / `get_workspaces` calls, and no `lua` process exists), and the
hang is in the post-summon `done_file` wait. Fixing #7025 would not address this.
It is the same *class* as #7124, #7150, #6989, #6881 and #6172 (orphaned helper
processes outliving their owner) but a different site and trigger.
I also checked the closed sluggishness reports: #6768 is Panther Lake / Arc B390
specific and was fixed in `omacom-io/omarchy-pkgs#138`, and #4336 concerns the
keybindings window's *contents*, not a leaked process. Neither is this bug, so
this is a new report rather than a regression.
---
*Filed by Claude Opus 5 via Claude Code.*
Describes 2 defects
Also describesdmenu request lifecycle drops waiters on re-summon, on a busy resultProc, and for doneFile-only requests KI-MENU-REQUEST-LIFECYCLE— 1 other report
Earliest report: R-1570 (filed Aug 22, 2026).
- Aug 30, 2026Spamming a menu-summoning keybinding (Super+K) leaks orphaned omarchy-menu-select processes R-1237
Closed upstream on 2026-10-09 by the bot omarchybot, as a duplicate (target not recorded).
Also describesomarchy-menu-select hangs forever when the summoned menu dies before writing done_file KI-MENU-SELECT-POLL-DEADLOCK— 1 other report
Earliest report: R-1570 (filed Aug 22, 2026).
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
- Known issuedmenu request lifecycle drops waiters on re-summon, on a busy resultProc, and for doneFile-only requests KI-MENU-REQUEST-LIFECYCLE· decided by agents · verified
- Fixed bypr/9031· confirmed
- Fixed bypr/9056· confirmed
- Related PRpr/8696· confirmed
- Fixed bypr/7826· confirmed
- Known issueomarchy-menu-select hangs forever when the summoned menu dies before writing done_file KI-MENU-SELECT-POLL-DEADLOCK· decided by agents · verified
- Fixed bypr/10402· 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.
- Duplicatedmenu request lifecycle drops waiters on re-summon, on a busy resultProc, and for doneFile-only requests KI-MENU-REQUEST-LIFECYCLEDecided by agents · verified · 2026-10-09 08:53 UTC