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 applies QML edits: Qt.clearComponentCache guard is always false
Reported by diogo7dias · filed Sep 12, 2026Closed upstream on 2026-09-20 by bjarneo via an automated message, as a duplicate of #6981 (R-1835). GitHub close reason: completed.
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?
- Not yet mapped to any requirement or component.
- 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. No decision has been recorded yet.
Original report
What happens
Editing a local plugin's QML (~/.config/omarchy/plugins/<id>/Panel.qml, Service.qml, ...) logs
DEBUG qml: Local plugin changed, reloading: io.github.diogo7dias.omaclackbut the shell keeps running the QML it compiled earlier. The edit only appears after omarchy restart shell, so plugin hot reload never actually applies a change.
Why
shell.qml, finishPluginReload (line 1459 in 4.0.3-1, unchanged on master as of 31bd80d):
if (typeof Qt.clearComponentCache === "function") Qt.clearComponentCache()
shell.pluginRegistry.rescan()QML's Qt object has no clearComponentCache — it is a QQmlEngine C++ method, not part of the QML global. The condition is therefore always false and the call never runs, so rescan() re-registers the plugin and the engine hands back the types it already compiled from the same file URLs.
Repro
- Install any local bar-widget plugin under
~/.config/omarchy/plugins/<id>/. - Open its panel.
- Change a visible string in its
Panel.qmland save. - The shell logs
Local plugin changed, reloading: <id>; reopen the panel — the old text is still there. omarchy restart shell— the new text appears.
A fix, and its catch
Replacing the local-plugin path with a soft engine reload does work; I ran it here for a while and edits showed up in well under a second, with windows preserved:
Timer {
id: localPluginReloadTimer
interval: 150
onTriggered: Quickshell.reload(false)
}The catch is blast radius. The watcher fires for any file written inside a plugin directory, not just QML, so plugins that write there at runtime would reload the entire shell repeatedly. That is not hypothetical: a plugin of mine ships a Python helper, and merely importing it creates bin/__pycache__/*.pyc inside the plugin directory, which triggered a full reload every time.
Narrowing it would avoid that — reload only when the changed path ends in .qml, or only under an opt-in dev flag, and keep the cheap registry path for enable/disable. I did not want to pick that design in a drive-by PR, but I am happy to send a patch in whichever shape you prefer.
Environment
omarchy 4.0.3-1, Quickshell 0.3.1, Arch Linux, Hyprland.
View raw text
### What happens
Editing a local plugin's QML (`~/.config/omarchy/plugins/<id>/Panel.qml`, `Service.qml`, ...) logs
```
DEBUG qml: Local plugin changed, reloading: io.github.diogo7dias.omaclack
```
but the shell keeps running the QML it compiled earlier. The edit only appears after `omarchy restart shell`, so plugin hot reload never actually applies a change.
### Why
`shell.qml`, `finishPluginReload` (line 1459 in 4.0.3-1, unchanged on master as of 31bd80d):
```qml
if (typeof Qt.clearComponentCache === "function") Qt.clearComponentCache()
shell.pluginRegistry.rescan()
```
QML's `Qt` object has no `clearComponentCache` — it is a `QQmlEngine` C++ method, not part of the QML global. The condition is therefore always false and the call never runs, so `rescan()` re-registers the plugin and the engine hands back the types it already compiled from the same file URLs.
### Repro
1. Install any local bar-widget plugin under `~/.config/omarchy/plugins/<id>/`.
2. Open its panel.
3. Change a visible string in its `Panel.qml` and save.
4. The shell logs `Local plugin changed, reloading: <id>`; reopen the panel — the old text is still there.
5. `omarchy restart shell` — the new text appears.
### A fix, and its catch
Replacing the local-plugin path with a soft engine reload does work; I ran it here for a while and edits showed up in well under a second, with windows preserved:
```qml
Timer {
id: localPluginReloadTimer
interval: 150
onTriggered: Quickshell.reload(false)
}
```
The catch is blast radius. The watcher fires for *any* file written inside a plugin directory, not just QML, so plugins that write there at runtime would reload the entire shell repeatedly. That is not hypothetical: a plugin of mine ships a Python helper, and merely importing it creates `bin/__pycache__/*.pyc` inside the plugin directory, which triggered a full reload every time.
Narrowing it would avoid that — reload only when the changed path ends in `.qml`, or only under an opt-in dev flag, and keep the cheap registry path for enable/disable. I did not want to pick that design in a drive-by PR, but I am happy to send a patch in whichever shape you prefer.
### Environment
omarchy 4.0.3-1, Quickshell 0.3.1, Arch Linux, Hyprland.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.
No decision has been recorded yet.