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

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.

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

Cloned menu plugin loses its appLibrary capability on plugin re-instantiation — Apps submenu is always emptyrevision 1 · captured 2026-10-04 12:10 UTC via poll

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

  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.

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.