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

Plugin hot-reload never clears the component cache: the Qt.clearComponentCache guard is always false

Reported by pmbemax · filed Sep 7, 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 clears the component cache: the Qt.clearComponentCache guard is always falserevision 1 · captured 2026-10-04 12:10 UTC via poll

Summary

shell/shell.qml:784 guards the cache clear that makes plugin hot-reload work:

function finishPluginReload() {
  ...
  if (typeof Qt.clearComponentCache === "function") Qt.clearComponentCache()
  shell.pluginRegistry.rescan()
}

That condition is always false. clearComponentCache() is a QQmlEngine member (/usr/include/qt6/QtQml/qqmlengine.h:67) and is not exposed on the QML Qt object, so the call never runs and the engine keeps its cached component for the edited file. rescan() then re-instantiates the cached type, and the plugin keeps running its previous code while the file on disk says something else.

Measured on Qt 6.11.2 / Omarchy 4.0.2:

// /tmp/qmlprobe2.qml
import QtQuick
Item {
  Component.onCompleted: {
    var t = typeof Qt.clearComponentCache
    console.warn("PROBE typeof Qt.clearComponentCache = " + t)
    Qt.exit(t === "function" ? 0 : 3)
  }
}
$ QT_QPA_PLATFORM=offscreen qml6 /tmp/qmlprobe2.qml ; echo exit=$?
exit=3

i.e. not a function. grep -rn clearComponentCache /usr/lib/qt6/qml/QtQml/plugins.qmltypes is likewise empty — it is not in the QML type registry, only in the C++ header.

Why it matters

docs/omarchy-shell.md:113 documents the rescanPlugins IPC as "re-walk plugin dirs and hot-reload plugin code", and agents/skills/migrations.md:135 relies on the shell hot-reloading edits. In practice, editing a plugin's QML and triggering a reload leaves the old code live with no warning anywhere.

The failure mode is expensive precisely because it is silent: a plugin author edits a file, sees the old behaviour, and concludes the edit was wrong. It cost me a full day on a bar plugin, "fixing" code that was never loaded — and the only reliable signal is comparing file mtimes against the shell process start time.

Observed here as WARN scene: file:///…/Panel.qml:833: TypeError lines citing line numbers from a previous version of a 600-line file, after a reload the shell reported as successful.

Possible directions

I have not written a patch because the fix is on the C++ side and I would be guessing at Quickshell's intentions. Options as I see them, in the order I would consider them:

  1. Have Quickshell expose the engine call (an IPC or an attached property) and use it here; if Quickshell already has an equivalent, this is a one-line change to reach for it instead.
  2. If no such API exists, make the limitation loud rather than silent: log once at WARN when a local plugin changes and the cache cannot be cleared, and say in docs/omarchy-shell.md that plugin code edits need omarchy restart shell while shell.json edits hot-reload. That alone would have saved me the day.
  3. Sidestep the cache by loading plugin components through a URL that changes per reload (a cache-busting query on the component URL), if Quickshell's loader allows it.

Happy to test any of these on this machine — three monitors, a handful of first-party clones and third-party plugins, and a reproducible case.

System

Omarchy 4.0.2-1, Hyprland 0.56.2, Qt 6.11.2 (runtime and build), Arch, AMD RX 7900 XTX. shell/shell.qml as of main (c82a0837) still carries the guard at line 784.

View raw text
## Summary

`shell/shell.qml:784` guards the cache clear that makes plugin hot-reload work:

```qml
function finishPluginReload() {
  ...
  if (typeof Qt.clearComponentCache === "function") Qt.clearComponentCache()
  shell.pluginRegistry.rescan()
}
```

That condition is **always false**. `clearComponentCache()` is a `QQmlEngine` member (`/usr/include/qt6/QtQml/qqmlengine.h:67`) and is not exposed on the QML `Qt` object, so the call never runs and the engine keeps its cached component for the edited file. `rescan()` then re-instantiates the *cached* type, and the plugin keeps running its previous code while the file on disk says something else.

Measured on Qt 6.11.2 / Omarchy 4.0.2:

```qml
// /tmp/qmlprobe2.qml
import QtQuick
Item {
  Component.onCompleted: {
    var t = typeof Qt.clearComponentCache
    console.warn("PROBE typeof Qt.clearComponentCache = " + t)
    Qt.exit(t === "function" ? 0 : 3)
  }
}
```

```
$ QT_QPA_PLATFORM=offscreen qml6 /tmp/qmlprobe2.qml ; echo exit=$?
exit=3
```

i.e. not a function. `grep -rn clearComponentCache /usr/lib/qt6/qml/QtQml/plugins.qmltypes` is likewise empty — it is not in the QML type registry, only in the C++ header.

## Why it matters

`docs/omarchy-shell.md:113` documents the `rescanPlugins` IPC as *"re-walk plugin dirs and hot-reload plugin code"*, and `agents/skills/migrations.md:135` relies on the shell hot-reloading edits. In practice, editing a plugin's QML and triggering a reload leaves the old code live with no warning anywhere.

The failure mode is expensive precisely because it is silent: a plugin author edits a file, sees the old behaviour, and concludes the edit was wrong. It cost me a full day on a bar plugin, "fixing" code that was never loaded — and the only reliable signal is comparing file mtimes against the shell process start time.

Observed here as `WARN scene: file:///…/Panel.qml:833: TypeError` lines citing line numbers from a *previous* version of a 600-line file, after a reload the shell reported as successful.

## Possible directions

I have not written a patch because the fix is on the C++ side and I would be guessing at Quickshell's intentions. Options as I see them, in the order I would consider them:

1. Have Quickshell expose the engine call (an IPC or an attached property) and use it here; if Quickshell already has an equivalent, this is a one-line change to reach for it instead.
2. If no such API exists, make the limitation loud rather than silent: log once at `WARN` when a local plugin changes and the cache cannot be cleared, and say in `docs/omarchy-shell.md` that plugin **code** edits need `omarchy restart shell` while `shell.json` edits hot-reload. That alone would have saved me the day.
3. Sidestep the cache by loading plugin components through a URL that changes per reload (a cache-busting query on the component URL), if Quickshell's loader allows it.

Happy to test any of these on this machine — three monitors, a handful of first-party clones and third-party plugins, and a reproducible case.

## System

Omarchy 4.0.2-1, Hyprland 0.56.2, Qt 6.11.2 (runtime and build), Arch, AMD RX 7900 XTX. `shell/shell.qml` as of `main` (`c82a0837`) still carries the guard at line 784.

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.