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 never picks up changed plugin code (stale QML component cache)
Reported by farangkao · filed Aug 15, 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
13th Gen Intel(R) Core(TM), AMD Radeon AI PRO R9700
What's wrong?
Title: Plugin hot-reload silently reloads stale code — Qt.clearComponentCache doesn't exist in QML
Note: analysis and report generated with AI (GLM 5-3), but reviewed and tested by a human in the loop (me)
Summary
When a locally installed plugin's files change (e.g. after omarchy plugin update),
the shell notices and logs Local plugin changed, reloading: <id>, and plugin
services/widgets are re-instantiated — but they are re-created from the **old
compiled QML**, so the update has no effect until the whole shell is restarted.
Environment
- Omarchy 4.0.0-1 (stable channel,
4.0.0.alpha) - quickshell 0.3.0 (quickshell-git, revision 28771c7c)
- Plugin under test:
lobo.omaspaces(any third-party bar-widget plugin shows this)
Reproduction
- Install a git-managed bar-widget plugin at an older version, with the shell running.
- Run
omarchy plugin update <id>(or just edit a file under
~/.config/omarchy/plugins/<id>/ — the file watcher triggers the same path).
3. The shell logs Local plugin changed, reloading: <id> and reloads.
4. Observe: the widget still runs the old code. Only omarchy restart shell
applies the update.
Concrete example: after updating lobo.omaspaces to a version that added a
right-click handler on its workspace pills, right-click kept doing nothing
after the automatic reload; omarchy restart shell fixed it.
A minimal proof that edits are ignored by the reload: adding a visible change
(e.g. a marker character in the widget's label) plus aComponent.onCompleted: console.warn(...) line to the plugin's QML triggered
the reloading log line, but the marker never appeared and the log line never
fired — the live instance was re-created from the previous compilation.
Root cause
shell.qml reload path: file change → localPluginReloadTimer (line 61) →reloadPlugins() (line 739) → finishPluginReload() (line 751):
if (typeof Qt.clearComponentCache === "function") Qt.clearComponentCache()Qt.clearComponentCache is a C++-only method on QQmlEngine; it is not
exposed to the QML Qt object. Verified live — a quickshell config evaluatingtypeof Qt.clearComponentCache prints undefined.
Because the guard is silently always false, the engine's in-memory component
cache is never cleared, and the subsequent Qt.createComponent(url) inloadPluginWidget() (line 794) returns the cached stale Component for
the same URL. Qt's in-memory component cache is keyed by URL and is not
mtime-invalidated — only clearComponentCache() drops it.
Quickshell's own live-reload can't cover this either: it only watches files
under its config path (-p /usr/share/omarchy/shell), while plugins live in~/.config/omarchy/plugins, outside that tree — which is why the custom
watcher exists in the first place.
The same stale-cache limitation applies to the shell rescanPlugins IPC
function: it re-runs syncPluginWidgets(), which for an unchanged entry-point
URL explicitly re-registers the existing (old) component
("If the component URL is unchanged, just refresh the metadata in place").
Expected behavior
omarchy plugin update (or any plugin file change) should take effect without
a manual shell restart — or, if that's not technically possible from QML, the
reload path should not silently pretend it succeeded.
Suggested fixes
- In
finishPluginReload(), detect that the cache cannot be cleared from QML
(typeof Qt.clearComponentCache !== "function") and fall back to a full
shell restart (the omarchy-restart-shell flow) instead of silently
reloading stale code.
2. Simpler alternative: have omarchy-plugin-update / omarchy-plugin-add
invoke omarchy-restart-shell when the shell is running.
3. Long-term: expose engine cache clearing (or a reload command) in quickshell
itself; there is currently no quickshell reload subcommand, and kill +
relaunch is the only way to get a fresh engine.
Workaround
omarchy restart shell after any plugin update. (Note: omarchy refresh shell
also restarts but resets shell.json to defaults — not what you want.)
View raw text
### System details
13th Gen Intel(R) Core(TM), AMD Radeon AI PRO R9700
### What's wrong?
**Title:** Plugin hot-reload silently reloads stale code — `Qt.clearComponentCache` doesn't exist in QML
Note: analysis and report generated with AI (GLM 5-3), but reviewed and tested by a human in the loop (me)
## Summary
When a locally installed plugin's files change (e.g. after `omarchy plugin update`),
the shell notices and logs `Local plugin changed, reloading: <id>`, and plugin
services/widgets are re-instantiated — but they are re-created from the **old
compiled QML**, so the update has no effect until the whole shell is restarted.
## Environment
- Omarchy 4.0.0-1 (stable channel, `4.0.0.alpha`)
- quickshell 0.3.0 (quickshell-git, revision 28771c7c)
- Plugin under test: `lobo.omaspaces` (any third-party bar-widget plugin shows this)
## Reproduction
1. Install a git-managed bar-widget plugin at an older version, with the shell running.
2. Run `omarchy plugin update <id>` (or just edit a file under
`~/.config/omarchy/plugins/<id>/` — the file watcher triggers the same path).
3. The shell logs `Local plugin changed, reloading: <id>` and reloads.
4. Observe: the widget still runs the old code. Only `omarchy restart shell`
applies the update.
Concrete example: after updating `lobo.omaspaces` to a version that added a
right-click handler on its workspace pills, right-click kept doing nothing
after the automatic reload; `omarchy restart shell` fixed it.
A minimal proof that edits are ignored by the reload: adding a visible change
(e.g. a marker character in the widget's label) plus a
`Component.onCompleted: console.warn(...)` line to the plugin's QML triggered
the `reloading` log line, but the marker never appeared and the log line never
fired — the live instance was re-created from the previous compilation.
## Root cause
`shell.qml` reload path: file change → `localPluginReloadTimer` (line 61) →
`reloadPlugins()` (line 739) → `finishPluginReload()` (line 751):
```qml
if (typeof Qt.clearComponentCache === "function") Qt.clearComponentCache()
```
`Qt.clearComponentCache` is a C++-only method on `QQmlEngine`; it is not
exposed to the QML `Qt` object. Verified live — a quickshell config evaluating
`typeof Qt.clearComponentCache` prints `undefined`.
Because the guard is silently always false, the engine's in-memory component
cache is never cleared, and the subsequent `Qt.createComponent(url)` in
`loadPluginWidget()` (line 794) returns the cached **stale** `Component` for
the same URL. Qt's in-memory component cache is keyed by URL and is not
mtime-invalidated — only `clearComponentCache()` drops it.
Quickshell's own live-reload can't cover this either: it only watches files
under its config path (`-p /usr/share/omarchy/shell`), while plugins live in
`~/.config/omarchy/plugins`, outside that tree — which is why the custom
watcher exists in the first place.
The same stale-cache limitation applies to the `shell rescanPlugins` IPC
function: it re-runs `syncPluginWidgets()`, which for an unchanged entry-point
URL explicitly re-registers the existing (old) component
("If the component URL is unchanged, just refresh the metadata in place").
## Expected behavior
`omarchy plugin update` (or any plugin file change) should take effect without
a manual shell restart — or, if that's not technically possible from QML, the
reload path should not silently pretend it succeeded.
## Suggested fixes
1. In `finishPluginReload()`, detect that the cache cannot be cleared from QML
(`typeof Qt.clearComponentCache !== "function"`) and fall back to a full
shell restart (the `omarchy-restart-shell` flow) instead of silently
reloading stale code.
2. Simpler alternative: have `omarchy-plugin-update` / `omarchy-plugin-add`
invoke `omarchy-restart-shell` when the shell is running.
3. Long-term: expose engine cache clearing (or a reload command) in quickshell
itself; there is currently no `quickshell reload` subcommand, and `kill` +
relaunch is the only way to get a fresh engine.
## Workaround
`omarchy restart shell` after any plugin update. (Note: `omarchy refresh shell`
also restarts but resets shell.json to defaults — not what you want.)
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 04:10 UTC