What the PR does. Upstream #11851 (vdvoorder) makes Super + K run Lua function binds. Before, picking one exited 1 and ran nothing. Hyprland reports them as __lua with a registry ref. When the source replay has no expression for such a bind, the row now stores the bind's identity: mask, submap, key, keycode and description, base64-encoded (dynamic_bindings, line 322). On a pick, the script writes a mark in the Lua registry and re-reads hyprctl binds. If exactly one bind matches and its ref is numeric, it calls registry[ref], but only while the mark is still there. The picked row is now matched through ENVIRON, not awk -v. The cache key moves from v14 to v19. On the mirror that is commit e03ad1ea. Every later commit is our proof work: a reproducer fixture fix, KI status, SW-REQ-261005-4MKB, 3 test scenarios, 2 test-harness fixes, obligation tags, mcdc:ignore lines and trace reviews.
Risk rationale: A targeted fix. Its tests drive the new dispatch path through a stubbed hyprctl that runs the Lua, and they cover refusal on reload, ambiguity and bad refs. But changed-line coverage of the script was not measured, nothing ran against a live compositor, and the configured differential does not reach this script. Two compat findings (witness renames from an engine parsing artefact) prevent readiness until a reviewer decides. Rebinds that keep the shipped chord and description still run the shipped action without #7564. Advisory only.
Read the full review (5 more paragraphs)
Is the fix verified? Changed-line coverage was not measured for bin/omarchy-menu-keybindings (coverage_unmeasured), so the package has no coverage fact for it. Test report: 68 passed, 0 failed, current. The error_handling cells of 4MKB and 9DMS are observed_pass; joined via tags on the one file-level test. My stubbed run of the dispatch path calls ref 264 for one matching bind. It calls nothing for two matches, a non-numeric ref, a reload between lookup and call, or another submap.
Compatibility. compat reports 2 mcdc_diff findings: cf-4c5e34efd4c84e91 (0W96) and cf-8507c81c0f32c95a (9DMS). Both prevent readiness; only a reviewer can approve them. Each is the same witness renamed. A Lua type(action) line in the PR's test heredoc is read as a function name, so the witness keybindings-menu-test.sh:1 became type. Same file, line 1, scenarios 1-3, meets policy. An engine parsing artefact. The differential's 0 differences come from a harness that runs only Menu.qml/MenuModel.js. Readiness: not_assessed. Next decision: human_judgement (compat_changes_unapproved), by a reviewer. Nothing here is an approval.
Questions for a human. (1) Does it run under a live Hyprland? All tests are stubbed. (2) A rebind that keeps the shipped chord and description still runs the shipped action; #7564 fixes that half. (3) Should a test pin binds the replay never reaches?
Traceability. 4MKB and 0W96 match at function level, 9DMS only at file level. Change effects were not analyzed.
Not caused by this PR. No check failed; 13 were skipped by configuration.
AI-generated review opinion derived from the evidence package. It is not a verdict, does not change readiness, and must be confirmed by a human reviewer.
21 context notes (info — not a finding)
- AI · confirmedThe PR's dispatch rule is codified as SW-REQ-261005-4MKB, added by our proof work
4MKB: a picked row for a Lua function bind calls the function through the registry ref Hyprland reports, only when one reported bind, and no other, matches the row and Hyprland did not reload its Lua state after the lookup. It is new on the mirror (commit 19d55809, proof work, not upstream). Its 7 function-level matches are the functions the PR adds plus dispatch_binding. It satisfies SYS-REQ-260922-6642. The requirement is in review status, not approved.
- AI · confirmeddispatch_binding gains a
__luaarm (SW-REQ-260922-0W96)Line 735:
__lua) [[ -n $arg ]] && dispatch_lua_function_binding "$arg". Before this change a__luarow with no cached expression reached the"") return 1arm with an empty dispatcher. 0W96 says Lua binds "remain dispatchable", and before this change function binds were not. The PR closes that gap for binds whose ref Hyprland reports. - AI · confirmedSW-REQ-260922-9DMS (code:N key display) matches only through its file-level trace
9DMS is code_changed because bin/omarchy-menu-keybindings and the test file changed. In dynamic_bindings, the PR keeps the
code:$keycodederivation and stores the raw key separately (raw_key, line 301) for the identity. My reading: the displayed key for code:N binds goes through the same lines as at base. Its trace was re-reviewed on the mirror (proof work). - AI opinion — unverifiedOpinion (my own run): the dispatch path calls exactly the one matching bind and leaves no mark
On the evaluated tree I extracted the PR's functions the way its test does and drove them with its stub_hyprctl_dispatch (macOS, luajit as lua). Results: one matching bind with ref 264 gives exit 0 and calls 264. Two binds with the same identity, a non-numeric ref, a reload between lookup and call, and the same chord and description in another submap each give exit 1, call nothing, and leave no mark. The picked-row awk: two identical exec rows dispatch the first, as before. An exec row and a
__luarow that read the same give no record. A label containing\tmatches through ENVIRON but not through the base'sawk -v. In our Linux runs, keybindings-menu-test.sh passed 33/33 on the PR head and on its merge onto quattro 035ce29f, and 16/16 on quattro. These results are outside the package. - AI opinion — unverifiedOpinion (engine bounds): why a Lua line names a bash test's witness
I reproduced the engine's witness-name scan on both versions of the test file. A bash function
name() {matches none of its patterns. At base nothing matches, so the witness falls back to<file>:<line>. At the head, the first match is the generic "typed declaration" pattern (<type> <name>(), which readsif type(at line 333 as a declaration namedtype. The keyword filter checks only the captured name, notif. Both compat findings follow from this. The fix belongs in the engine (skip heredoc bodies in shell files, or use the shell function pattern), not in this PR. This is my reading of the engine, not a package fact. - AI opinion — unverifiedtest_diff: upstream adds tests only; our proof work moved the picker stub into a function
test_diff reports that no existing test file was modified or deleted. Upstream e03ad1ea adds 323 lines to keybindings-menu-test.sh and removes none. On the mirror our proof work changed existing test code: the omarchy-menu-select stub moved into use_pick_file_picker(), which run_keybindings re-installs on every run (pick_from_menu overwrites it). It also extended the Verifies header to 4MKB. No existing assertion changed.
- AI opinion — unverifiedRemoved behaviour 1:
awk -v selection=read a backslash in a label as an escapeAt base the picked row was found with
awk -v selection="$selection" '$1 == selection'. awk -v processes escape sequences, so a label containing\matched no row, for every bind kind. At the head the selection is matched through ENVIRON (line 758), which is literal. The PR states the intent: "The picked row is now matched through the environment instead ofawk -v, which reads a backslash in a description as an escape and then matches no row." The test at line 598 pins it. review.behaviorDiff.inventory is empty, so this is my inventory, not the engine's. - AI opinion — unverifiedRemoved behaviour 2: identical rows dispatched the first; now refused when one is a function bind
At base, two rows with the same text dispatched the first (
{ print; exit }). At the head (line 762) a single match is dispatched. Two or more matches are dispatched (first row) only when none of them is__lua; otherwise nothing is printed and the script exits 1 with no message. The PR states this: "When two rows read the same and one of them is a function bind, the pick is refused". Tests at lines 625 and 637 pin it. Our stubbed run of the shipped config found no two rows that read the same. - AI opinion — unverifiedSW-REQ-261005-4MKB MC/DC: 4 of 6 rows witnessed by the PR's tests, 2 marked defensive
From the audit run (mcdc_coverage passed): keybindings-menu-test.sh witnesses 4MKB rows 1, 3, 4 and 5 (meets_policy true). Our proof work added the
# MCDCmarkers (lines 393, 418, 456, 578) and the 3 new scenarios: undecodable identity, refused mark, and menu closed without a pick. The two remaining rows aremcdc:ignore:defensive(test lines 5-6, REVIEW-261005-THHB): a called function with the mark gone, and no call with one match and the mark present. Each needs the guard or the hand-off removed. These are requirement-level witnesses, not changed-line coverage. - AI opinion — unverifiedObligation cells: 4MKB and 9DMS error_handling observed_pass at file granularity; 9DMS timeout not_linked
testResults is ingested and current (68 passed, 0 failed); 1 tagged test, joined. Our proof work (97e2bbf3) added obligation tags to keybindings-menu-test.sh: 9DMS error_handling nominal (line 181) and negative (line 202), and 4MKB error_handling nominal (line 394) and negative (lines 534, 566). The junit report records the whole file as one test, so each of those cells rests on that file's single pass (tests: 1), not on the individual scenarios. 9DMS external_call_timeout_bounded is not_linked: no test is tagged with that class, so it is not shown as tested. 0W96 error_handling is suppressed by a recorded suppression in the base data. The earlier test_results_not_joined disclosure is gone at this package.
- AI · confirmedTest helper called() has no function-level trace: test plumbing (noise)
called() asserts which registry ref the stub recorded as called. It is added by the upstream PR. It has no behaviour of its own beyond the scenarios that call it, and the file's
# Verifies:header already ties those scenarios to 0W96, 9DMS and 4MKB. No requirement is missing. A function-level trace would add nothing. - AI · confirmedTest helper called_nothing() has no function-level trace: test plumbing (noise)
called_nothing() asserts the stub recorded no call. It is added by the upstream PR. It has no behaviour of its own beyond the scenarios that call it, and the file's
# Verifies:header already ties those scenarios to 0W96, 9DMS and 4MKB. No requirement is missing. A function-level trace would add nothing. - AI · confirmedTest helper left_no_mark() has no function-level trace: test plumbing (noise)
left_no_mark() asserts no mark is left in the stub's Lua state. It is added by the upstream PR. It has no behaviour of its own beyond the scenarios that call it, and the file's
# Verifies:header already ties those scenarios to 0W96, 9DMS and 4MKB. No requirement is missing. A function-level trace would add nothing. - AI · confirmedTest helper lua_function_bind() has no function-level trace: test plumbing (noise)
lua_function_bind() prints one
hyprctl bindsrecord in the 0.56.2 function-bind shape. It is added by the upstream PR. It has no behaviour of its own beyond the scenarios that call it, and the file's# Verifies:header already ties those scenarios to 0W96, 9DMS and 4MKB. No requirement is missing. A function-level trace would add nothing. - AI · confirmedTest helper pick_from_menu() has no function-level trace: test plumbing (noise)
pick_from_menu() installs a picker that returns one row and runs the real script. It is added by the upstream PR. It has no behaviour of its own beyond the scenarios that call it, and the file's
# Verifies:header already ties those scenarios to 0W96, 9DMS and 4MKB. No requirement is missing. A function-level trace would add nothing. - AI · confirmedTest helper stub_hyprctl_dispatch() has no function-level trace: test plumbing (noise)
stub_hyprctl_dispatch() installs a
hyprctlstub that runs the dispatched Lua and records calls and marks. It is added by the upstream PR. It has no behaviour of its own beyond the scenarios that call it, and the file's# Verifies:header already ties those scenarios to 0W96, 9DMS and 4MKB. No requirement is missing. A function-level trace would add nothing. - AI · confirmedTest helper use_pick_file_picker() has no function-level trace: test plumbing (noise)
use_pick_file_picker() re-installs the file-driven picker stub before each run. It is added by our proof work (it wraps the existing picker stub). It has no behaviour of its own beyond the scenarios that call it, and the file's
# Verifies:header already ties those scenarios to 0W96, 9DMS and 4MKB. No requirement is missing. A function-level trace would add nothing. - AI · confirmedThe reproducer change is our proof work, outside the verification scope
pocs/crs-260930-jvx8-c02.sh is the reproducer of KI-MENU-KEYBINDINGS-FUNC-BIND-NO-DISPATCH, not part of the upstream PR. Commit dc773390 makes its stub print
arg: 264for the Select all bind, the shape Hyprland 0.56.2 prints. With the old emptyarg:a fix that dispatches through the ref still read as broken. Its hyprctl() stub is untraced and the file is out of scope (changed_files_out_of_scope). It realizes the KI's check on 0W96, so no product requirement is missing. - AI opinion — unverifiedKI-MENU-KEYBINDINGS-FUNC-BIND-NO-DISPATCH is marked fixed on the mirror (proof work)
80236345 sets the KI's status to fixed by omacom/omarchy#11851. 49ebebcf re-captures its evidence as not reproduced. In our Linux runs the fixed reproducer exits 0 (defect present) on quattro-proof 1e433732 and exits 1 (fixed) on the PR head and its merge.
- AI opinion — unverifiedOpinion (reading): six shipped binds are affected at this base, not five
The PR text lists Zoom in, Reset zoom and the three universal clipboard actions. The base also ships
Select all(default/hypr/bindings/clipboard.lua:45,send_shortcut_once("CTRL", "A")), a Lua function bind next to them. The fix is generic, so it covers that bind too; the reproducer uses it. - AI opinion — unverifiedChange effects were not analyzed (effects_unavailable): no call-level facts
review.effects is absent for this bash change, so no new_call_path, effect_gain or new_mutable_state claim can be checked, and the scope analysis could not detect uncodified connections. From reading: the new callees are dispatch_lua_expression (existing; runs
hyprctl dispatch) andhyprctl binds,base64and awk. The only product caller of dispatch_binding is the script's own main block (line 767). These facts come from reading, not from the engine.