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-2134GitHub issuePublicFixed

Menu: Left/Backspace from a submenu always returns to the first row, discarding the parent selection (8 open PRs, no issue)

Reported by rkondratowicz · filed Sep 30, 2026 · open at source

In the audited area (menu) — decided by agents · verified. Not validated 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?
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?
Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. So far: in the audited area (menu) — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.

Original report

Menu: Left/Backspace from a submenu always returns to the first row, discarding the parent selection (8 open PRs, no issue)revision 1 · captured 2026-09-30 18:36 UTC via poll

System details

AMD Ryzen 9 7940HS (integrated Radeon 780M, no discrete GPU), Omarchy 4.0.4-1, kernel 7.2.5-3-omarchy, Hyprland, stable channel.

Reproduces on stock Omarchy with no custom plugins and an unmodified ~/.config/omarchy/extensions/omarchy-menu.jsonc. Not hardware- or theme-specific — the cause is a single unconditional assignment in Menu.qml, so it should affect every user on 4.0.x.

What's wrong?

In the menu, descending into a submenu with Right and then returning with Left (or Backspace on an empty filter) always places the cursor on the first row, discarding the parent row you entered from. The menu effectively forgets where you were.

Steps to reproduce

  1. Press SUPER+SPACE to open the menu.
  2. Scroll to a category that is not at the top — e.g. Style.
  3. Press Right to enter it.
  4. Press Left to go back.

Actual: the cursor jumps to Apps (row 0).
Expected: the cursor is back on Style (row 3), the row you entered from.

Also affects the shipped direct-submenu bindings

The same reset happens when the menu is opened straight into a submenu, because openExistingMenu() starts with an empty navStack:

  • omarchy menu summon system, then Left → lands on Apps instead of System.
  • SUPER+ESCAPE (System) or SUPER+CTRL+C (Capture), then Left → same reset.

And after a search result, where the saved row is not a direct child of the rebuilt parent:

  • Type position at root, open Style > Menu Bar > Position, then Left → lands on Apps, because root is rebuilt unfiltered and no row matches the saved id.

This is why I am reporting it as a bug rather than a feature request: Left/Backspace are implemented as navigation-back, and they do navigate back. What is broken is that they unconditionally discard the cursor position, including for the direct-submenu bindings Omarchy ships. There is no configuration to change this — I checked omarchy-menu.jsonc, shell.json, and shell.toml.

Root cause

shell/plugins/menu/Menu.qml, setActiveMenu():

function setActiveMenu(id, pushHistory, fromPointer) {
  ...
  root.activeMenu = id
  root.filterText = ""
  root.selectedIndex = 0     // <-- unconditional
  ...
}

goBack() calls setActiveMenu() on every path, so the cursor is always reset:

function goBack() {
  if (root.activeMenu === "root") return false
  if (root.navStack.length > 0) {
    var previous = root.navStack[root.navStack.length - 1]
    ...
    root.setActiveMenu(previous, false)
    return true
  }
  var active = root.item(root.activeMenu)
  root.setActiveMenu((active && active.parent) ? active.parent : "root", false)
  return true
}

The root problem is that navStack (property var navStack: []) stores only menu ids. It has no room to record which row was selected, so there is nothing to restore — hence the hardcoded reset.

Verified not fixed on the default branch either. On quattro (as of today) setActiveMenu() still contains root.selectedIndex = 0, so a fix is needed regardless of which release this lands in.

Related work — this is blocked behind duplication, not behind a decision

I want to be explicit that this issue already has implementations and no owner. There are currently eight open PRs for exactly this behaviour, and three more were auto-closed as duplicates. None have been merged. All touch only shell/plugins/menu/Menu.qml (plus MenuModel.js / test/shell.d/menu-test.sh):

PRApproach
#7074restore by numeric index
#7954store index alongside navStack history
#11577fix nav history resetting cursor index
#12098restore by stable item ID, index clamp fallback
#12644falls back to nearest visible ancestor; covers the direct-submenu paths
#12759also restores each level's filterText; makes Left always navigate back
#13012restore by item ID so the cursor follows reordering
#13225restore by item ID, skip non-selectable rows

On 2026-09-21 an automated duplication check closed #7657, #7800 and #8322 as "similar to #7074", keeping #7074 open as the survivor. #7074 has not been merged since. For the record, #7657 was the most thoroughly validated of the three before closure: it was reviewed on a live VM (test/shell.d/menu-test.sh, 124 passed, plus five adjacent suites) and had the direct-submenu gap from #11409's reporter folded into it.

The approaches are not identical, which is likely why consolidation has stalled: restoring by numeric index (#7074) breaks when rows change or when a search result is not a direct child of the rebuilt parent, while restoring by item id (#12098, #13012, #13225) survives that but needs a fallback when the saved row is hidden or no longer selectable. #12644 additionally covers the direct-submenu and deep-search cases listed above. A single fix likely needs the item-id restore plus a nearest-visible-ancestor fallback.

What would help

  1. Pick a canonical PR (or say which approach should win) so the other seven can be closed in one go.
  2. The enhancement label on most of these reads wrong to me — losing your place on Back is a navigation defect, and it affects shipped bindings. Relabelling as bug would also match the behaviour being reported here.
  3. Do not close the menu plugin's `appLibrary` wiring — I checked whether patching locally via omarchy plugin clone omarchy.menu was a viable workaround and it is not, on 4.0.4-1: cloned menu plugins get a null scoped appLibrary, which empties the Apps submenu (#12061, #12989, #12944, #12325). Mentioning it only so nobody spends time on that workaround while this is open.

Happy to test a consolidated fix on my machine and report back, and I can provide a screen recording of the A/B if that is useful.

View raw text
## System details

AMD Ryzen 9 7940HS (integrated Radeon 780M, no discrete GPU), Omarchy 4.0.4-1, kernel 7.2.5-3-omarchy, Hyprland, stable channel.

Reproduces on stock Omarchy with no custom plugins and an unmodified `~/.config/omarchy/extensions/omarchy-menu.jsonc`. Not hardware- or theme-specific — the cause is a single unconditional assignment in `Menu.qml`, so it should affect every user on 4.0.x.

## What's wrong?

In the menu, descending into a submenu with `Right` and then returning with `Left` (or `Backspace` on an empty filter) always places the cursor on the **first row**, discarding the parent row you entered from. The menu effectively forgets where you were.

### Steps to reproduce

1. Press `SUPER+SPACE` to open the menu.
2. Scroll to a category that is not at the top — e.g. `Style`.
3. Press `Right` to enter it.
4. Press `Left` to go back.

Actual: the cursor jumps to `Apps` (row 0).
Expected: the cursor is back on `Style` (row 3), the row you entered from.

### Also affects the shipped direct-submenu bindings

The same reset happens when the menu is opened straight into a submenu, because `openExistingMenu()` starts with an empty `navStack`:

- `omarchy menu summon system`, then `Left` → lands on `Apps` instead of `System`.
- `SUPER+ESCAPE` (System) or `SUPER+CTRL+C` (Capture), then `Left` → same reset.

And after a search result, where the saved row is not a direct child of the rebuilt parent:

- Type `position` at root, open `Style > Menu Bar > Position`, then `Left` → lands on `Apps`, because `root` is rebuilt unfiltered and no row matches the saved id.

This is why I am reporting it as a bug rather than a feature request: `Left`/`Backspace` *are* implemented as navigation-back, and they do navigate back. What is broken is that they unconditionally discard the cursor position, including for the direct-submenu bindings Omarchy ships. There is no configuration to change this — I checked `omarchy-menu.jsonc`, `shell.json`, and `shell.toml`.

## Root cause

`shell/plugins/menu/Menu.qml`, `setActiveMenu()`:

```qml
function setActiveMenu(id, pushHistory, fromPointer) {
  ...
  root.activeMenu = id
  root.filterText = ""
  root.selectedIndex = 0     // <-- unconditional
  ...
}
```

`goBack()` calls `setActiveMenu()` on every path, so the cursor is always reset:

```qml
function goBack() {
  if (root.activeMenu === "root") return false
  if (root.navStack.length > 0) {
    var previous = root.navStack[root.navStack.length - 1]
    ...
    root.setActiveMenu(previous, false)
    return true
  }
  var active = root.item(root.activeMenu)
  root.setActiveMenu((active && active.parent) ? active.parent : "root", false)
  return true
}
```

The root problem is that `navStack` (`property var navStack: []`) stores **only menu ids**. It has no room to record which row was selected, so there is nothing to restore — hence the hardcoded reset.

Verified not fixed on the default branch either. On `quattro` (as of today) `setActiveMenu()` still contains `root.selectedIndex = 0`, so a fix is needed regardless of which release this lands in.

## Related work — this is blocked behind duplication, not behind a decision

I want to be explicit that **this issue already has implementations and no owner**. There are currently **eight open PRs** for exactly this behaviour, and three more were auto-closed as duplicates. None have been merged. All touch only `shell/plugins/menu/Menu.qml` (plus `MenuModel.js` / `test/shell.d/menu-test.sh`):

| PR | Approach |
|---|---|
| [#7074](https://github.com/omacom/omarchy/pull/7074) | restore by numeric index |
| [#7954](https://github.com/omacom/omarchy/pull/7954) | store index alongside `navStack` history |
| [#11577](https://github.com/omacom/omarchy/pull/11577) | fix nav history resetting cursor index |
| [#12098](https://github.com/omacom/omarchy/pull/12098) | restore by stable item ID, index clamp fallback |
| [#12644](https://github.com/omacom/omarchy/pull/12644) | falls back to nearest visible ancestor; covers the direct-submenu paths |
| [#12759](https://github.com/omacom/omarchy/pull/12759) | also restores each level's `filterText`; makes `Left` always navigate back |
| [#13012](https://github.com/omacom/omarchy/pull/13012) | restore by item ID so the cursor follows reordering |
| [#13225](https://github.com/omacom/omarchy/pull/13225) | restore by item ID, skip non-selectable rows |

On 2026-09-21 an automated duplication check closed [#7657](https://github.com/omacom/omarchy/pull/7657), [#7800](https://github.com/omacom/omarchy/pull/7800) and [#8322](https://github.com/omacom/omarchy/pull/8322) as "similar to #7074", keeping #7074 open as the survivor. #7074 has not been merged since. For the record, #7657 was the most thoroughly validated of the three before closure: it was reviewed on a live VM (`test/shell.d/menu-test.sh`, 124 passed, plus five adjacent suites) and had the direct-submenu gap from #11409's reporter folded into it.

The approaches are not identical, which is likely why consolidation has stalled: restoring by **numeric index** (#7074) breaks when rows change or when a search result is not a direct child of the rebuilt parent, while restoring by **item id** (#12098, #13012, #13225) survives that but needs a fallback when the saved row is hidden or no longer selectable. #12644 additionally covers the direct-submenu and deep-search cases listed above. A single fix likely needs the item-id restore *plus* a nearest-visible-ancestor fallback.

## What would help

1. **Pick a canonical PR** (or say which approach should win) so the other seven can be closed in one go.
2. The `enhancement` label on most of these reads wrong to me — losing your place on Back is a navigation defect, and it affects shipped bindings. Relabelling as `bug` would also match the behaviour being reported here.
3. **Do not close the menu plugin's `appLibrary` wiring** — I checked whether patching locally via `omarchy plugin clone omarchy.menu` was a viable workaround and it is not, on 4.0.4-1: cloned menu plugins get a null scoped `appLibrary`, which empties the Apps submenu (#12061, #12989, #12944, #12325). Mentioning it only so nobody spends time on that workaround while this is open.

Happy to test a consolidated fix on my machine and report back, and I can provide a screen recording of the A/B if that is useful.

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

  • Fixed bypr/11577· confirmed
  • Fixed bypr/12644· confirmed
  • Related PRpr/12759· confirmed
  • Related PRpr/7954· confirmed
  • Related PRpr/7074· confirmed
  • Related PRpr/13012· confirmed
  • Related PRpr/13225· confirmed

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.

  • Decided by agents · verified: in the audited area (menu)

The outcome (valid, duplicate, not a bug, …) is not decided yet.