Repository navigation
Conversation
Automated AI review
Verified: the defect is real and this change fixes the reported case. On the base, a cache built under Two cases still serve a stale order, because the key hashes locale names rather than the collation The key can still match while sort's collation changes
In both cases the served and fresh lists hold the same 227 rows, with 119 of them at different positions. The base behaves the same way, so neither case is a regression. They are gaps in the fix. Impact: narrow. Case A needs an Suggested change: force the collation on the sort that creates the dependency,
The C order keeps each modifier set together, for example all
|
Most cheatsheet rows tie on both sort keys, so their order comes from sort's last-resort whole-line comparison, and that comparison follows LC_COLLATE. 120 of the 226 rendered rows land at a different index under en_US.UTF-8 than under C, the largest move being 70 places.
02d29f7 to
dcefff8
Compare
|
Taking the suggested change. The branch now pins the sort and touches the cache key not at all, and I have retitled the PR and rewritten the description to match. Both corrections in the review were right, and I reproduced them rather than take them on trust. The size of the effect. On
So the description's "locale-independent outright" was wrong about Cases A and B. I did not reproduce these two individually, because pinning the sort makes both moot rather than narrower: there is no longer a cached order that depends on a locale. Worth saying that they are the reason the approach changed and not just an edge case I accepted, since a key that hashes locale names can only ever approximate the collation One thing I can add from outside the diff, since it is about where this sits rather than what it does. The cache-key approach was pushing against two open changes, which I had not found when I opened this. #8140 reports that an input to The one thing a reviewer still has to decide is the one the review framed correctly: this makes |
|
Reviewed at The bug is real. I rendered the menu from the worker's live Hyprland binds (229 binds, 224 rows), with a fresh cache for each run. On the base script, 118 of the 224 rows sit at a different position under Open: the cache version needs to move ( Minor: the equality check at Second opinion: Codex Medium reviewed the diff. It agreed that the problem is real and the change proportionate, and it agreed on the cache finding, which my brief had already pointed it at, so that agreement is not independent. It found the empty-output gap in the test on its own. I checked both against the source. Waiting on the author for the cache version bump. The maintainer also has a presentation call to make: en_US users will see the |
The check renders the two SLASH rows under C and under en_US and compares the renders, but nothing said the rows were in them. A hyprctl that reports no binds still leaves the two rows static_bindings prints on its own, and those tie the same way under either collation, so the comparison holds and the assertion passes with the rows it is about gone. There is no empty render to notice instead, for the same reason: those two always print. Assert both renders carry both rows before comparing, and keep each render in a variable rather than rendering four times to compare and report.
The records are sorted before they are cached, and the cache key hashes the version literal, the active keymap and `hyprctl binds` -- none of which moves when this script's sort does. So an install that already has a cache keeps serving the order it was written with, and since en_US.UTF-8 is the default locale that is most installs: the pinned order would arrive later, at some unrelated moment when a bind or the keymap changed. Measured over one cache directory and the two chords that swap, the new script printed the old script's en_US order byte for byte. Bump the version so that cache is retired, and plant a record under the key the previous version would have written, so that changing the order without a bump fails here rather than in a user's menu. The literal skips to v18 because v15 is taken by omacom#7564 and omacom#8229, v16 by omacom#13888 and v17 by omacom#12557, all open: two branches setting that line to the same value merge with no conflict and leave no bump at all. omacom#13521 bumped v13 to v14 for a records change the same way.
Both files skip a locale-pinning check when the second collation is not generated, and it is the same question asked of the same `locale -a`. test/shell.d/theme-install-guards-test.sh already allows the hyphenated spelling, so a box that prints en_US.utf-8 runs that check and silently skips this one.
|
Both findings reproduced, both fixed, three commits on top of The cacheYou are right, and the mechanism is worse than a missed invalidation: the key hashes the version literal, the active keymap and
So the bump is the whole difference between the fix reaching an install and not reaching it, and it leaves nothing behind:
On the number: The coordination is a smell and you named it. If you want, I will open a separate PR that derives the version from the script rather than from a hand-kept literal, so that any change to how records are built retires the cache without anyone having to remember. The cost is a refresh on every update that touches this file: 7 ms cached against 36 ms uncached here, and 36 is a floor rather than a desktop figure, since this run stubs The testAlso right, and the reason is sharper than two empty renders: there is no empty render to notice.
SuitesSame box, same day, each side in its own worktree, head
Across 286 files the per-file The presentation callThat one is the maintainer's, so here is the reasoning rather than a recommendation. Two things bear on it and neither is about taste. @dhh has already made this call once in this repo. And Thank youThis is more review than a one-line diff usually gets. You rendered 229 live binds with a fresh cache per run instead of taking the diff's word for it, you ran my new assertion with the fix reverted, and the cache finding is the one thing here that cannot be seen in the diff at all: it needed somebody to write a cache, change the script underneath it and look again. I did not catch it. I also want to name the part that is easy to read past. You wrote that Codex agreed about the cache because your brief had already pointed it there, so that agreement was not independent, and you said which finding it reached on its own. Reviewers do not often mark their own evidence down, and it is why I could tell which parts of the review to go and check and which to simply trust. Still based on 🤖 Generated with Claude Code |
|
|
Read the three commits on top of From reading alone, the commits respond to both open findings from the last review. Waiting on a review run with a second opinion, which will run the stale-cache and collation assertions on a worker. Nothing is needed from the author. The presentation call from last time (en_US users seeing the |
|
Nothing needed here, and both time-sensitive halves of your read still hold at One precision, and it is on my list rather than on your reading of it. The four-PR picture came from my comment, and it is partial. The whole setEnumerated over every open PR's changed paths rather than by keyword: 32 open PRs touch
Your conclusion holds: The method had a hole worth naming, since it is the same class of defect as the one this PR fixes. My first pass read 100 changed paths per PR, and 14 open PRs change more than that, so it quietly missed any of them that touch this file. Two do: #6105 and #13993, both Where I overstated itI wrote that two branches setting that line to the same value "merge with no conflict and leave no bump at all". The second half is right, the first is not, and the gap between them is the interesting part. Merging every pair of those seven
It is outside every time. Both sides write the byte-identical line, so git takes it silently and sends the resolver to an unrelated hunk with the version question never raised. So the failure mode is not "the merge is clean", which it usually is not. It is that this is the one line in the file a conflict will never put in front of anyone. All 21 pairs end at I have not audited what each of those seven does to the records, so I am not saying any of them is wrong, and none of those authors can see the other six numbers. That is the point rather than a complaint: the scheme asks seven unrelated people to coordinate an integer through a file none of them shares. The offer, unchangedThat is the argument for deriving the version from the script rather than keeping the literal by hand, which I offered last time and am still not opening unasked. Measured cost is 7 ms cached against 36 ms uncached, the 36 being a floor. If a maintainer would rather the number stopped coming up, say so and it goes up as its own PR against That call is still yours. Thank you for saying outright that the run stopped unverified, which model was up, and that the cap rather than the code is why. Half of what I checked above I only checked because you marked that boundary instead of rounding it off. 🤖 Generated with Claude Code |
|
Pinning keybindings sort collation is real (locale-dependent row order is a footgun). Currently |
prioritize_entriesends withsort -k1,1n -k2,2(bin/omarchy-menu-keybindings:504), and that sort decides the order of every row in the keybindings cheatsheet. Key 1 is the numeric priority. Key 2 is the second blank-separated field, which works out to the leading modifier token, so it takes 23 values across the shipped binds:SUPER,SHIFT,CTRL,ALT,PRINTand the 18 bareXF86*keys. Tapping the pipeline where the sort reads it, 174 of the 226 rows tie with at least one other row on both keys, and the largest single tie group is 75 rows. Every one of those ties is settled by sort's last-resort whole-line comparison, and that comparison followsLC_COLLATE.So the cheatsheet is in a different order depending on the locale the menu happens to run under.
Measured
On
e332dc97, coreutils 9.11 and glibc 2.44, feeding the real script the binds the shipped Lua config registers, with a fresh cache per run so nothing is served from the previous locale:SUPER + SLASH → Monitor scaling upis row 104 underCand row 174 underen_US.UTF-8. Three more rows move 42 places, all nineSUPER CTRL ALTrows move as a block by 22, and 106 rows stay where they were.The mechanism is that
en_UScollation ignores spaces and+at the primary level, soSUPER + SLASHis weighed asSUPERSLASHagainstSUPER ALT + SLASHasSUPERALTSLASHand loses to it, while underCthe+(0x2B) sorts belowA(0x41) and everySUPER + <key>row stays ahead of theSUPER ALTones. Stripping the punctuation by hand reproduces theen_USorder exactly:In other words
Ckeeps a modifier set together anden_USinterleaves the sets.The change
Name the collation on the one sort that has this dependency:
LC_ALL=C sort -k1,1n -k2,2 |That is locale-independent by construction, and it is the pattern the repo already uses where output has to be stable (
bin/omarchy-theme-color:294).What users see
C/C.UTF-8en_US.UTF-8CSo this is a visible change for anyone on a non-
Ccollation, and worth a look before merging. Two things argue it is the right order to land on: it keeps each modifier set contiguous instead of interleaving them, and it is the order the repository's own ordering assertions already check, because the test harness runs the menu underenv -i(test/shell.d/keybindings-menu-test.sh:44).Tests
Two assertions in
test/shell.d/keybindings-menu-test.sh. The first renders the same two binds underCand underen_US.UTF-8and requires the same order; it fails on the base script, whereSUPER + SLASHis row 3 under one collation and row 4 under the other. Since it can only do real work where a second collation is generated, it is skipped rather than passed silently whenen_US.UTF-8is missing, and a second assertion pins the mechanism on every box../test/cliand./test/shellwere run on this branch and onquattroon the same machine. 8 assertions fail, the same 8 on each side and no others, all of them checking system state a plain checkout does not have.keybindings-menu-test.shitself goes 16/16 to 18/18.This replaces the approach the PR was opened with
Worth stating plainly, because the thread above argues about the other one. This PR originally left the sort alone and added the collation to the records cache key instead, on the reasoning that it preserved the order
en_USusers see today. @llstrk's review found two holes in that, both of which I reproduced:sortfalls back to byte comparison whensetlocalefails or the named locale is not generated, and neither of those changesLC_ALL,LC_COLLATEorLANG, so the key matches while the order does not. Pinning the sort closes both by construction.The description also carried two errors of mine, corrected above. It reported 31 rows moving by up to 4, which was measured on the declared binds rather than on the rendered rows; the user-visible figure is 120 of 226 by up to 70. And it said adding
-sto the sort would make the order locale-independent outright, which is too strong:-sdrops only the last-resort comparison, while-k2,2is still compared withstrcoll. For the shipped binds that happens to be enough, but it is not independent, and a synthetic case shows it moving:Dropping the cache-key edit also gets this out of the way of two open changes it was pushing against. #8140 reports that an input to
keybindings_cache_keywhich flips at runtime costs a ~540 ms rebuild each time, made worse becauserefresh_keybindings_cachedeletes every sibling cache; #8229 is the open fix and it removes an input. Adding one was the wrong direction, and the two conflicted insidekeybindings_cache_keyitself. They no longer do:git merge-treeon this branch and #8229 leavesbin/omarchy-menu-keybindingsclean, with one adjacency conflict where both append to the end of the same test file. This version also needs no cache version bump, which matters becausev15is already claimed by both #8229 and #7564.Found while surveying what a locale change does to Omarchy for #12345, and sent on its own as promised in that issue's §7. Of the four follow-ups named there this is the one that is a defect on today's tree, and it has nothing to do with translated text: the strings are English either way, it is the order they come out in that moves.
🤖 Generated with Claude Code