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.
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
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
System details
- Omarchy 4.0.2-1, quickshell 0.3.1-1 (Arch,
extra/quickshell), Hyprland ≥0.56 (Lua config) - Plugin: a
bar-widgetkind plugin that declares anIpcHandler { target: "<plugin id>" }in itsBarWidget.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):
- In a plugin's bar widget add:
property string buildTag: "A"
IpcHandler { target: "acme.demo"; function probe(): string { return "build " + root.buildTag } }omarchy-restart-shell, thenqs -p /usr/share/omarchy/shell ipc call acme.demo probe→build A✅- 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. qs -p /usr/share/omarchy/shell ipc call acme.demo probe→ still `build A` ❌ (expectedbuild B).qs ipc showat this point already lists the new file's function set.- Adding a new IPC function in step 3 and calling it returns
Function not found.even thoughipc showlists 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