verify
Drive EZR's actual surfaces to confirm a change works — the program runner, the browser interface, and the tool CLIs. Use when verifying a change under ezr/, or when asked to check that EZR works. Not
SKILL.mdezr/.claude/skills/verify/SKILL.md
GitHubVerifying EZR
Runtime observation, not test runs. run.sh is the CI gate and proves
nothing about whether a change is reachable by a user.
The thing that will catch you out
There are three languages in this repository and they are not the same language. A change to one is invisible from the others, and the test suites do not notice.
| Surface | Language | Where it lives |
|---|---|---|
2-interpreter-python/ezrun.py |
the core — lists, let, show, len/head/tail, recursion |
syntax.py's AST pipeline |
2-interpreter-python/ezr.py <file> |
the directive language — tax = 40, expect, learn |
the EZR class in ezr.py |
5-runtime-java/ezr |
the core again, in Java — same language, same exit codes | com.codric.ezr |
6-interface-html/ezr-live.html |
the directive language again, in JS | hand-written, self-contained |
A core-language program fed to ezr.py or the browser returns
cannot parse. That is correct behaviour, not a bug — but it is how
an entire feature set once shipped unreachable while 87 assertions
passed, so check the surface matches the change before concluding
anything.
Never verify by importing. from syntax import compile_ezr then
calling it is a unit test you wrote. It was exactly how lists and
let were "verified" while no user could run them. Shell out.
Driving the core language
cd 2-interpreter-python
python3 ezrun.py ../examples/sum.ezr # 15 @ 256/256
python3 ezrun.py ../examples/largest.ezr # 42 @ 256/256
python3 ezrun.py -e 'let x = 5 in x + 1' # 6 @ 256/256
echo '[1, 2, 3]' | python3 ezrun.py - # stdin
python3 ezrun.py ../examples/largest.ezr --call 'largest([3, 9, 2])'
A file of definitions must say what to run — main() or --call —
because program := definitions | expression and the trailing-
expression grammar is ambiguous.
Exit codes are the thing to assert on: 0 a value, 1 a refusal
(the program ran and produced Z), 2 input that would not
compile. Check them with $? directly — cmd | head gives you
head's status, which is the bug that let run.sh report PASSED for
failing layers for several commits.
Worth probing: head([]) (refusal, exit 1), len(5) (wrong type),
a missing file and a directory (sentences, not tracebacks), -d 2
(depth ceiling), a file with neither main() nor --call.
Driving the Java runtime
cd 5-runtime-java
./build.sh # javac; needs a JDK
./ezr ../examples/sum.ezr # 15 @ 256/256
./ezr -e 'let x = 5 in x + 1' # 6 @ 256/256
echo 'def main() = 6 * 7' | ./ezr - # 42 @ 256/256
Same exit codes as ezrun.py: 0 a value, 1 a refusal, 2 uncompilable.
Gotcha: if the environment sets JAVA_TOOL_OPTIONS, the JVM prints
Picked up JAVA_TOOL_OPTIONS: ... to stderr on every single start, which
corrupts any byte comparison. Setting it empty does not help — the banner
still prints, with an empty value. It has to be removed:
env -u JAVA_TOOL_OPTIONS java .... The ./ezr wrapper already does
this; a raw java -cp out invocation does not.
A change to the core language is not verified until both runners
agree. python3 differential.py runs one corpus through ezrun.py and
./ezr as processes and compares the value, the exit code, which stage
refused, and which binding defect. It does not compare the wording —
two runners have different names for themselves and different phrasing
for the same refusal, and holding them to identical prose tests the error
messages rather than the language.
Driving the browser interface
python3 -m http.server 8099 --directory 6-interface-html
Then Playwright against /opt/pw-browsers/chromium. Two gotchas that
cost time:
#editoris hidden until you click EDIT. Order is EDIT → fill#editor→ LOAD PROGRAM → RUN.- The demo picker is a
<select>, not buttons —pg.locator("select").first.select_option("full"). Clicking the option text times out. - Buttons by index:
0 RESET, 1 ←BACK, 2 STEP→, 3 RUN, 4 EDIT/VIEW, 5 LOAD.get_by_role("button", name="RUN", exact=True)does not match; use.nth(3). - The only console 404 is
favicon.ico. Harmless.
Output lands in the EVENTS pane; a healthy run shows BIND,
ANCHOR, ASCEND and renders the weave on the 0–256 scale.
Driving the tools
cd 2-interpreter-python && python3 audit.py # documents vs code
cd 7-forge && python3 selfheal.py # inject a defect, repair it
cd 7-forge && python3 forge.py --cycles 3 --cycle-budget 2000
audit.py exits 1 on an overstatement. probe_runner inside it
shells out to ezrun.py, so a core language that cannot be run fails
run.sh through both Audit and Physics.
Checks worth making for any change under ezr/
- Which of the three languages does this touch? Drive that one.
- Grammar change → all twenty front ends must agree
(
7-forge/forge.py --cycles), andGRAMMAR.ebnfmust be re-emitted (grammar_doc.py), never hand-edited. - New language surface → does
audit.pyhave a probe for it? An undocumented capability is a finding in this project. - Anything claimed in a document →
python3 audit.pydecides, not memory.