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-1792GitHub issuePublicFixed

Clicking workspaces in BarWidget is sluggish vs keybinds

Reported by ohai89 · filed Aug 16, 2026 · open at source

Partly in the audited area (menu) — decided by agents · verified. Not validated 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?
AreaPartly in the audited areamenu· Decided by agents · verified
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

Clicking workspaces in BarWidget is sluggish vs keybindsrevision 1 · captured 2026-09-28 21:29 UTC via poll

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

  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():
  function run(command) {
      if (!command) return
      Util.execDetached(command)
  }
  • Util.qml's execDetached():
  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:

  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.
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.