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-1129GitHub issuePublicDecided· Out of scope

Plugin hot-reload: qs ipc calls keep hitting the destroyed bar-widget instance (stale IpcHandler) until omarchy-restart-shell

Reported by nixfred · filed Sep 1, 2026 · open at source

Decided: Out of scope — decided by agents · verified 3 days ago.

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?
AreaOutside the audited area· 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?
Decided by agents and verified by a second, independent agent — see the decision.

Original report

Plugin hot-reload: qs ipc calls keep hitting the destroyed bar-widget instance (stale IpcHandler) until omarchy-restart-shellrevision 1 · captured 2026-09-28 21:24 UTC via poll

System details

  • Omarchy 4.0.2-1, quickshell 0.3.1-1 (Arch, extra/quickshell), Hyprland ≥0.56 (Lua config)
  • Plugin: a bar-widget kind plugin that declares an IpcHandler { target: "<plugin id>" } in its BarWidget.qml

What's wrong?

After a local plugin hot-reload (any file under ~/.config/omarchy/plugins/<id>/ changes → shell.qml logs Local plugin changed, reloading: <id> → reloadPlugins()), the bar widget is destroyed and recreated correctly (its Component.onDestruction / onCompleted fire), but `qs ipc call <target> …` keeps being served by the OLD, destroyed widget instance. qs ipc show lists the new instance's functions; qs ipc call runs the old object's code with its old state. Its child objects behave as dead (a Process never spawns on running = true), so anything the IPC does through them silently no-ops. Only omarchy-restart-shell clears it. Since omarchy-plugin-update writes files, every plugin update leaves that plugin's IPC on a zombie until the next shell restart.

Minimal repro (no plugin-specific code needed):

  1. In a plugin's bar widget add:
   property string buildTag: "A"
   IpcHandler { target: "acme.demo"; function probe(): string { return "build " + root.buildTag } }
  1. omarchy-restart-shell, then qs -p /usr/share/omarchy/shell ipc call acme.demo probe → build A ✅
  2. Edit the file in place: sed -i 's/buildTag: "A"/buildTag: "B"/' ~/.config/omarchy/plugins/acme.demo/BarWidget.qml, wait for the reload log line.
  3. qs -p /usr/share/omarchy/shell ipc call acme.demo probe → still `build A` ❌ (expected build B). qs ipc show at this point already lists the new file's function set.
  4. Adding a new IPC function in step 3 and calling it returns Function not found. even though ipc show lists it.

Tried: setting enabled = false on the IpcHandler in Component.onDestruction — no effect (the old handler still answers). Verified across three reloads in a row (A→B→C: the answer stays at the instance alive when the shell started).

Plugin authors currently have to avoid IPC-driven keybinds entirely (I moved mine to hyprctl clients -j + hl.dsp.focus/hl.dsp.window.close, IPC only to create the window). Happy to test a fix — plugin: https://github.com/nixfred/blip (see CLAUDE.md, "After an Omarchy plugin HOT-RELOAD…").

View raw text
### System details

- Omarchy 4.0.2-1, quickshell 0.3.1-1 (Arch, `extra/quickshell`), Hyprland ≥0.56 (Lua config)
- Plugin: a `bar-widget` kind plugin that declares an `IpcHandler { target: "<plugin id>" }` in its `BarWidget.qml`

### What's wrong?

After a **local plugin hot-reload** (any file under `~/.config/omarchy/plugins/<id>/` changes → `shell.qml` logs `Local plugin changed, reloading: <id>` → `reloadPlugins()`), the bar widget is destroyed and recreated correctly (its `Component.onDestruction` / `onCompleted` fire), **but `qs ipc call <target> …` keeps being served by the OLD, destroyed widget instance.** `qs ipc show` lists the new instance's functions; `qs ipc call` runs the old object's code with its old state. Its child objects behave as dead (a `Process` never spawns on `running = true`), so anything the IPC does through them silently no-ops. Only `omarchy-restart-shell` clears it. Since `omarchy-plugin-update` writes files, every plugin update leaves that plugin's IPC on a zombie until the next shell restart.

Minimal repro (no plugin-specific code needed):

1. In a plugin's bar widget add:
   ```qml
   property string buildTag: "A"
   IpcHandler { target: "acme.demo"; function probe(): string { return "build " + root.buildTag } }
   ```
2. `omarchy-restart-shell`, then `qs -p /usr/share/omarchy/shell ipc call acme.demo probe` → `build A` ✅
3. Edit the file in place: `sed -i 's/buildTag: "A"/buildTag: "B"/' ~/.config/omarchy/plugins/acme.demo/BarWidget.qml`, wait for the reload log line.
4. `qs -p /usr/share/omarchy/shell ipc call acme.demo probe` → **still `build A`** ❌ (expected `build B`). `qs ipc show` at this point already lists the new file's function set.
5. Adding a new IPC function in step 3 and calling it returns `Function not found.` even though `ipc show` lists it.

Tried: setting `enabled = false` on the `IpcHandler` in `Component.onDestruction` — no effect (the old handler still answers). Verified across three reloads in a row (A→B→C: the answer stays at the instance alive when the shell started).

Plugin authors currently have to avoid IPC-driven keybinds entirely (I moved mine to `hyprctl clients -j` + `hl.dsp.focus`/`hl.dsp.window.close`, IPC only to create the window). Happy to test a fix — plugin: https://github.com/nixfred/blip (see `CLAUDE.md`, "After an Omarchy plugin HOT-RELOAD…").

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.

  • Out of scopeDecided by agents · verified · 2026-10-09 10:22 UTC