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.
Third-party menu-kind plugin gets shell.appLibrary === null, leaving Apps submenu empty
Reported by gbflores · filed Sep 17, 2026Closed upstream on 2026-10-12 by the bot omarchybot, as a duplicate (target not recorded).
This defect is tracked under R-5209. This report describes the same defect, so it is folded under it in the inbox.
Earliest report of this defect: R-394 (filed Sep 17, 2026).
Decided by agents · verified· Oct 9, 2026
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?
- 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
What happened
The Apps submenu of a third-party/cloned "menu" kind plugin (io.github.surfacedp.xmb-menu, a public clone of the built-in omarchy.menu) rendered completely empty — no rows, no icons — no matter how it was opened (bar click, omarchy menu summon apps, after omarchy plugin update, after a full omarchy restart shell).
Switching the same install temporarily back to the built-in omarchy.menu (omarchy plugin disable io.github.surfacedp.xmb-menu && omarchy plugin enable omarchy.menu) showed every installed app correctly, Discord included. So the desktop entries, icons, and DesktopEntries scanning are all fine — the difference is specific to how a non-first-party "menu" kind plugin gets its shell.appLibrary wired up.
Root cause
I added a temporary console.warn inside the plugin's mergeAppRows() (which mirrors /usr/share/omarchy/shell/plugins/menu/Menu.qml's own mergeAppRows() almost verbatim) and got:
XMBDEBUG mergeAppRows called, appLibrary= null shell= PluginShellApi_QMLTYPE_54(0x...)So root.shell is a valid PluginShellApi instance, but root.shell.appLibrary is null.
Tracing through /usr/share/omarchy/shell/shell.qml:
pluginShellFor(manifest)(line 739):if (!manifest || manifest.__isFirstParty) return shell— first-party plugins get the realshellsingleton directly, soshell.appLibraryis always the liveAppLibraryinstance for them. Everything else falls through tocreateScopedPluginShell(manifest, key, true, shell.pluginHasBarCapabilities(manifest)).createScopedPluginShell(...)(line 569) builds the plugin-scoped API object with (line 603):
appLibrary: shell.manifestHasKind(manifest, "menu")
? shell.pluginAppLibraryFor(cacheKey, key) : null,manifestHasKind(manifest, kind)(line 349) is!!manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf(kind) !== -1.pluginAppLibraryFor(cacheKey, pluginId)(line 423) creates/caches aPluginAppLibraryApi(services/PluginAppLibraryApi.qml) that proxies_sortedEntries/_entryName/_iconSource/_launch/_removeback to the realshell.appLibrary.
The plugin's manifest (manifest.json) does declare "kinds": ["menu", "bar-widget"], so manifestHasKind(manifest, "menu") should be true and pluginAppLibraryFor should return a working proxy — but empirically root.shell.appLibrary comes back null on this install. I could not instrument pluginAppLibraryFor/manifestHasKind directly without editing packaged files under /usr/share/omarchy, so I can't pin down which of the two conditions is failing, but the net effect is: any third-party "menu" kind plugin can silently get `appLibrary: null`, and any code that (correctly, defensively) does if (!root.appLibrary) return before populating its Apps list ends up rendering nothing, with no warning anywhere in journalctl or the quickshell log.
Expected behavior
A third-party clone of omarchy.menu that declares "kinds": ["menu", ...] in its manifest should receive a working shell.appLibrary proxy, the same way the built-in menu gets the real one — or, if it can legitimately be null in some state (e.g. before some service is ready), the plugin-facing API should make that a documented, retryable condition (e.g. an appLibrary property that emits a change signal once it becomes available) rather than a silent, permanent null.
Steps to reproduce
omarchy plugin clone omarchy.menu(or install any third-party plugin declaring"kinds": ["menu", ...]in itsmanifest.json) so it is no longer first-party.- Give it an Apps submenu the same way
plugins/menu/Menu.qmldoes: guard onroot.shell.appLibrary, callroot.appLibrary.sortedEntries("")when opening the "apps" node. omarchy restart shell, open the plugin's menu, navigate into Apps.- Apps submenu is empty. Add a
console.warn("appLibrary:", root.shell && root.shell.appLibrary)to confirm it logsnull.
System info
omarchy version: 4.0.4-1quickshell: 0.3.1-1hyprland: 0.56.2-2- CPU: AMD Ryzen 9 5900XT
- GPU: AMD Radeon RX 580 (Polaris 20 XL)
- Kernel: 7.2.5-3-omarchy
(omarchy debug --no-sudo --print is not a recognized command on 4.0.4 — omarchy commands --all has no debug entry — so I could not attach that log; happy to run whatever the current equivalent is if pointed to it.)
Workaround shipped in the affected plugin
I opened surfacedP/omarchy-xmb-menu#3, which works around this by reading Quickshell.DesktopEntries directly (falling back off root.appLibrary) whenever the host hands back null. That restores the Apps submenu for that specific plugin, but any other third-party menu-kind plugin following the same first-party pattern would hit the same silent empty list.
View raw text
## What happened
The Apps submenu of a third-party/cloned "menu" kind plugin (`io.github.surfacedp.xmb-menu`, a public clone of the built-in `omarchy.menu`) rendered completely empty — no rows, no icons — no matter how it was opened (bar click, `omarchy menu summon apps`, after `omarchy plugin update`, after a full `omarchy restart shell`).
Switching the same install temporarily back to the built-in `omarchy.menu` (`omarchy plugin disable io.github.surfacedp.xmb-menu && omarchy plugin enable omarchy.menu`) showed every installed app correctly, Discord included. So the desktop entries, icons, and DesktopEntries scanning are all fine — the difference is specific to how a **non-first-party** "menu" kind plugin gets its `shell.appLibrary` wired up.
## Root cause
I added a temporary `console.warn` inside the plugin's `mergeAppRows()` (which mirrors `/usr/share/omarchy/shell/plugins/menu/Menu.qml`'s own `mergeAppRows()` almost verbatim) and got:
```
XMBDEBUG mergeAppRows called, appLibrary= null shell= PluginShellApi_QMLTYPE_54(0x...)
```
So `root.shell` is a valid `PluginShellApi` instance, but `root.shell.appLibrary` is `null`.
Tracing through `/usr/share/omarchy/shell/shell.qml`:
- `pluginShellFor(manifest)` (line 739): `if (!manifest || manifest.__isFirstParty) return shell` — first-party plugins get the real `shell` singleton directly, so `shell.appLibrary` is always the live `AppLibrary` instance for them. Everything else falls through to `createScopedPluginShell(manifest, key, true, shell.pluginHasBarCapabilities(manifest))`.
- `createScopedPluginShell(...)` (line 569) builds the plugin-scoped API object with (line 603):
```qml
appLibrary: shell.manifestHasKind(manifest, "menu")
? shell.pluginAppLibraryFor(cacheKey, key) : null,
```
- `manifestHasKind(manifest, kind)` (line 349) is `!!manifest && Array.isArray(manifest.kinds) && manifest.kinds.indexOf(kind) !== -1`.
- `pluginAppLibraryFor(cacheKey, pluginId)` (line 423) creates/caches a `PluginAppLibraryApi` (`services/PluginAppLibraryApi.qml`) that proxies `_sortedEntries`/`_entryName`/`_iconSource`/`_launch`/`_remove` back to the real `shell.appLibrary`.
The plugin's manifest (`manifest.json`) does declare `"kinds": ["menu", "bar-widget"]`, so `manifestHasKind(manifest, "menu")` should be `true` and `pluginAppLibraryFor` should return a working proxy — but empirically `root.shell.appLibrary` comes back `null` on this install. I could not instrument `pluginAppLibraryFor`/`manifestHasKind` directly without editing packaged files under `/usr/share/omarchy`, so I can't pin down which of the two conditions is failing, but the net effect is: **any third-party "menu" kind plugin can silently get `appLibrary: null`**, and any code that (correctly, defensively) does `if (!root.appLibrary) return` before populating its Apps list ends up rendering nothing, with no warning anywhere in `journalctl` or the quickshell log.
## Expected behavior
A third-party clone of `omarchy.menu` that declares `"kinds": ["menu", ...]` in its manifest should receive a working `shell.appLibrary` proxy, the same way the built-in menu gets the real one — or, if it can legitimately be null in some state (e.g. before some service is ready), the plugin-facing API should make that a documented, retryable condition (e.g. an `appLibrary` property that emits a change signal once it becomes available) rather than a silent, permanent `null`.
## Steps to reproduce
1. `omarchy plugin clone omarchy.menu` (or install any third-party plugin declaring `"kinds": ["menu", ...]` in its `manifest.json`) so it is no longer first-party.
2. Give it an Apps submenu the same way `plugins/menu/Menu.qml` does: guard on `root.shell.appLibrary`, call `root.appLibrary.sortedEntries("")` when opening the "apps" node.
3. `omarchy restart shell`, open the plugin's menu, navigate into Apps.
4. Apps submenu is empty. Add a `console.warn("appLibrary:", root.shell && root.shell.appLibrary)` to confirm it logs `null`.
## System info
- `omarchy version`: 4.0.4-1
- `quickshell`: 0.3.1-1
- `hyprland`: 0.56.2-2
- CPU: AMD Ryzen 9 5900XT
- GPU: AMD Radeon RX 580 (Polaris 20 XL)
- Kernel: 7.2.5-3-omarchy
(`omarchy debug --no-sudo --print` is not a recognized command on 4.0.4 — `omarchy commands --all` has no `debug` entry — so I could not attach that log; happy to run whatever the current equivalent is if pointed to it.)
## Workaround shipped in the affected plugin
I opened surfacedP/omarchy-xmb-menu#3, which works around this by reading `Quickshell.DesktopEntries` directly (falling back off `root.appLibrary`) whenever the host hands back `null`. That restores the Apps submenu for that specific plugin, but any other third-party menu-kind plugin following the same first-party pattern would hit the same silent empty list.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
- Duplicate ofR-5209Cloned menu gets null appLibrary — manifestHasKind Array.isArray fails on QVariantList kinds· decided by agents · verified
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.