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.
Cloned menu plugin loses its appLibrary capability on plugin re-instantiation — Apps submenu is always empty
Reported by Dazzle-sys · filed Sep 9, 2026Closed upstream on 2026-09-20 by bjarneo via an automated message, as a duplicate of #10661 (R-860). 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
Title: Cloned menu plugin loses its appLibrary capability on plugin re-instantiation — Apps submenu is always empty
System
- Omarchy 4.0.3-1 (Quattro), omarchy-shell on Quickshell 0.3.1
- Arch Linux, kernel 7.2.3-arch1-3, Hyprland 0.56.2, Wayland, NVIDIA RTX 3070 Mobile
- 92 desktop entries installed; built-in menu lists them all
Context
Found while using the third-party Chinese localization project
omarchy-zh-cn, which ships its
translated menu as a omarchy plugin clone-style clone. The bug reproduces
with a pristine clone (see "Reproducer"), so it is not related to that
project's translations.
What happened
After switching the menu from the built-in plugin to a clone, the menu opens
normally and all static entries work, but the Apps submenu is empty
(Super+Space → Apps, and the apps-only menu on Super+Alt+Space); searching
an app name returns nothing. The built-in omarchy.menu lists all ~60
applications in the same session.
Other cloned bar-widget plugins (clock, audio, bluetooth, network, …) keep
working, so this appears to affect only menu-kind clones.
What I expected
The cloned menu should list desktop applications exactly like the built-in
menu, per the documented clone behavior ("Calls made to the original built-in
id get routed to your clone").
Reproducer
- On Omarchy 4.0.3-1, switch the menu to a clone (`omarchy plugin clone
omarchy.menu, or replace the built-in Menu.qml` with a byte-identical copy
inside a clone — both verified).
2. In the verified reproducer the clone's Menu.qml was byte-identical to
/usr/share/omarchy/shell/plugins/menu/Menu.qml; manifest.json kept
kinds: ["menu","bar-widget"] with the same entry points.
3. Press Super+Space, open Apps (or Super+Alt+Space).
4. The submenu is empty; searching an app name returns nothing.
5. Switching back to the built-in omarchy.menu (same session, same config)
restores the full app list.
Plugin state in the broken session
(omarchy-shell shell listPlugins, excerpt): omarchy.menu disabled,
clone illli.menu enabled with kinds: ["menu","bar-widget"].
Diagnosis (instrumented copy of shell.qml)
In the live Menu.qml instance the app list is empty becausemergeAppRows() bails out: appLibrary
(readonly property var appLibrary: root.shell ? root.shell.appLibrary : null)
is null. Two distinct end states were observed:
- Observed A (instrumented test instance): the clone's scoped shell is
replaced by one whose appLibrary is null.
- Observed B (regular shell after `omarchy restart shell`): the recreated
panel instance's root.shell is null outright — the item.shell
assignment in the panel Loader.onLoaded evidently produced null.
Tracing pluginShellFor() shows the scoped shell for the clone is built
multiple times with different results:
- First instantiation wave (panel Loader at startup): called with the
real manifest (kinds: ["menu","bar-widget"]);
manifestHasKind(manifest, "menu") is true, so
createScopedPluginShell() attaches a working PluginAppLibraryApi.
2. Second wave — timing coincides with the overlay plugins loading and the
panel Instantiator rebuilding its delegates: pluginShellFor() is called
again for the clone. Its log of the manifest still shows
kinds: ["menu","bar-widget"], but inside createScopedPluginShell()
manifestHasKind(manifest, "menu") now returns false. That yields a
different capability profile (…|no-menu vs …|menu), so
revokePluginShellApi() destroys the good cached scoped shell and recreates
it with appLibrary: null.
Verbatim instrumented log (test instance; illli.menu = the clone):
TESTSHELL pluginShellFor id: illli.menu firstParty: false kinds: ["menu","bar-widget"]
TESTSHELL createScopedPluginShell key: illli.menu hasMenuKind: true kinds: ["menu","bar-widget"] cacheKey: illli.menu allowOwnService: true
TESTSHELL pluginShellFor result null? false
… (bar widgets instantiate) …
TESTSHELL pluginShellFor id: illli.menu firstParty: false kinds: ["menu","bar-widget"]
TESTSHELL createScopedPluginShell key: illli.menu hasMenuKind: false kinds: ["menu","bar-widget"] cacheKey: illli.menu allowOwnService: true
TESTSHELL pluginShellFor result null? false
…
ZHDEBUG mergeAppRows appLibrary: false shell: object shellAppLibrary: no-shell shellNull: trueOpen question: within a single call, pluginShellFor()'s log andcreateScopedPluginShell()'s log print different verdicts for what should be
the same manifest object (kinds array still stringifies as["menu","bar-widget"], yet manifestHasKind(manifest, "menu") is false). I
could not pin down which intermediate object loses the menu kind between the
waves — only that the effect is deterministic and specific to theappLibrary wiring of menu-kind clones.
Upstream status
Compared against quattro HEAD shell/shell.qml (fetched 2026-09-09):pluginShellFor() is byte-identical, andappLibrary: shell.manifestHasKind(manifest, "menu") insidecreateScopedPluginShell() is unchanged, so the bug is very likely still
present in the latest Quattro code.
Workaround (verified on this machine)
Switch the bar layout / enabled state back to the built-in omarchy.menu.
The user menu extension (~/.config/omarchy/extensions/omarchy-menu.jsonc)
still applies to the built-in menu, so a translated clone can keep its
localized labels this way. Alternatively, a clone could fall back to its ownDesktopEntries-based app list when shell.appLibrary is null.
View raw text
**Title:** Cloned menu plugin loses its `appLibrary` capability on plugin re-instantiation — Apps submenu is always empty
## System
- Omarchy 4.0.3-1 (Quattro), omarchy-shell on Quickshell 0.3.1
- Arch Linux, kernel 7.2.3-arch1-3, Hyprland 0.56.2, Wayland, NVIDIA RTX 3070 Mobile
- 92 desktop entries installed; built-in menu lists them all
## Context
Found while using the third-party Chinese localization project
[omarchy-zh-cn](https://github.com/QueedWen/omarchy-zh-cn), which ships its
translated menu as a `omarchy plugin clone`-style clone. The bug reproduces
with a **pristine** clone (see "Reproducer"), so it is not related to that
project's translations.
## What happened
After switching the menu from the built-in plugin to a clone, the menu opens
normally and all static entries work, but the **Apps submenu is empty**
(`Super+Space` → Apps, and the apps-only menu on `Super+Alt+Space`); searching
an app name returns nothing. The built-in `omarchy.menu` lists all ~60
applications in the same session.
Other cloned bar-widget plugins (clock, audio, bluetooth, network, …) keep
working, so this appears to affect only **menu-kind clones**.
## What I expected
The cloned menu should list desktop applications exactly like the built-in
menu, per the documented clone behavior ("Calls made to the original built-in
id get routed to your clone").
## Reproducer
1. On Omarchy 4.0.3-1, switch the menu to a clone (`omarchy plugin clone
omarchy.menu`, or replace the built-in `Menu.qml` with a byte-identical copy
inside a clone — both verified).
2. In the verified reproducer the clone's `Menu.qml` was **byte-identical** to
`/usr/share/omarchy/shell/plugins/menu/Menu.qml`; `manifest.json` kept
`kinds: ["menu","bar-widget"]` with the same entry points.
3. Press `Super+Space`, open **Apps** (or `Super+Alt+Space`).
4. The submenu is empty; searching an app name returns nothing.
5. Switching back to the built-in `omarchy.menu` (same session, same config)
restores the full app list.
Plugin state in the broken session
(`omarchy-shell shell listPlugins`, excerpt): `omarchy.menu` disabled,
clone `illli.menu` enabled with `kinds: ["menu","bar-widget"]`.
## Diagnosis (instrumented copy of `shell.qml`)
In the live `Menu.qml` instance the app list is empty because
`mergeAppRows()` bails out: `appLibrary`
(`readonly property var appLibrary: root.shell ? root.shell.appLibrary : null`)
is null. Two distinct end states were observed:
- **Observed A (instrumented test instance):** the clone's scoped shell is
replaced by one whose `appLibrary` is null.
- **Observed B (regular shell after `omarchy restart shell`):** the recreated
panel instance's `root.shell` is `null` outright — the `item.shell`
assignment in the panel `Loader.onLoaded` evidently produced `null`.
Tracing `pluginShellFor()` shows the scoped shell for the clone is built
multiple times with different results:
1. **First instantiation wave** (panel Loader at startup): called with the
real manifest (`kinds: ["menu","bar-widget"]`);
`manifestHasKind(manifest, "menu")` is **true**, so
`createScopedPluginShell()` attaches a working `PluginAppLibraryApi`.
2. **Second wave** — timing coincides with the overlay plugins loading and the
panel `Instantiator` rebuilding its delegates: `pluginShellFor()` is called
again for the clone. Its log of the manifest still shows
`kinds: ["menu","bar-widget"]`, but inside `createScopedPluginShell()`
`manifestHasKind(manifest, "menu")` now returns **false**. That yields a
different capability profile (`…|no-menu` vs `…|menu`), so
`revokePluginShellApi()` destroys the good cached scoped shell and recreates
it with `appLibrary: null`.
Verbatim instrumented log (test instance; `illli.menu` = the clone):
```
TESTSHELL pluginShellFor id: illli.menu firstParty: false kinds: ["menu","bar-widget"]
TESTSHELL createScopedPluginShell key: illli.menu hasMenuKind: true kinds: ["menu","bar-widget"] cacheKey: illli.menu allowOwnService: true
TESTSHELL pluginShellFor result null? false
… (bar widgets instantiate) …
TESTSHELL pluginShellFor id: illli.menu firstParty: false kinds: ["menu","bar-widget"]
TESTSHELL createScopedPluginShell key: illli.menu hasMenuKind: false kinds: ["menu","bar-widget"] cacheKey: illli.menu allowOwnService: true
TESTSHELL pluginShellFor result null? false
…
ZHDEBUG mergeAppRows appLibrary: false shell: object shellAppLibrary: no-shell shellNull: true
```
Open question: within a single call, `pluginShellFor()`'s log and
`createScopedPluginShell()`'s log print different verdicts for what should be
the same manifest object (`kinds` array still stringifies as
`["menu","bar-widget"]`, yet `manifestHasKind(manifest, "menu")` is false). I
could not pin down which intermediate object loses the `menu` kind between the
waves — only that the effect is deterministic and specific to the
`appLibrary` wiring of menu-kind clones.
## Upstream status
Compared against `quattro` HEAD `shell/shell.qml` (fetched 2026-09-09):
`pluginShellFor()` is byte-identical, and
`appLibrary: shell.manifestHasKind(manifest, "menu")` inside
`createScopedPluginShell()` is unchanged, so the bug is very likely still
present in the latest Quattro code.
## Workaround (verified on this machine)
Switch the bar layout / enabled state back to the built-in `omarchy.menu`.
The user menu extension (`~/.config/omarchy/extensions/omarchy-menu.jsonc`)
still applies to the built-in menu, so a translated clone can keep its
localized labels this way. Alternatively, a clone could fall back to its own
`DesktopEntries`-based app list when `shell.appLibrary` is null.
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.