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-5040GitHub issuePublicReceived

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.

Received and recorded verbatim. Nothing has been checked 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?
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

Plugin hot reload never applies QML edits: Qt.clearComponentCache guard is always falserevision 1 · captured 2026-10-04 12:10 UTC via poll

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):

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:

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.