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

A user's own binds in ~/.config/hypr/bindings.lua never reach the keybindings menu's Lua scan

Reported by linyiru · filed Sep 29, 2026Closed upstream on 2026-09-29 by its own reporter linyiru, as a duplicate of #13276. GitHub close reason: duplicate.

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

A user's own binds in ~/.config/hypr/bindings.lua never reach the keybindings menu's Lua scanrevision 1 · captured 2026-09-29 22:16 UTC via poll

What happens

omarchy-menu-keybindings keeps its own Lua scan of the user's config, because Hyprland reports every Lua bind as dispatcher __lua with no arg and no id. The scan dies partway through the shipped config, and the pcall wrapped around it discards the error, so nothing surfaces.

The result is that no bind in `~/.config/hypr/bindings.lua` is ever scanned. A user's own binds get no dispatcher recovery and no id in the keybindings menu.

Why

Two things line up:

  1. default/hypr/omarchy.lua:18 is require("default.hypr.qconsole"). Under the scan's noop stub, hl.get_workspace returns a stub table, so in default/hypr/qconsole.lua:123 the mon.scale > 0 test compares a table with a number and throws.
  2. config/hypr/hyprland.lua requires default.hypr.omarchy at line 14 and hypr.bindings at line 21. The throw at step 1 unwinds past line 21.

The 229 default binds registered before line 18 do survive, which is why the menu looks fine on a stock install and why this has gone unnoticed.

Reproduce

# extract the scan from the menu, verbatim
sed -n "/^    lua <<'LUA'\$/,/^LUA\$/p" bin/omarchy-menu-keybindings | sed '1d;$d' >/tmp/scan.lua

# a stock config plus one personal bind, where the comments in the shipped
# bindings.lua tell you to put it
mkdir -p /tmp/h/.config && cp -r config/hypr /tmp/h/.config/hypr
echo 'o.bind("SUPER + ALT + P", "My own bind", "true")' >>/tmp/h/.config/hypr/bindings.lua

env -i PATH="$PATH" HOME=/tmp/h OMARCHY_PATH="$PWD" DEBUG=1 lua /tmp/scan.lua
[DEBUG] lua bind scan failed: .../default/hypr/qconsole.lua:123: attempt to compare number with table

229 lines on stdout, and My own bind is not one of them. Move require("default.hypr.helpers") and require("hypr.bindings") ahead of require("default.hypr.omarchy") in hyprland.lua and it becomes 230 lines with My own bind present, which confirms the load order is what is doing it.

Why it matters beyond the id

A user who renames or translates a bind description in ~/.config/hypr/bindings.lua gets no id for it, so the menu falls back to the description. Since the menu's row pairing and its sort order both key on a name, a renamed bind loses the row it shared and the tier it belonged to. Measured on linyiru:menu-sort-by-bind-id (the follow-up to #12557, which moves those two decisions onto the id), with two chords declaring the same id = "Close window" and a translated description:

# both binds in ~/.config/hypr/bindings.lua, shipped load order
SUPER ALT + Q                       → 關閉視窗
SUPER ALT + W                       → 關閉視窗

# same two binds, load order changed so the scan reaches them
SUPER ALT + W / SUPER ALT + Q       → 關閉視窗

So this gates the bind-id work: the ids are threaded correctly, and a personal config still cannot use them, because the scan never reads the file they are declared in.

Scope

Pre-existing and unrelated to bind ids. I am reporting rather than patching because the fix is a judgement call I would rather not make alone: either the scan's stub grows enough shape for qconsole.lua to survive it, or the scan stops depending on the shipped config loading cleanly, or pcall stops being allowed to fail silently. Happy to implement whichever you prefer.

Verified 2026-09-29 on Arch, Lua 5.5.1, at 647b4a21.

🤖 Generated with Claude Code

View raw text
### What happens

`omarchy-menu-keybindings` keeps its own Lua scan of the user's config, because Hyprland reports every Lua bind as dispatcher `__lua` with no arg and no id. The scan dies partway through the *shipped* config, and the `pcall` wrapped around it discards the error, so nothing surfaces.

The result is that **no bind in `~/.config/hypr/bindings.lua` is ever scanned**. A user's own binds get no dispatcher recovery and no id in the keybindings menu.

### Why

Two things line up:

1. `default/hypr/omarchy.lua:18` is `require("default.hypr.qconsole")`. Under the scan's `noop` stub, `hl.get_workspace` returns a stub table, so in `default/hypr/qconsole.lua:123` the `mon.scale > 0` test compares a table with a number and throws.
2. `config/hypr/hyprland.lua` requires `default.hypr.omarchy` at line 14 and `hypr.bindings` at line 21. The throw at step 1 unwinds past line 21.

The 229 default binds registered before line 18 do survive, which is why the menu looks fine on a stock install and why this has gone unnoticed.

### Reproduce

```bash
# extract the scan from the menu, verbatim
sed -n "/^    lua <<'LUA'\$/,/^LUA\$/p" bin/omarchy-menu-keybindings | sed '1d;$d' >/tmp/scan.lua

# a stock config plus one personal bind, where the comments in the shipped
# bindings.lua tell you to put it
mkdir -p /tmp/h/.config && cp -r config/hypr /tmp/h/.config/hypr
echo 'o.bind("SUPER + ALT + P", "My own bind", "true")' >>/tmp/h/.config/hypr/bindings.lua

env -i PATH="$PATH" HOME=/tmp/h OMARCHY_PATH="$PWD" DEBUG=1 lua /tmp/scan.lua
```

```
[DEBUG] lua bind scan failed: .../default/hypr/qconsole.lua:123: attempt to compare number with table
```

229 lines on stdout, and `My own bind` is not one of them. Move `require("default.hypr.helpers")` and `require("hypr.bindings")` ahead of `require("default.hypr.omarchy")` in `hyprland.lua` and it becomes 230 lines with `My own bind` present, which confirms the load order is what is doing it.

### Why it matters beyond the id

A user who renames or translates a bind description in `~/.config/hypr/bindings.lua` gets no id for it, so the menu falls back to the description. Since the menu's row pairing and its sort order both key on a name, a renamed bind loses the row it shared and the tier it belonged to. Measured on `linyiru:menu-sort-by-bind-id` (the follow-up to #12557, which moves those two decisions onto the id), with two chords declaring the same `id = "Close window"` and a translated description:

```
# both binds in ~/.config/hypr/bindings.lua, shipped load order
SUPER ALT + Q                       → 關閉視窗
SUPER ALT + W                       → 關閉視窗

# same two binds, load order changed so the scan reaches them
SUPER ALT + W / SUPER ALT + Q       → 關閉視窗
```

So this gates the bind-id work: the ids are threaded correctly, and a personal config still cannot use them, because the scan never reads the file they are declared in.

### Scope

Pre-existing and unrelated to bind ids. I am reporting rather than patching because the fix is a judgement call I would rather not make alone: either the scan's stub grows enough shape for `qconsole.lua` to survive it, or the scan stops depending on the shipped config loading cleanly, or `pcall` stops being allowed to fail silently. Happy to implement whichever you prefer.

Verified 2026-09-29 on Arch, Lua 5.5.1, at `647b4a21`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

  • Related PRpr/11851· confirmed
  • Fixed bypr/7564· confirmed
  • Fixed bypr/13276· confirmed
  • Fixed bypr/11733· confirmed
  • Related PRpr/12557· 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.

No decision has been recorded yet.