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-494GitHub issuePublicDecided· Duplicate

Third-party (cloned) menu plugin: Apps submenu stays empty — shell never grants appLibrary

Reported by Hangoverdk · filed Sep 16, 2026 · open at source

Decided: Duplicate — decided by agents · verified 3 days ago.

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-494 (filed Sep 16, 2026).

Decided by agents · verified· Oct 8, 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?
AreaIn the audited areamenu· Decided by agents · verified
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

Third-party (cloned) menu plugin: Apps submenu stays empty — shell never grants appLibraryrevision 1 · captured 2026-09-28 21:23 UTC via poll

System details

AMD Ryzen 8C/16T, AMD RX 9070 (Navi 48, amdgpu), Omarchy 4.0.4-1, kernel 7.2.5-3-omarchy, quickshell 0.3.1-1, Hyprland.

Full omarchy-debug --no-sudo --print log: /tmp/omarchy-debug.log

What's wrong?

When you clone the first-party menu plugin (omarchy plugin clone omarchy.menu -> e.g. jan.menu), disable omarchy.menu, and add the clone to the bar, the Apps submenu is empty even though .desktop entries exist.

Steps to reproduce:
1. omarchy plugin clone omarchy.menu (produces a third-party plugin like jan.menu)
2. Add the clone as a bar widget, and put omarchy.menu in disabledPlugins in ~/.config/omarchy/shell.json
3. omarchy restart shell
4. Press SUPER+SPACE -> Omarchy menu -> Apps submenu shows no rows.

Expected: installed applications are listed (I have 95 .desktop files on this system).

First-party omarchy.menu is NOT affected - only clones. Only the Apps submenu breaks; other menu sections (System, Power, etc.) work fine.

Root cause

The Apps rows come from the shell's shared AppLibrary proxy, which is only granted when the shell detects the plugin has the menu kind:

  • shell/shell.qml:349
function manifestHasKind(manifest, kind) {
  return !!manifest && Array.isArray(manifest.kinds)
    && manifest.kinds.indexOf(kind) !== -1
}
  • shell/shell.qml:603 (inside createScopedPluginShell):
appLibrary: shell.manifestHasKind(manifest, "menu")
  ? shell.pluginAppLibraryFor(cacheKey, key) : null,

For third-party menu plugins, the manifest that reaches createScopedPluginShell (via the panel loader's Instantiator model) carries kinds as an array-like backed by QJsonArray rather than a real JS Array. Array.isArray(manifest.kinds) is therefore false, so manifestHasKind returns false and:

  1. appLibrary is set to null, and mergeAppRows() in the menu's Menu.qml bails out immediately (if (!root.appLibrary) return) -> empty Apps submenu.
  2. The capability profile flips to "no-menu" (shell/shell.qml:529), which revokes and re-creates the plugin shell API chip on menu capability.

The QML menu code hands the appLibrary down via root.shell.appLibrary, so it silently degrades to an empty list.

Workaround

Patch manifestHasKind to accept array-like kinds (this resolves the issue locally):

function manifestHasKind(manifest, kind) {
  if (!manifest) return false
  var kinds = manifest.kinds
  if (Array.isArray(kinds)) return kinds.indexOf(kind) !== -1
  if (!kinds || typeof kinds.length !== "number" || Number(kinds.length) === 0) return false
  var count = Number(kinds.length)
  for (var i = 0; i < count; i++) if (String(kinds[i]) === kind) return true
  return false
}

Since I edited the package-owned file, this is overwritten on omarchy update - so a real upstream fix would be great. It would be even better to normalize the manifest shape once at the plugin-registry boundary so kinds is always a real JS Array (e.g. Array.prototype.slice.call) rather than teaching manifestHasKind about array-likes.

View raw text
## System details

AMD Ryzen 8C/16T, AMD RX 9070 (Navi 48, amdgpu), Omarchy 4.0.4-1, kernel 7.2.5-3-omarchy, quickshell 0.3.1-1, Hyprland.

Full `omarchy-debug --no-sudo --print` log: `/tmp/omarchy-debug.log`

## What's wrong?

When you clone the first-party menu plugin (`omarchy plugin clone omarchy.menu` -> e.g. `jan.menu`), disable `omarchy.menu`, and add the clone to the bar, the **Apps submenu is empty** even though `.desktop` entries exist.

Steps to reproduce:
1. `omarchy plugin clone omarchy.menu` (produces a third-party plugin like `jan.menu`)
2. Add the clone as a bar widget, and put `omarchy.menu` in `disabledPlugins` in `~/.config/omarchy/shell.json`
3. `omarchy restart shell`
4. Press SUPER+SPACE -> Omarchy menu -> **Apps** submenu shows no rows.

Expected: installed applications are listed (I have 95 `.desktop` files on this system).

First-party `omarchy.menu` is NOT affected - only clones. Only the Apps submenu breaks; other menu sections (System, Power, etc.) work fine.

### Root cause

The Apps rows come from the shell's shared `AppLibrary` proxy, which is only granted when the shell detects the plugin has the `menu` kind:

- `shell/shell.qml:349`

```qml
function manifestHasKind(manifest, kind) {
  return !!manifest && Array.isArray(manifest.kinds)
    && manifest.kinds.indexOf(kind) !== -1
}
```

- `shell/shell.qml:603` (inside `createScopedPluginShell`):

```qml
appLibrary: shell.manifestHasKind(manifest, "menu")
  ? shell.pluginAppLibraryFor(cacheKey, key) : null,
```

For **third-party menu plugins**, the manifest that reaches `createScopedPluginShell` (via the panel loader's Instantiator model) carries `kinds` as an array-like backed by `QJsonArray` rather than a real JS Array. `Array.isArray(manifest.kinds)` is therefore `false`, so `manifestHasKind` returns `false` and:

1. `appLibrary` is set to `null`, and `mergeAppRows()` in the menu's `Menu.qml` bails out immediately (`if (!root.appLibrary) return`) -> empty Apps submenu.
2. The capability profile flips to `"no-menu"` (`shell/shell.qml:529`), which revokes and re-creates the plugin shell API chip on menu capability.

The QML menu code hands the `appLibrary` down via `root.shell.appLibrary`, so it silently degrades to an empty list.

### Workaround

Patch `manifestHasKind` to accept array-like `kinds` (this resolves the issue locally):

```qml
function manifestHasKind(manifest, kind) {
  if (!manifest) return false
  var kinds = manifest.kinds
  if (Array.isArray(kinds)) return kinds.indexOf(kind) !== -1
  if (!kinds || typeof kinds.length !== "number" || Number(kinds.length) === 0) return false
  var count = Number(kinds.length)
  for (var i = 0; i < count; i++) if (String(kinds[i]) === kind) return true
  return false
}
```

Since I edited the package-owned file, this is overwritten on `omarchy update` - so a real upstream fix would be great. It would be even better to normalize the manifest shape once at the plugin-registry boundary so `kinds` is always a real JS Array (e.g. `Array.prototype.slice.call`) rather than teaching `manifestHasKind` about array-likes.

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

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.

  • DuplicateR-5209Decided by agents · verified · 2026-10-08 22:13 UTC