Release v0.0.120: trap categorisation + stdout preserved on trap#533
Release v0.0.120: trap categorisation + stdout preserved on trap#533
Conversation
- Hero image overflowed the viewport because .hero-image only set
max-width: 640px, which won the specificity contest against the
global img { max-width: 100% } rule. On a 375px viewport the image
kept its 640px target and ran off the right edge. Added width: 100%
alongside max-width so the image now scales to viewport width up to
640px.
- VeraBench GitHub button was being stretched to 280px on mobile by
a .btn { width: 100%; max-width: 280px } rule meant only for the
hero CTA stack (where buttons stack vertically and benefit from a
full-width target). Scoped that rule to .cta-bar .btn so isolated
buttons elsewhere on the page keep their natural inline-flex width.
- Shell-command code blocks (.runs and .install) ran off the right
edge because <pre> defaults to white-space: pre, which doesn't wrap
long lines like "vera run examples/factorial.vera --fn factorial -- 10".
overflow-x: auto produces a scroll affordance but iOS users have no
good gesture to use it inside a tight column. Added
white-space: pre-wrap; word-break: break-word for .code-block pre
on mobile only. Vera sample blocks (.sample .code) intentionally keep
pre so syntax indentation isn't disrupted at narrow widths.
Verified at 375px viewport: hero image 327px (fits), bench button
143px (inline-sized), runs code blocks scrollWidth === clientWidth
(no overflow). Hero CTA buttons remain stretched at 280px as designed.
Co-Authored-By: Claude <noreply@anthropic.invalid>
First pair from the bug-killing campaign. Two paired fixes that share the same site (vera/codegen/api.py's except Exception branch) and one test fixture, so they ship together as v0.0.120: #522 (closes) - IO.print output preserved on trap #516 Stage 1 - WASM traps classified to a stable kind ## Implementation - New WasmTrapError(RuntimeError) carrying stdout, stderr, kind. Liskov-substitutable: existing 'except RuntimeError' blocks still catch it and see a sensible string. - New _classify_trap(exc, last_violation) -> (kind, message). The contract-violation host-import channel (last_violation) always wins over the wasmtime trap reason; everything else is matched on the 'wasm trap: <reason>' substring in str(exc) and mapped to one of: divide_by_zero / out_of_bounds / stack_exhausted / unreachable / overflow / contract_violation / unknown. - execute()'s except branch now raises WasmTrapError with the captured buffers. cmd_run handles it before the generic except RuntimeError, printing buffered stdout BEFORE the error in text mode and packing it into the JSON envelope (with trap_kind) in JSON mode. - Explicit sys.stdout.flush() after the captured-output write so '2>&1' redirects show the right merged-stream order. ## Tests - New tests/test_runtime_traps.py (16 tests, 284 lines): classifier unit tests with a _FakeTrap stand-in (the classifier is stringly- typed, decoupled from wasmtime); WasmTrapError shape; end-to-end cmd_run for stdout-on-trap and trap-kind in both text and JSON modes. - Full suite: 3520 -> 3536 (+16) passing, 14 skipped, zero existing tests broken. WasmTrapError(RuntimeError) preserved compatibility. ## Folded in: PR #532 (mobile CSS fixes) Cherry-picked the docs(site) commit from PR #532 since it's a single- file CSS-only change with no overlap with the trap work. Three iOS Safari overflow bugs at iPhone widths fixed: hero meerkat image (width:100% missing), VeraBench vera-bench button (mobile .btn rule leaking outside .cta-bar), and Runs section shell-command code blocks (white-space:pre with no wrap). ## Doc updates - Version bump 0.0.119 -> 0.0.120 across pyproject.toml, vera/__init__.py, docs/index.html, README.md (test count + release count too). - KNOWN_ISSUES: removed #522 row (closed); rewrote #516 row to reflect Stage 1 done. - ROADMAP: row 3 now '#516 (Stages 2-3)' instead of '#516 + #522 paired'; intro updated from 'twelve open' to 'eleven remain'. - HISTORY: added v0.0.120 row to Stage 9 table. - CHANGELOG: [Unreleased] -> [0.0.120] - 2026-04-26 with Fixed/Improved/ Tests/Website sections; new empty [Unreleased] block on top. - TESTING: total counts 3534 -> 3550, file count 28 -> 29, new row for test_runtime_traps.py. Co-Authored-By: Claude <noreply@anthropic.invalid>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds Stage‑1 runtime trap diagnostics: introduces Changes
Sequence DiagramsequenceDiagram
participant WasmExec as WASM Executor (vera/codegen/api.py)
participant Classifier as Trap Classifier (_classify_trap)
participant CLI as CLI Handler (vera/cli.py)
participant Streams as stdout/stderr
WasmExec->>WasmExec: run WASM, buffer stdout (and stderr)
WasmExec->>Classifier: _classify_trap(exc, last_violation)
Classifier-->>WasmExec: (kind, message)
WasmExec->>WasmExec: raise WasmTrapError(message, stdout, stderr, kind)
WasmExec-->>CLI: propagate WasmTrapError
CLI->>Streams: flush and replay captured stdout
CLI->>Streams: replay captured stderr
alt JSON mode
CLI->>Streams: emit JSON envelope { ok:false, trap_kind, stdout, stderr }
else Text mode
CLI->>Streams: print formatted error message after replays
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #533 +/- ##
==========================================
- Coverage 91.03% 91.01% -0.03%
==========================================
Files 58 58
Lines 21964 22014 +50
Branches 259 259
==========================================
+ Hits 19995 20036 +41
- Misses 1962 1971 +9
Partials 7 7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@HISTORY.md`:
- Line 257: The new release entry v0.0.120 was added but the roll-up summary
still says "119 tagged releases"; update the summary count to reflect the new
release (change "119 tagged releases" to "120 tagged releases") and scan
HISTORY.md for any other places that mention the total number of tagged releases
to keep them consistent with the newly added v0.0.120 entry.
In `@KNOWN_ISSUES.md`:
- Line 17: The documentation incorrectly refers to the envelope field `kind`;
update the KNOWN_ISSUES.md wording to reference the actual emitted JSON key
`trap_kind` (as produced by cmd_run()) and adjust any examples or explanation
that direct downstream consumers to `kind` so they instead use `trap_kind`;
ensure the note still mentions staged fixes and v0.0.120 but points readers to
`trap_kind` used per diagnostic by cmd_run().
In `@README.md`:
- Line 184: Update the README banner text that currently reads "Vera is in
**active development** at v0.0.120 — 810+ commits, 120 releases, 3,536 tests,
96% code coverage, 80 conformance programs, 32 examples, and a 13-chapter
specification." to use the correct example count "30 examples" (i.e., change "32
examples" to "30 examples"); search for other prose occurrences of the example
count and make them consistent if present (references: the README banner line
containing "v0.0.120" and the mention of "examples").
In `@ROADMAP.md`:
- Line 39: Update the roadmap text to use the shipped JSON field name: replace
the mention of `kind` with `trap_kind` in the sentence that currently reads "the
JSON envelope carries the `kind`" so it instead reads that the JSON envelope
carries `trap_kind`; target the string `kind` in the ROADMAP.md entry for
"Runtime trap diagnostics (Stages 2–3)" and ensure the exact token `trap_kind`
is used to match the codebase.
In `@tests/test_runtime_traps.py`:
- Around line 195-211: The current test
test_json_mode_includes_stdout_in_envelope only covers IO.print/stdout and
misses the stderr-preservation contract; add a sibling regression test (e.g.,
test_json_mode_includes_stderr_in_envelope) that creates a temp file from a
program which writes to IO.stderr(...) before crashing, calls cmd_run(...,
as_json=True), parses the JSON envelope, asserts rc==1 and envelope["ok"] is
False, and verifies the envelope contains a "stderr" field with the expected
stderr lines (similar to the stdout assertions); reference cmd_run and IO.stderr
in the new test to ensure stderr is captured into the envelope.
In `@vera/cli.py`:
- Around line 640-685: The cmd_run execution path currently doesn't capture
runtime stderr so WasmTrapError handler's exc.stderr and JSON "stderr" field are
never populated; update the call site in cmd_run where execute(...) is invoked
to pass capture_stderr=True (matching how stdout is captured), then propagate
and use exec_result.stderr on the non-trap success path (i.e. when not raising
WasmTrapError) to replay or include stderr output the same way you handle
exec_result.stdout/as_json so both text and JSON modes emit the captured stderr
field and replay it to sys.stderr in text mode.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1ffde53b-6bf6-423b-ba72-255e1831a29d
⛔ Files ignored due to path filters (5)
docs/index.htmlis excluded by!docs/**docs/index.mdis excluded by!docs/**docs/llms-full.txtis excluded by!docs/**docs/llms.txtis excluded by!docs/**docs/sitemap.xmlis excluded by!docs/**
📒 Files selected for processing (11)
CHANGELOG.mdHISTORY.mdKNOWN_ISSUES.mdREADME.mdROADMAP.mdTESTING.mdpyproject.tomltests/test_runtime_traps.pyvera/__init__.pyvera/cli.pyvera/codegen/api.py
Five real findings from CodeRabbit, one rabbit-incorrect: 1. (real) HISTORY.md line 294: '119 tagged releases' -> 120. 2. (real) KNOWN_ISSUES line 17: clarified the JSON envelope key is trap_kind, distinct from WasmTrapError.kind (the Python attribute). 3. (rabbit-incorrect) README.md '32 examples' is correct (filesystem count via ls examples/*.vera = 32). Not changed; replied. 4. (real) ROADMAP.md line 39: same kind/trap_kind clarification as #2. 5. (real) Added test_json_mode_includes_stderr_in_envelope. 6. (real, the important one) cmd_run never passed capture_stderr=True to execute(), so WasmTrapError.stderr and the JSON envelope's stderr field were permanently empty. Wired through: a. cmd_run now passes capture_stderr=True (parallel to stdout's always-on capture) b. Success path replays exec_result.stderr to sys.stderr (text) or includes it in the envelope (JSON) c. Existing WasmTrapError handler now actually populates these fields on the trap path d. Preserved the original 'stdout suppresses value-printing' behaviour (stderr does not suppress value-printing -- it's independent in its own stream) Plus lint fix: 'uv lock --check' was failing because pyproject.toml version bumped 0.0.119 -> 0.0.120 but uv.lock still pinned 0.0.119. Ran 'uv lock' to sync. Test count: 3536 -> 3537 (+1 from new stderr test); doc counts and TESTING.md row updated accordingly. Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ROADMAP.md`:
- Line 272: Update the release count and version marker in the ROADMAP summary
string: change "119 tagged releases (as of v0.0.119)" to "120 tagged releases
(as of v0.0.120)" so the ROADMAP summary (the sentence containing "810+ commits,
119 tagged releases (as of v0.0.119), 3,551 tests...") matches the new v0.0.120
release referenced in HISTORY.md.
In `@TESTING.md`:
- Line 9: Update the aggregate test summary in TESTING.md to reflect the actual
run: change the total from 3,551 to 2,896 and update the passed/skipped counts
to match the real test run output (recalculate passed = actual passed, skipped =
actual skipped from your test run). Ensure the summary line that currently reads
"| **Tests** | 3,551 across 29 files (~34,200 lines of test code; 3,537 passed,
14 skipped) |" is replaced with the corrected totals and that the per-file
breakdown includes test_runtime_traps.py with 17 tests; verify the numbers are
consistent (total equals sum of per-file counts and passed+failed+skipped equals
total).
In `@tests/test_runtime_traps.py`:
- Around line 195-247: The tests test_json_mode_includes_stdout_in_envelope and
test_json_mode_includes_stderr_in_envelope currently assert envelope contents
but don't assert that nothing was written to the real stderr when cmd_run(...,
as_json=True) is used; update those tests to also assert capsys.readouterr().err
is empty (or specifically does not contain human-readable error text) after
calling cmd_run to ensure cmd_run/execute is invoked with capture_stderr=True
and that JSON mode does not leak messages to real stderr; locate assertions in
these tests and add the stderr-empty check, and consider adding the same check
to the related test functions referencing cmd_run and as_json in this diff.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6d28d7c6-629e-47d4-8b9f-30c7adb49b34
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock,!uv.lock
📒 Files selected for processing (7)
CHANGELOG.mdHISTORY.mdKNOWN_ISSUES.mdROADMAP.mdTESTING.mdtests/test_runtime_traps.pyvera/cli.py
…nvariant Two real findings, one rabbit-incorrect: 1. (real) ROADMAP line 272: '119 tagged releases (as of v0.0.119)' was missed in the version bump for the v0.0.120 release. Updated to '120 tagged releases (as of v0.0.120)'. 2. (rabbit-incorrect) TESTING.md '3,551 tests' is correct. The rabbit suggested 2,896 which appears hallucinated — actual pytest collection is 3,551, and check_doc_counts.py enforces that exact number. No change; replied with verification. 3. (real) Added JSON-mode 'no stderr leak' invariant assertions to all four JSON-emitting tests in test_runtime_traps.py. The assertion captured.err == '' pins that JSON mode keeps all error reporting INSIDE the envelope — writing to actual stderr would split machine-readable output across two streams and break parsing for downstream consumers (LSP, agents). Same assertion added to: - test_json_mode_includes_stdout_in_envelope - test_json_mode_includes_stderr_in_envelope (new in last round) - test_json_mode_includes_trap_kind - test_json_contract_violation_kind Test count unchanged at 17 (assertions added inline); line count 319 -> 345 in TESTING.md table. Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/test_runtime_traps.py`:
- Around line 175-193: Update the
test_text_mode_prints_buffered_stdout_before_error test to verify cross-stream
ordering by capturing stdout and stderr into the same buffer: add imports for io
and redirect_stdout/redirect_stderr, create a single io.StringIO, run
cmd_run(str(path)) while redirecting both stdout and stderr into that buffer,
then assert that the "line 1...line 4" messages appear in order and that "Error"
appears after them in the combined buffer; keep existing separate capsys
assertions optional but rely on the merged-buffer check for the ordering
guarantee.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 35564a33-2674-4b18-a711-c9a53d745891
📒 Files selected for processing (3)
ROADMAP.mdTESTING.mdtests/test_runtime_traps.py
Real finding: existing test_text_mode_prints_buffered_stdout_before_error asserts per-stream content (capsys gives stdout/stderr separately) but doesn't pin the ORDER of writes across the two streams. Added sibling test_text_mode_cross_stream_ordering using contextlib.redirect_stdout + redirect_stderr aimed at one io.StringIO. Asserts: - all four 'line N before crash' messages appear in order, AND - the 'Error' message appears AFTER the last print line Honest caveat in the docstring: this test does NOT exercise the OS-level buffering concern that sys.stdout.flush() defends against. With both streams aimed at one StringIO the flush is a no-op (StringIO has no OS-level buffer). What it DOES catch: a refactor that swaps the stdout-replay and error-print blocks in cmd_run's WasmTrapError handler. Added as a SIBLING test rather than mutating the existing one because they cover distinct properties: - existing: per-stream content (stdout has prints, stderr has error) - new: cross-stream code order Different regressions catch different bugs; both worth pinning. Test count: 17 -> 18; doc counts updated. Co-Authored-By: Claude <noreply@anthropic.invalid>
First pair from the bug-killing campaign. Two paired fixes share the same site (
vera/codegen/api.py'sexcept Exceptionbranch) and one test fixture, so they ship together as v0.0.120:IO.printoutput preserved on trapkindWhat changes
WasmTrapError(new)A
RuntimeErrorsubclass carrying:stdout/stderr— whatever the program wrote before the trap, previously discarded as the exception unwound out ofexecute()kind— stable identifier:divide_by_zero/out_of_bounds/stack_exhausted/unreachable/overflow/contract_violation/unknownLiskov-substitutable: every existing
except RuntimeErrorblock still catches the trap and sees a sensible string. No call-site changes needed in older code._classify_trap(new)Pure function. Maps
(wasmtime exc, last_violation)to(kind, message). Contract-violation host-import channel always wins over the wasmtime trap reason — the host import has a more specific Vera-native message. Otherwise the wasmtime exception'sstr()is matched against thewasm trap: <reason>substring and routed to one of the documentedkinds. Stringly-typed against wasmtime's error format, which keeps the classifier free of a wasmtime import — handy for tests and the eventual browser-runtime branch.cmd_runhandlerNew
except WasmTrapErrorblock beforeexcept RuntimeError:stdout/stderrto the corresponding actual streams before the error, with an explicitsys.stdout.flush()so2>&1redirects show the right merged-stream order. Thenf"Error: {exc}"to stderr.stdoutandstderrgo into the envelope; the diagnostic gains atrap_kindfield. Streams are NOT touched in JSON mode (would corrupt parsed output).Folded in: PR #532 (mobile CSS fixes)
Cherry-picked the single commit from
mobile-overflow-fixessince it's a CSS-onlydocs/index.htmledit with no overlap with the trap work. Three iOS Safari overflow bugs at iPhone widths fixed: hero image (width:100%missing), VeraBench button (mobile.btnrule was leaking outside.cta-bar), and Runs section shell-command code blocks (white-space:prewith no wrap rule). Authorship preserved; #532 closed with a comment cross-referencing this PR.Test plan
tests/test_runtime_traps.py— classifier per-kind,WasmTrapErrorshape, end-to-endcmd_runfor both text and JSON modes/tmp/buffer_test.verafrom IO.print output lost when program traps (stdout fully buffered) #522) — all fourIO.printlines now appear before the error message under2>&1safe_div(@Int, @Int -> @Int)with second arg 0) still produces "Precondition violation in safe_div..." in text mode andtrap_kind: contract_violationin JSON modeDoc updates
pyproject.toml,vera/__init__.py,docs/index.html,README.mdKNOWN_ISSUES.md: removed IO.print output lost when program traps (stdout fully buffered) #522 row (closed); rewrote Runtime traps need Vera-native diagnostics, not raw wasmtime stack traces #516 row to "Stage 1 done, Stages 2–3 remain"ROADMAP.md: row 3 now#516 (Stages 2–3)instead of paired-with-IO.print output lost when program traps (stdout fully buffered) #522; intro updated from "twelve open" to "eleven remain"HISTORY.md: v0.0.120 row added to Stage 9 tableCHANGELOG.md:[Unreleased]content moved to[0.0.120] - 2026-04-26; new empty[Unreleased]block on topTESTING.md: total counts 3534→3550, file count 28→29, new row fortest_runtime_traps.pyWhat this does NOT do
kindFix:paragraphs) of Runtime traps need Vera-native diagnostics, not raw wasmtime stack traces #516 — both remain open. Stage 2 needs debug-info plumbing through codegen, which is its own piece of work.$allocgrows memory by only 1 page — large single allocations trap #487, GC worklist overflow for deeply nested object graphs #348, Opaque handle memory leak in host stores #346, GC shadow stack pollution from opaque handle parameters #347, Over-rooting of host-managed handles in array_fold (and future iterative combinators) #490) — independent, sequential.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
New Features
trap_kindappears in JSON diagnostics.Tests
Documentation
Closes #522