What the PR does. Upstream #13968 (ff77cd02 + b2dae8db, live head) replaces stripJsonc's two regexes with one string-aware scanner and makes parseMenuJsonc return no rows for an array root. It fixes #13250 (a comma before } or ] inside a string was eaten), #13492 (an array root became rows "0","1") and #13493 (an inline // emptied the file). b2dae8db also reads every JS whitespace character outside strings (BOM, NBSP, Unicode spaces, VT, FF) as a space. Mirror commit 7a6ccfcd applies it onto baseline quattro-proof 311d29c7; the 14 commits after it are our proof work (record tip 680029da). Advisory audit; not an approval.
Risk rationale: Small parser change. Each fix has differential and observed test evidence, all 127 differences match a requirement, and the fuzz found nothing worse than the base. But 135 compatibility changes are unapproved (prevents ready), the production QV4 runtime and its parse cost are untested, and upgraded users' previously-ignored extension files will start to apply.
Read the full review (5 more paragraphs)
Is the fix verified? Changed-code coverage: 17/17 decision lines, 34/34 decisions. Test results are current, 59/59 passed (run-d071fef7; the baseline added 2 emoji-picker tests). All obligation cells of the 6 parser requirements are observed_pass. I ran base 311d29c7 and head: each issue reproduces at base and is fixed at head; 200k fuzzed inputs: none worse.
Compatibility. Readiness is not_assessed. Next decision: human_judgement (compat_changes_unapproved), "Approve each of the 135 backward-compatibility changes", by a reviewer under the approved policy @ 311d29c7. 135 compat_change_unapproved disclosures prevent readiness. I checked all 127 differences: 123 data inputs with a JS whitespace character go from parse error to parsed (BNZG); 4 are the issue reproducers. The 5 test_diff findings are tests renamed and flipped from expected failure to fixed. The 3 unapproved removed_behavior findings are intended too. The auditor's notes are not approvals; only a human reviewer approves.
Questions for a human. (1) Does Quickshell's QV4 engine match node on /\s/ and on cost (in node stripJsonc is ~4x slower on the default menu, ~16x on 1.1 MB)? No test runs QV4. (2) User extension files that loaded empty before (inline comment, Unicode space) now load and can override shipped entries after an upgrade.
Traceability. stripJsonc and parseMenuJsonc are traced at function level: E4J2, 3T3F, 66FW, C8W1, BNZG, BMFE (4 added; 3 modified, with PPDW). 23 more requirements are code_changed only via file-level test traces; their 27 not_linked cells have no tagged test. No spec-gap candidates; change effects were not analyzed (effects_unavailable).
Not caused by this PR. KI-MENU-JSONC-CR-LINE-ENDINGS and -STRIP-GAPS stay open. The baseline's move to 311d29c7 changes no file this PR touches. No gate fails; 13 checks were skipped.
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.
28 context notes (info — not a finding)
- AI · confirmed#13250: a comma is dropped only when the next token outside strings is } or ] (SW-REQ-260927-66FW, added)
The comma branch (MenuModel.js:26-38) looks ahead past whitespace and // comments and keeps the comma unless } or ] follows. String contents are copied verbatim (lines 13-19), so
"x, ]y"andmv f{.bak,}survive. I ran it on base 311d29c7 and head: base gives labelx ]y, head givesx, ]y. - AI · confirmed#13493: a // comment outside strings is dropped wherever it starts on the line (SW-REQ-260928-C8W1, added)
Lines 23-25 skip to (not past) the next \n, so the tokens on either side stay apart;
1//c\n2is still rejected. I ran it on base 311d29c7 and head: base gives 0 rows for{...} // note, head gives 1. The comment ends only at \n, so a CR-only file is unchanged (KI-MENU-JSONC-CR-LINE-ENDINGS stays open). - AI · confirmed#13492: an array root yields no rows (SW-REQ-260928-BMFE, added)
parseMenuJsonc adds
|| Array.isArray(parsed)to the non-object guard (MenuModel.js:100). I ran it: base gives rows0:a,1:b, head gives []. Array.isArray is realm-safe, so the engine's inventory note about instanceof across realms does not apply here. - AI · confirmedb2dae8db: JS whitespace outside strings becomes an ASCII space (SW-REQ-261001-BNZG, added)
Lines 39-42 map every /\s/ character except \n to ' ' (BOM, NBSP, U+1680, U+2000-200A, U+2028/9, U+202F, U+205F, U+3000, VT, FF, and also CR and tab, which JSON already accepts). Characters inside strings are untouched. This is in the live PR head b2dae8db.
- AI · confirmedSW-REQ-260922-E4J2 (JSONC stripping) is reworded from full-line comments to comments outside string literals
The description now says "strips // comments and trailing commas outside string literals". It matches the scanner and the PR's docs/menu.md text.
- AI · confirmedSW-REQ-260922-3T3F (non-object input gives an empty set) changes only its rationale (array root)
Only the rationale changed; the clause text is unchanged.
- AI · confirmedSYS-REQ-260922-PPDW (default + user menu merge) is modified on the mirror
This is the parent of the parser requirements. The edit is our proof work, not upstream code: only its hazard_review rationale (now describing the scanner and the array check) and review stamp changed. Its nominal, boundary and error_handling cells are observed_pass (nominal inherited from STK-REQ-260922-XTNR).
- AI opinion — unverified23 requirements are code_changed only through file-level test traces
menumodel-replay.test.mjs, menu-test.sh and report-reproducers.test.mjs carry whole-file Verifies traces (flagged weak_mapping). These requirements share the test files; their own text did not change. Each shows observed passing tests. Their 27 not_linked obligation cells have no tagged test and are NOT tested per class. Inherited: error_handling and external_call_timeout_bounded from SYS-REQ-260922-47T8 (2JZT, JRW1, MQ37, W17G, Y58B; RGCV the timeout class only), boundary from SYS-REQ-260922-R8DQ (CYB9, N3RM, PRNV, XW52, 8VJQ), error_handling from SYS-REQ-260927-WC89 (N3RM), atomicity from SYS-REQ-260922-0M8A (EFNR), boundary/error_handling from STK-REQ-260922-XTNR (0M8A both, R8DQ error_handling, V7W6 both) and boundary from STK-REQ-260912-XJ5D (WC89). Own: 0M8A atomicity, R8DQ boundary, WC89 error_handling. None of these classes concerns the parser change.
- AI opinion — unverifiedPurpose branches are covered and their obligation cells are observed passing
diffCoverage: 17/17 changed decision lines, 34/34 changed decisions, 0 uncovered (mcdc aggregate). testResults: junit, current, 59 passed / 0 failed / 0 skipped (57 last round; the baseline added test/shell.d/emojis-test.sh, run by its new plugins-shell command, and test/shell.d/menu-emoji-picker-test.sh; the package's dependencies list both among the tests run, and this PR touches neither). 66FW, C8W1, BMFE, BNZG, E4J2 and 3T3F have every obligation cell observed_pass (boundary, error_handling, malformed_input, input_domain, totality).
- AI opinion — unverifiedMirror composition: upstream product change vs our proof work
7a6ccfcd ("review: apply omacom/omarchy#13968 @b2dae8db onto baseline quattro-proof 311d29c7") applies upstream b2dae8db (MenuModel.js, docs/menu.md, the PR's menu-test.sh block). I diffed the evaluated tree against upstream b2dae8db: docs/menu.md is identical, and MenuModel.js differs only by added comment lines (
// Implements:and//mcdc:ignore). The next 14 commits (40684ad3..e0d5d42d) are our proof work, replayed unchanged from the previous mirror: reworded and added requirements, witnesses, KI closures, hazard re-review stamps, STE100 edits, trace reviews and KI history order. 680029da is an empty record commit. The previous mirror's tip commit (233160ad, a trace review of the baseline .jsonc link to STK-REQ-260922-XTNR) was dropped, because the baseline now carries that review. Against the previous evaluated mirror 233160ad (on quattro-proof 58cd8018), the tree differs exactly by the baseline's own edits 58cd8018..311d29c7: the same 9 files with the same added and removed lines. In menu.vars.yaml the baseline's 4 new SW-REQ-261004-JATY variables sit after the mirror's 9 parser variables: 136 menu variables, under the budget of 140 (127 at the baseline). STK-REQ-260922-XTNR is now byte-identical to the baseline's. The variables file's independence note still counts 59 guarantees; it was written before C9GM and JATY (the package's budget record shows 61). Their outputs app_listed and emoji_listed are used by no other requirement (I searched specs/), so the stated disjointness still holds. Every file under shell/plugins/menu, test/node, test/bdiff, the JSONC KI records, docs/menu.md, menu-test.sh and menu-acceptance-test.sh is byte-identical to the previous mirror. The package's diff, coverage, compat findings, behaviour-diff differences (ids, input digests, classifications), inventory and obligation cells are unchanged apart from commit, run and baseline ids. mcdc_diff counted 87 decisions at the base and 91 at the head (86 and 90 last round), with 0 findings. proof change freshness judges this package fresh against 311d29c7. - AI opinion — unverifiedMirror-only: four JSONC KI records marked fixed by this PR; history in time order
KI-MENU-JSONC-ARRAY-ROOT, -COMMA-IN-STRING, -INLINE-COMMENT and -UNICODE-WHITESPACE carry status: fixed (fixed by #13968) on the mirror. I checked that each file's history
at:stamps ascend. This is proof data on the mirror, not part of the upstream PR. The KI evidence on the mirror records known_issue_not_reproduced for the fixed KIs. - AI opinion — unverifiedBaseline move 58cd8018 -> 311d29c7 changes no file this PR touches
311d29c7 does not merge upstream (the upstream product code is still quattro 393a43d4). It adds a test command plugins-shell that runs the emoji picker's upstream test test/shell.d/emojis-test.sh and lists it under checks.test_results.commands. It puts shell/plugins/emojis/EmojiSearch.js in the menu scope with SW-REQ-261004-JATY (the picker's search lists an emoji exactly when the entry has an emoji, its keywords contain the trimmed, lowercased search text and the list has room under the limit; parent SYS-REQ-260922-6642), adds comment-only Implements lines to EmojiSearch.js, and adds the witness test test/shell.d/menu-emoji-picker-test.sh. It records the trace review of STK-REQ-260922-XTNR -> default/omarchy/omarchy-menu.jsonc, adds proof/review-guidance/menu.md (review rules RG-MENU-1..5, which no check reads) and updates the README's engine line. None of these files is in the PR's diff, and JATY and SYS-REQ-260922-6642 are not in review.changes.
- AI opinion — unverifiedSTK-REQ-260922-XTNR trace review now comes from the baseline
The baseline's file-level
// Implements: STK-REQ-260922-XTNRon line 2 of default/omarchy/omarchy-menu.jsonc is a traced link (the engine reads .jsonc annotations). Last round the mirror's tip commit recorded its review; now baseline commit b5c7073b records it, with the same citations (the menu file line 2, menu-acceptance-test.sh:12, menu-dispatcher-test.sh:48). review.traceReviewRestamped no longer lists STK-REQ-260922-XTNR. The PR does not touch the menu file. - AI opinion — unverifiedRemoved whole-line-comment \s quirk is preserved (cf-39627352fe66ab9c, fixed)
At base,
\u00A0// notelines were removed whole by ^\s*. At head, the NBSP becomes a space and the comment is dropped, so the row count is the same. menu-test.sh pins this for BOM, NBSP, LSEP, IDEO, VT and FF before a comment. This one is recorded as fixed, not unapproved. - AI opinion — unverifiedDifferential: all 127 recorded differences checked; none unexplained
I checked every data-partition difference: each of the 123 inputs carries exactly one of VT, FF, NBSP, U+1680, U+2000-200A, U+2028, U+2029, U+202F, U+205F, U+3000 or BOM outside strings, and goes from strip_parse=ERR (0 rows) to object (0/2/344 rows, by seed). CR, CRLF, empty, huge and invalid-UTF-8 inputs showed no difference, nor did the lifecycle (14), scale (3) and timing (3) inputs. The 4 corpus differences are the issue reproducers. Engine classification: intended with requirement (123 BNZG, 2 C8W1, 1 BMFE, 1 66FW). Separately, I fuzzed 200,000 random JSONC files (labels with , } ] / " \\ and Unicode spaces, random comments, trailing commas, CRLF): 165,888 same, 34,112 changed to the correct result, 0 changed to a wrong one (rerun on this mirror, base 311d29c7). That fuzz is outside the package.
- AI · confirmedIntended difference bd-05efa2fff912da5a: #13493 reproducer (sample extension with inline comment tail): 0 to 3 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-260928-C8W1 specifies. Recorded classification: intended (SW-REQ-260928-C8W1). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other corpus differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-41c905c2c01b5b15: #13493 reproducer (inline comment rows): 0 to 3 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-260928-C8W1 specifies. Recorded classification: intended (SW-REQ-260928-C8W1). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other corpus differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-4453e5b664893137: #13492 array root: phantom rows 0,1 to no rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-260928-BMFE specifies. Recorded classification: intended (SW-REQ-260928-BMFE). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other corpus differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-9495f0dcccf57210: #13250 comma before } inside a label/description is kept
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-260927-66FW specifies. Recorded classification: intended (SW-REQ-260927-66FW). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other corpus differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-9746b2470668a698: BOM on every line of the shipped default menu: 0 to 344 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-261001-BNZG specifies. Recorded classification: intended (SW-REQ-261001-BNZG). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other 122 data differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-37bfadd5b04e9651: NBSP indenting the shipped default menu: 0 to 344 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-261001-BNZG specifies. Recorded classification: intended (SW-REQ-261001-BNZG). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other 122 data differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-4aaa4dc6085c50ba: U+2028 indenting the shipped default menu: 0 to 344 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-261001-BNZG specifies. Recorded classification: intended (SW-REQ-261001-BNZG). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other 122 data differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-013206277c5844ea: vertical tab indenting the shipped default menu: 0 to 344 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-261001-BNZG specifies. Recorded classification: intended (SW-REQ-261001-BNZG). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other 122 data differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-df933919303826e2: form feed indenting the issue #11211 snippet: 0 to 2 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-261001-BNZG specifies. Recorded classification: intended (SW-REQ-261001-BNZG). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other 122 data differences follow the same pattern (see the differential finding).
- AI · confirmedIntended difference bd-cfd90a4600fd0086: U+3000 indenting the issue #11211 snippet: 0 to 2 rows
Base gives strip_parse=ERR or wrong rows; head gives the result SW-REQ-261001-BNZG specifies. Recorded classification: intended (SW-REQ-261001-BNZG). I reproduced this class of input with node on base 311d29c7 vs head MenuModel.js. Representative of its group; the other 122 data differences follow the same pattern (see the differential finding).
- AI opinion — unverifiedKI-MENU-JSONC-CR-LINE-ENDINGS and KI-MENU-JSONC-STRIP-GAPS remain open; the PR does not change them
I ran both: a CR-only file with a // comment (
// c\r{...}) and a /* */ block comment give 0 rows at base and at head. The scanner ends a comment only at \n and has no block-comment state. The PR's docs/menu.md now says block comments are not supported. These KIs pre-date the PR. - AI opinion — unverifiedChange effects not analyzed; scope analysis bounds acknowledged
effects_unavailable: review.effects covers C/C++ only, so callers, gained effects, new mutable state and call-level requirement impact are not in the package for this JS change. The scope approximations say no uncodified connection could be detected, so 'No spec-gap candidates' proves nothing here. By reading: stripJsonc/parseMenuJsonc add no callees, no I/O, and no state beyond local variables.
- AI opinion — unverifiedAll 135 compatibility findings stay unapproved; only a human reviewer can approve them
Each of the 135 compat_change_unapproved disclosures prevents readiness until a reviewer or above approves it under the approved policy @ 311d29c7, or the author fixes it. The next decision states the exact count ("Approve each of the 135 backward-compatibility changes"). The auditor (buger, who is also this PR's author) has recorded a note on each of the 135, assessing it as acceptable with the requirement or issue it follows; the engine shows those as informational notes, not approvals, and they resolve nothing. My reading that each difference is intended is commentary, not an approval either.