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.
Clicking workspaces in BarWidget is sluggish vs keybinds
Reported by ohai89 · filed Aug 16, 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?
- 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: partly in the audited area (menu) — decided by agents · verified; matches no known issue — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.
Original report
System details
Omarchy 4.0.0-1, Thinkpad Carbon X1 gen 13
What's wrong?
What happened
Commons/Util.qml's execDetached wraps every dispatched command in a login shell:
function execDetached(command) {
Quickshell.execDetached(["bash", "-lc", command])
}bash -lc sources /etc/profile and the user's ~/.bash_profile/~/.profile on every single invocation, on top of the fork/exec itself. Any bar interaction that goes through Bar.run() → execDetached pays this cost on every call — most noticeably Workspaces.qml's focusWorkspace, which shells out to hyprctl dispatch on every click:
function focusWorkspace(id) {
if (!root.bar) return
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
}Clicking a workspace pill in the bar is perceptibly slower than switching workspaces via a native Hyprland keybind, which is handled entirely inside the compositor with no IPC round trip and no shell involved at all.
Expected
Bar-driven Hyprland dispatches (workspace switching, and likely other Bar.run() call sites) should feel as instant as the equivalent keybind, since Quickshell already maintains a live IPC connection to Hyprland via the Quickshell.Hyprland import.
Steps to reproduce
- Fresh Omarchy 4.0.0 install, default Quickshell bar
- Switch workspaces via a keybind (e.g.
SUPER + 2) — instant - Switch workspaces by clicking a workspace pill in the bar — noticeably slower, subjectively closer to ~100ms+ than the keybind's near-zero latency
Evidence
Traced the call path from the bar widget down to the actual spawn:
Workspaces.qml→root.bar.run(...)Bar.qml'srun():
function run(command) {
if (!command) return
Util.execDetached(command)
}Util.qml'sexecDetached():
function execDetached(command) {
Quickshell.execDetached(["bash", "-lc", command])
}So every workspace click is: bar widget → bash -lc (login shell, sources profile files) → hyprctl (separate process, parses args, opens its own connection to Hyprland's IPC socket) → dispatch. The keybind path skips all of this — it's handled natively inside Hyprland.
Confirmed the fix locally by cloning the plugin (omarchy-plugin-clone omarchy.workspaces --edit) and replacing the function:
Before:
function focusWorkspace(id) {
if (!root.bar) return
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
}After:
function focusWorkspace(id) {
Hyprland.dispatch("hl.dsp.focus({ workspace = \"" + id + "\" })")
}Hyprland.dispatch() (already available via the Quickshell.Hyprland import at the top of Workspaces.qml) reuses Quickshell's existing persistent socket connection instead of spawning a shell + hyprctl process. After this change, clicking a workspace pill feels identical to using the keybind.
Suggested fix
Two independent fixes, either of which helps:
- In `Workspaces.qml`: use
Hyprland.dispatch(...)directly instead of routing throughBar.run()/execDetached, as shown above. - In `Util.qml`:
execDetacheddefaulting to a login shell (bash -lc) for arbitrary commands adds unnecessary profile-sourcing overhead to every bar-triggered command, not just Hyprland dispatches. A non-login shell (bash -c) would avoid this for the general case, though callers that specifically need Hyprland IPC are better served byHyprland.dispatch()regardless.
View raw text
### System details
Omarchy 4.0.0-1, Thinkpad Carbon X1 gen 13
### What's wrong?
## What happened
`Commons/Util.qml`'s `execDetached` wraps every dispatched command in a **login shell**:
```qml
function execDetached(command) {
Quickshell.execDetached(["bash", "-lc", command])
}
```
`bash -lc` sources `/etc/profile` and the user's `~/.bash_profile`/`~/.profile` on every single invocation, on top of the fork/exec itself. Any bar interaction that goes through `Bar.run()` → `execDetached` pays this cost on every call — most noticeably `Workspaces.qml`'s `focusWorkspace`, which shells out to `hyprctl dispatch` on every click:
```qml
function focusWorkspace(id) {
if (!root.bar) return
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
}
```
Clicking a workspace pill in the bar is perceptibly slower than switching workspaces via a native Hyprland keybind, which is handled entirely inside the compositor with no IPC round trip and no shell involved at all.
## Expected
Bar-driven Hyprland dispatches (workspace switching, and likely other `Bar.run()` call sites) should feel as instant as the equivalent keybind, since Quickshell already maintains a live IPC connection to Hyprland via the `Quickshell.Hyprland` import.
## Steps to reproduce
1. Fresh Omarchy 4.0.0 install, default Quickshell bar
2. Switch workspaces via a keybind (e.g. `SUPER + 2`) — instant
3. Switch workspaces by clicking a workspace pill in the bar — noticeably slower, subjectively closer to ~100ms+ than the keybind's near-zero latency
## Evidence
Traced the call path from the bar widget down to the actual spawn:
- `Workspaces.qml` → `root.bar.run(...)`
- `Bar.qml`'s `run()`:
```qml
function run(command) {
if (!command) return
Util.execDetached(command)
}
```
- `Util.qml`'s `execDetached()`:
```qml
function execDetached(command) {
Quickshell.execDetached(["bash", "-lc", command])
}
```
So every workspace click is: bar widget → `bash -lc` (login shell, sources profile files) → `hyprctl` (separate process, parses args, opens its own connection to Hyprland's IPC socket) → dispatch. The keybind path skips all of this — it's handled natively inside Hyprland.
Confirmed the fix locally by cloning the plugin (`omarchy-plugin-clone omarchy.workspaces --edit`) and replacing the function:
**Before:**
```qml
function focusWorkspace(id) {
if (!root.bar) return
root.bar.run("hyprctl dispatch " + Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })"))
}
```
**After:**
```qml
function focusWorkspace(id) {
Hyprland.dispatch("hl.dsp.focus({ workspace = \"" + id + "\" })")
}
```
`Hyprland.dispatch()` (already available via the `Quickshell.Hyprland` import at the top of `Workspaces.qml`) reuses Quickshell's existing persistent socket connection instead of spawning a shell + `hyprctl` process. After this change, clicking a workspace pill feels identical to using the keybind.
## Suggested fix
Two independent fixes, either of which helps:
1. **In `Workspaces.qml`:** use `Hyprland.dispatch(...)` directly instead of routing through `Bar.run()`/`execDetached`, as shown above.
2. **In `Util.qml`:** `execDetached` defaulting to a *login* shell (`bash -lc`) for arbitrary commands adds unnecessary profile-sourcing overhead to every bar-triggered command, not just Hyprland dispatches. A non-login shell (`bash -c`) would avoid this for the general case, though callers that specifically need Hyprland IPC are better served by `Hyprland.dispatch()` regardless.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/7231· confirmed
- Fixed bypr/7011· confirmed
- Related PRpr/9419· 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 agents · verified: partly in the audited area (menu)
- Decided by agents · verified: matches no known issue
The outcome (valid, duplicate, not a bug, …) is not decided yet.