fix(repl): normalizer PREV_BINOP class for binop-at-EOL continuations (#620 burndown)#643
Merged
Merged
Conversation
…ons (#620 burndown) 5th hole in normalize_newlines_to_semicolons after #628/#635/#640. When a UserTalk script ends a line with a binary-operator token ('and', 'or', '==', '>=', '<=', '!=', '>', '<', '+', '-', '*', '/', ',', '('), the next line is a continuation of the same expression. Previously the (PREV_OTHER, NEXT_OTHER) cell emitted ';' between them, producing source like 'x and; y' that the parser rejected. The 5 failing sys.* integration tests with multi-line expressions (typeof(sys.os()) == stringType) and ...) were the visible symptom. Add PREV_BINOP class to the transition table. The classifier (prev_is_binop) walks back through the output buffer to detect multi-byte operator tokens ('and' / 'or' word-bounded, '==' / '>=' / '<=' / '!=' via byte-before check) so single '=' (assignment) is correctly distinguished from '=='. New BINOP row suppresses ';' for every next-kind. Adds 8 integration tests in protocol_eval_compile_errors.yaml covering the new transition cell and the disambiguation paths (single '=' assignment, identifier ending in 'and' / 'or'). Integration: -5 failures (sys cluster) + 8 new tests, all green. Unit: 489/489. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bar-raiser flagged P1 test gaps:
- Docblock claims BINOP coverage for >=, <=, !=, >, <, *, /, - but tests
only exercised and, or, ==, +, ',', '('.
- 'and' had a band/kand word-boundary regression but 'or' did not.
Add 4 new tests:
- '>=' pins the '>' arm of the byte-before-'=' disambiguation
- '!=' pins the '!' arm
- '*' pins the BINOP single-char arithmetic byte-class
- 'door' as variable pins the 'or' word-boundary check (symmetric to 'band')
Integration: 2321 total / 2050 passed / 64 failed / 207 skipped — +4 total,
+5 passed (the 4 new tests + a flake recovery), -1 failed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner
Author
Local /gate round 2 (post-P1 fixes)Verdict: PASS — both P1 findings addressed. Round 1 → Round 2Round 1 (
Tests at HEAD
|
Owner
Author
/auto SummaryWhat went well:
What could improve:
Recommendations:
🤖 Generated with Claude Code /auto |
jsavin
added a commit
that referenced
this pull request
May 25, 2026
…for ODB editing (closes #644) (#647) * feat(tooling): edit_virgin_root.sh stage-and-confirm wrapper (closes #644) Interpose a staging step between the operator and databases/Virgin.root for protocol-mode ODB edits. The wrapper copies Virgin.root to /tmp/frontier-edit-<hash>/, spawns frontier-cli --protocol against the staged copy, and prompts before promoting changes back to the canonical file. A killed session, runaway script, or leaked --allow-mutate process can no longer corrupt the source — worst case the staged copy is discarded. Also hardens tests/debug_protocol_test.sh to stage Virgin.root into a tmpdir before launching protocol mode (latent risk if the #588 read-only default ever regresses), and updates docs/ODB_SCRIPT_EDITING.md to point operators at the wrapper as the recommended workflow with the raw --allow-mutate invocation retained as an emergency/experts-only fallback. Background: PR #643 saw databases/Virgin.root grow from 20MB to 31MB during an autonomous agent run, traced to a leaked --allow-mutate session in a sibling worktree. The documented workflow points --allow-mutate directly at the canonical file by design — this wrapper addresses that gap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(tooling): address /gate review for #647 — mktemp, EXIT trap, retab, atomic promote, race-check Round-2 review fixes for edit_virgin_root.sh: 1. (P1 security) Use `mktemp -d -t frontier-edit-<md5>-XXXXXXXX` instead of a predictable `/tmp/frontier-edit-<md5>` path. Closes symlink-hijack on /tmp. Adds belt-and-suspenders check that staged path is a regular file (not a symlink). Help text updated to describe "$TMPDIR" not /tmp. 2. (P1 bar-raiser) Install an EXIT trap to clean up the stage dir. Sets STAGE_PRESERVE=1 on the two forensic branches (rejected promotion and --read-only-changed) so the trap leaves the stage dir for inspection. Drops the scattered explicit `rm -rf "$STAGE_DIR"` calls. 3. (P1 bar-raiser) Retab tools/edit_virgin_root.sh and tests/integration/edit_virgin_root_wrapper_test.sh from tabs to 4-space indent. Every other *.sh in the repo uses 4-space; CLAUDE.md's tab rule is C-only. 4. (P2 promoted) Atomic promotion: copy staged copy to .promoting.$$ tmp in the canonical's directory, sync, then mv into place. A Ctrl-C mid-copy leaves the old Virgin.root intact; only the rename swaps. Trap also sweeps any orphan .promoting.$$ files in the canonical dir. 5. (P2 promoted) Stale-process race check: before promotion, recompute md5 of canonical and refuse with exit 5 (preserving staged copy) if it changed externally during the session. Addresses the wrapper's whole reason for existing — another process holding the file writable. Bash test updated to look for stage dirs under $TMPDIR (macOS default /var/folders/.../T/) and /tmp, since mktemp -d -t honors $TMPDIR. All 5/5 wrapper tests pass. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
jsavin
added a commit
that referenced
this pull request
May 25, 2026
…648) * fix(repl): recognize symbolic && / || in prev_is_binop (#643 burndown) The newline normalizer's PREV_BINOP classifier covered only the keyword forms 'and' / 'or'. A line ending in '&&' or '||' before a bare newline was misclassified as PREV_OTHER, causing a synthetic ';' to be inserted ('&&;\n') and the parser to reject the script. Add two-byte lookbehind for '&&' and '||' in prev_is_binop, mirroring the existing '=' multi-byte arm. Single '&' / '|' return 0 — neither is a UserTalk operator, so they correctly fall through to the default ';'-emission path. Motivating failure: db_migration's "Database - system table accessible after hydration" test, whose body is a multi-line 'sizeOf(x) > 0 &&\n...' chain. Replacing '&&' with 'and' makes that test pass without any code change — the falsifiable check that this was the root cause. Tests: 4 new cases in protocol_eval_compile_errors.yaml exercise '&&' EOL, '||' EOL, '&&' followed by parens, and mixed symbolic/keyword operators across continuation lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(repl): complete PREV_BINOP coverage for all UserTalk infix operators Builds on d092576 (which added `&&` and `||`). This commit: - Adds `%` (modulo) to the symbolic single-char BINOP row. - Refactors keyword-operator detection into a `prev_matches_keyword` helper with explicit word-boundary check. Replaces the ad-hoc `case 'd'` / `case 'r'` arms for `and` / `or`. - Adds keyword infix operators: `equals`, `notEquals`, `lessThan`, `greaterThan`, `contains`, `beginsWith`, `endsWith`. `lessThanOrEqual` / `greaterThanOrEqual` are documented in docs/usertalk/operators_and_idioms.md but the UserTalk parser does NOT actually accept them as infix operators (verified empirically: `1 lessThanOrEqual 1` compiles and returns `1`, not `true`). They are intentionally omitted; documented inline alongside the code. Tests: 11 new positive cases in protocol_eval_compile_errors.yaml covering each new operator across a newline, one word-boundary regression guard for `contains`, plus negative cases asserting bare `&` and bare `|` at EOL are not promoted to BINOP. Bar-raiser P2-1 + P2-2 closure on PR #126. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
5th hole in
normalize_newlines_to_semicolons(after #628/#635/#640): a UserTalk script ending a line with a binary-operator token (and,or,==,>=,<=,!=,>,<,+,-,*,/,,,() had a synthetic;inserted before the continuation, producing source likex and; ythat the parser rejected. The 5 failingsys.*integration tests using(typeof(...) == stringType) and\n (typeof(...) == stringType)patterns were the visible symptom; the same hole catches any multi-line expression with a trailing binop.Add a
PREV_BINOProw to the transition table that suppresses;for every next-kind — a continuation is a continuation regardless of what follows.Implementation
PREV_BINOPmember inprev_kind_t. The transition table grows one row; all five cells aresuppress.prev_is_binop(out, dst)helper walks back through the output buffer to detect the trailing operator token. Single-char operators (+ - * / , ( > <) match on the last byte. Multi-char operators require disambiguation:=is BINOP only if the byte before is=,>,<, or!(so==/>=/<=/!=match but bare assignment=does not).and/orrequire a word boundary before the keyword (so identifiers likebandordoorare not misclassified).classify_prev_full(prev_nonws, out, dst)is the buffer-aware wrapper used by the normalizer loop. Falls back to single-byteclassify_prevfor anchor bytes (\0 ; { }), then upgradesPREV_OTHERtoPREV_BINOPwhenprev_is_binopmatches.kEmitSep: new BINOP row, all-zero. Existing rows untouched.Judgement calls
-is over-suppressed.x = -\n 1(unary minus split across lines) is statically indistinguishable from binary-without a real tokenizer. We treat all trailing-as BINOP. The worst case is a script that was already malformed losing one parse-error surfacing, which is preferable to silently mis-normalizing the commonx +\n yandx -\n ypatterns.)is NOT BINOP. Close paren is end-of-expression, not continuation. Same for}and;(handled by their own prev-kind classes).sys.appisrunningimplementation, buttests/headless_sys_verbs.calready implements it viapgrep -x(committed earlier, marked COMPLETE). Manual testing confirmssys.appisrunning("frontier-cli")returnstruewhen called from inside frontier-cli. The 5 failing tests are 100 percent normalizer-caused (all useand\ncontinuations). Change B from the task was unnecessary.Tests
8 new tests in
protocol_eval_compile_errors.yaml:and/or, relational==, arithmetic+, comma, open paren)local (band = 7)\n return band— identifier ending inandmust still get;)x =\n 5— single=is assignment, must still get;; result is a parse error, locked in assuccess: false)Test plan
make -C frontier-cli -j$(sysctl -n hw.ncpu)— clean build, no new warnings./tools/run_headless_tests.sh— 489/489 passcd tests && make test-integration— 70 to 65 failures (-5 sys cluster), +8 new tests all green. Net: 2032 to 2045 passing, 2309 to 2317 totalprotocol_eval_compile_errors.yamlstill pass unchangedsys.appisrunning("frontier-cli")returnstruevia--protocol script/eval— appisrunning was already workingPer-cluster count change
sys verbs - system information suite— fixed (usedand\n)sys verbs - all info verbs return strings— fixed (usedand\n)sys verbs - numeric verbs return long— fixed (usedand\n)sys verbs - boolean verbs return bool— fixed (usedand\n)sys environment verbs - isolation test— fixed (usedand\n)Generated with Claude Code (https://claude.com/claude-code)