Skip to content

Add IO.sleep, IO.time, IO.stderr (closes #463, v0.0.114)#491

Merged
aallan merged 6 commits into
mainfrom
io-sleep-time-stderr
Apr 18, 2026
Merged

Add IO.sleep, IO.time, IO.stderr (closes #463, v0.0.114)#491
aallan merged 6 commits into
mainfrom
io-sleep-time-stderr

Conversation

@aallan

@aallan aallan commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Summary

Three new operations on the built-in IO effect. Closes #463.

  • IO.sleep(@Nat) -> Unit — pause for N milliseconds
  • IO.time(@Unit) -> @Nat — current Unix time in milliseconds
  • IO.stderr(@String) -> Unit — write to stderr separate from stdout

Enables animation loops, frame-budget computation, elapsed-time measurement, and stderr-separated CLI tools. Discovered missing while a model wrote Conway's Game of Life in Vera.

Implementation

  • environment.py: three OpInfo entries added to the built-in IO EffectInfo.
  • codegen/api.py: host_sleep (→ time.sleep), host_time (→ int(time.time() * 1000), i64), host_stderr (→ sys.stderr or optional in-memory buffer). execute() gains capture_stderr: bool = False; ExecuteResult gains stderr: str field (empty by default to preserve shape).
  • codegen/assembly.py: three new _IO_IMPORTS entries.
  • wasm/context.py: _is_void_expr now treats IO.sleep and IO.stderr as void (like IO.print), so trailing statements don't emit a spurious drop.
  • browser/runtime.mjs: hostSleep busy-waits on performance.now() (Atomics.wait unavailable on the main thread), hostTime = BigInt(Date.now()), hostStderr captures to a buffer exposed via getStderr()/clearStderr(). reset() includes stderrBuf.
  • browser/harness.mjs: emits stderr alongside stdout in the JSON output.

Tests

  • tests/conformance/ch07_io_time_stderr.vera — time + stderr together; level=run. (Sleep excluded: wall-clock timing is inherently flaky — covered in unit tests instead.)
  • tests/test_codegen.py — 5 new tests.
  • tests/test_browser.py — 3 new tests.

Documentation

  • SKILL.md: IO table now lists 10 operations (was 7); browser-runtime paragraph extended.
  • spec/12-runtime.md: three host-import rows, three new §12.4.1.x subsections (sleep/time/stderr), browser comparison table extended.
  • CHANGELOG.md: v0.0.114 section cut.
  • Doc-count sweep touches README / FAQ / AGENTS / TESTING / ROADMAP to reconcile 73→74 conformance, 3,338→3,351 tests, and test-file line/count drift.

Also bundled

The ROADMAP → HISTORY shuffle for #480, which should have been in #489 but was missed:

Version bump

v0.0.113 → v0.0.114. Tagged on branch; per the release workflow the tag will move to main HEAD after merge and a GitHub release will follow.

Test plan

  • mypy vera/ clean
  • pytest tests/ -q — 3,340 passed, 11 skipped (8 new)
  • All pre-commit hooks green
  • vera run against a manual sleep+time+stderr program

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added IO operations: sleep, time and stderr; stderr can be captured separately by runtimes and API responses.
  • Bug Fixes

    • Fixed GC header size handling to prevent corruption of large allocations (≥65,536 bytes).
  • Performance Improvements

    • Array operations moved to iterative WebAssembly implementations to reduce shadow-stack usage.
  • Tests

    • Conformance suite expanded to 75 programs with new IO-focused tests.
  • Chores

    • Release/version bump and added pre-push lockfile sync check.

Three new operations on the built-in IO effect:

- IO.sleep(@nat) -> Unit            # pause for N milliseconds
- IO.time(@Unit) -> @nat            # current Unix time in ms
- IO.stderr(@string) -> Unit        # write to stderr

Implementation trail:

- environment.py: three OpInfo entries on the IO EffectInfo.
- codegen/api.py: host_sleep (time.sleep), host_time (time.time *
  1000, i64), host_stderr (writes to sys.stderr or optional capture
  buffer).
- ExecuteResult gains a `stderr: str` field (empty by default to
  preserve the pre-#463 shape).  execute() gains a `capture_stderr:
  bool = False` parameter so tests can opt in without leaking to the
  real stderr.
- codegen/assembly.py: three _IO_IMPORTS entries (sleep param i64,
  time result i64, stderr param i32 i32).
- wasm/context.py: _is_void_expr now treats IO.sleep and IO.stderr
  as void like IO.print, so trailing statements don't emit a
  spurious `drop`.
- browser/runtime.mjs: hostSleep busy-waits on performance.now
  (Atomics.wait unavailable on main thread; long sleeps block
  rendering), hostTime = Date.now() as BigInt, hostStderr captures
  to stderrBuf with getStderr()/clearStderr() accessors.  Reset
  path includes stderrBuf.
- browser/harness.mjs: emits `stderr` alongside `stdout` in the JSON
  output so test_browser.py can assert on captured stderr.

Tests:

- tests/conformance/ch07_io_time_stderr.vera — exercises time +
  stderr together; level=run.  (Sleep excluded — wall-clock timing
  is inherently flaky; covered by unit tests instead.)
- tests/test_codegen.py: 5 new tests (time returns positive Nat,
  sleep completes, sleep(0) no-op, stderr captured when requested,
  stderr empty by default).
- tests/test_browser.py: 3 new tests (stderr capture, time past
  epoch, sleep completes).

Documentation:

- SKILL.md: IO table updated from 7 ops to 10; browser-runtime
  paragraph extended to cover the new ops.
- spec/12-runtime.md: three new host-import rows, three new
  §12.4.1.x subsections (sleep/time/stderr), browser comparison
  table extended.
- CHANGELOG.md: v0.0.114 section with the full write-up.
- Doc-count sweep: 73 → 74 conformance, 3,338 → 3,351 tests,
  test_browser/test_codegen/test_codegen_monomorphize line counts,
  test count references in README/FAQ/AGENTS/TESTING/ROADMAP.

Also bundled in this PR (ROADMAP → HISTORY shuffle for #480, which
should have been in #489 but was missed):

- HISTORY.md gains three Stage 11 entries: #484 widen header, #480
  iterative combinators, and the v0.0.114 #463 entry.
- ROADMAP.md removes #480 row from "What's next", removes the #463
  standalone bullet from Phase 4a, adjusts the reshape paragraph
  and capability-expansion examples list to match.

Version bump: v0.0.113 → v0.0.114 in pyproject.toml,
vera/__init__.py, docs/index.html, README.md.

Co-Authored-By: Claude <noreply@anthropic.invalid>
@codecov

codecov Bot commented Apr 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.50649% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.28%. Comparing base (62abd51) to head (6aea03b).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
vera/browser/runtime.mjs 89.13% 5 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #491   +/-   ##
=======================================
  Coverage   90.27%   90.28%           
=======================================
  Files          58       58           
  Lines       19980    20052   +72     
  Branches      225      228    +3     
=======================================
+ Hits        18037    18104   +67     
- Misses       1939     1944    +5     
  Partials        4        4           
Flag Coverage Δ
javascript 51.51% <90.00%> (+0.84%) ⬆️
python 95.07% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ccbde8b5-62b9-497d-9170-fd1856a7900c

📥 Commits

Reviewing files that changed from the base of the PR and between e63f72d and 6aea03b.

📒 Files selected for processing (1)
  • scripts/check_doc_counts.py

📝 Walkthrough

Walkthrough

Adds IO.sleep, IO.time and IO.stderr end-to-end: environment registration, WAT import signatures, Python/browser host implementations with optional stderr capture, tests and conformance entries, docs/ROADMAP/CHANGELOG/HISTORY updates, pre-push hook addition, and version bump to 0.0.114.

Changes

Cohort / File(s) Summary
Version metadata
vera/__init__.py, pyproject.toml, README.md
Bumped project/package version to 0.0.114 and updated release/test metrics.
Docs & project status
AGENTS.md, CLAUDE.md, FAQ.md, CHANGELOG.md, HISTORY.md, ROADMAP.md, TESTING.md, SKILL.md, CONTRIBUTING.md
Updated conformance program count (73→75), added 0.0.114 changelog/HISTORY entries (IO ops, GC header fix, array iteration change), adjusted roadmap and CI/hook descriptions, and expanded IO documentation.
Spec / Runtime docs
spec/12-runtime.md
Formalised host imports for vera.sleep, vera.time, vera.stderr; documented execute(capture_stderr=True) and ExecuteResult.stderr; updated browser adaptation notes and public API signatures.
Effect registration / types
vera/environment.py
Registered IO.sleep(NAT)->UNIT, IO.time(UNIT)->NAT, IO.stderr(STRING)->UNIT.
Codegen & WASM imports
vera/codegen/assembly.py, vera/wasm/context.py
Added WAT import signatures for sleep, time, stderr; treated IO.sleep and IO.stderr as void expressions in wasm context.
Execution API & stderr plumbing
vera/codegen/api.py
Added capture_stderr: bool=False to execute(), introduced ExecuteResult.stderr: str, wired stderr capture through normal/exit/trap paths, and registered host imports for sleep/time.
Browser runtime & harness
vera/browser/runtime.mjs, vera/browser/harness.mjs
Implemented browser-side sleep (busy-wait), time (Date.now() → BigInt), and stderr buffering; added getStderr()/clearStderr() and included stderr in harness JSON output.
Tests & conformance
tests/conformance/manifest.json, tests/test_codegen.py, tests/test_browser.py
Added two conformance entries (ch07_io_time_stderr, ch07_io_sleep) and unit/browser tests for IO.time, IO.sleep (zero/non-zero) and IO.stderr capture behaviour.
Tooling / allowlist & hooks
scripts/check_skill_examples.py, scripts/check_doc_counts.py, .pre-commit-config.yaml, CONTRIBUTING.md
Shifted SKILL allowlist line numbers; relaxed docs regex; added uv-lock-check pre-push hook and updated related docs and counts.
Browser & runtime codegen
vera/browser/..., vera/codegen/...
Added runtime buffering APIs and exports (getStderr, clearStderr), updated WAT IO import registry and host binding signatures.

Sequence Diagram(s)

sequenceDiagram
    participant Test as rgba(31,119,180,0.5) Test/Harness
    participant Execute as rgba(44,160,44,0.5) Execute()
    participant WASM as rgba(214,39,40,0.5) Compiled WASM
    participant Host as rgba(148,103,189,0.5) Host bindings
    participant Runtime as rgba(140,86,75,0.5) Python/Browser runtime

    Test->>Execute: call execute(capture_stderr=bool)
    Execute->>WASM: instantiate & call entry
    WASM->>Host: call import vera.time / vera.sleep / vera.stderr
    Host->>Runtime: dispatch to Python or Browser implementation
    Runtime-->>Host: return value / append stderr
    Host-->>WASM: return to module
    WASM-->>Execute: normal return or trap
    Execute-->>Test: return ExecuteResult(stdout, stderr, state)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

compiler, tests, spec, ci, docs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarises the PR's primary change: adding three new IO operations (sleep, time, stderr) and bumping to v0.0.114.
Linked Issues check ✅ Passed All requirements from #463 are met: IO.sleep, IO.time, and IO.stderr operations implemented in environment.py, codegen/api.py, browser runtime, tests, and documentation.
Out of Scope Changes check ✅ Passed Changes are tightly scoped to #463. The pre-commit hook addition (uv-lock-check) and doc-count regex widening are follow-up housekeeping naturally bundled with the release; no unrelated feature work is present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch io-sleep-time-stderr

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
FAQ.md (1)

206-210: ⚠️ Potential issue | 🟡 Minor

IO operations list is now outdated in project status.

After adding IO.sleep, IO.time, and IO.stderr, the bullet at Line 210 still lists only the older IO set. Please expand that list to keep the FAQ accurate.

As per coding guidelines: "**/*.md: Review Markdown files for factual accuracy against the codebase, broken links, and outdated information."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@FAQ.md` around lines 206 - 210, Update the IO operations bullet to reflect
the current API by adding the new IO functions IO.sleep, IO.time, and IO.stderr
to the existing list (which currently mentions print, read_line, read_file,
write_file, args, exit, get_env); locate the FAQ.md bullet that enumerates IO
operations and expand it to include IO.sleep, IO.time, and IO.stderr so the
documentation matches the codebase.
🤖 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`:
- Around line 34-45: Remove the shipped issue references from ROADMAP.md by
deleting mentions of `#463` and `#480` (and any retrospective phrasing around them)
so the roadmap only lists future work; then add one-line entries for each
shipped item to the appropriate Stage 9 row in HISTORY.md (e.g., "Closed `#463` —
IO.sleep/time/stderr" and "Closed `#480` — prerequisite structural refactor")
ensuring the HISTORY.md entry format matches existing Stage 9 table rows and
that surrounding ROADMAP.md prose remains grammatically correct after removal.

In `@spec/12-runtime.md`:
- Around line 218-254: Update the count of IO host function bindings in section
12.4.1 to reflect the three new ops (IO.sleep, IO.time, IO.stderr) so the text
that currently reads "seven host function bindings" now says "ten host function
bindings" (or equivalent) and ensure any nearby summary/listing references match
the definitions of 12.4.1.1 through 12.4.1.10.

In `@TESTING.md`:
- Line 58: Update the IO coverage line in TESTING.md that references
`test_codegen.py` to include the new IO operations by adding "sleep, time,
stderr" to the existing list of covered IO ops; find the table row mentioning
`test_codegen.py` and append these three symbols to the IO section so the
documentation matches the current codebase.

In `@tests/conformance/manifest.json`:
- Around line 565-577: Add a non-runtime conformance case for IO.sleep by adding
a new manifest entry similar to "ch07_io_time_stderr" but with a unique id
(e.g., "ch07_io_sleep"), file "ch07_io_sleep.vera", chapter 7, title mentioning
IO.sleep, level set to "check" or "verify", spec_ref "Section 7.2", and features
including "io_effect" and "io_sleep"; also create the corresponding
tests/conformance/ch07_io_sleep.vera file (simple deterministic check for
IO.sleep behavior) so the manifest references an actual test program.

In `@tests/test_codegen.py`:
- Around line 7186-7204: The test test_io_stderr_default_not_captured currently
only asserts exec_result.stderr == "" but needs to also assert that stdout
remains empty to ensure no cross-stream leakage; update the
test_io_stderr_default_not_captured function to include an explicit assertion on
exec_result.stdout (e.g., assert exec_result.stdout == "") immediately after the
stderr assertion so both streams are validated when capture_stderr is not
enabled.
- Around line 7127-7128: The test uses a hard-coded epoch threshold to validate
IO.time which can fail on skewed/frozen clocks; change the assertion to capture
host time immediately before and/or after running the command and assert
int(exec_result.stdout) falls between those captured host timestamps (e.g.,
record host_time_before and host_time_after around the execution and assert
host_time_before <= int(exec_result.stdout) <= host_time_after) so the check
compares against the actual host clock rather than a fixed epoch constant.

In `@vera/browser/harness.mjs`:
- Around line 89-92: Update the example/documentation comment that describes the
emitted JSON to reflect the new shape which now includes stderr; locate the
comment next to the output object construction (variable output) where
getStdout(), getStderr(), and getState() are used and modify the example JSON to
include a stderr field alongside stdout and state (and remove any stale fields),
so the documented schema matches the actual emitted object.

In `@vera/codegen/api.py`:
- Around line 104-108: The ExecuteResult dataclass field order was changed
causing the public positional constructor to shift; restore the original
positional shape by reordering the fields so they are defined as value, stdout,
state, exit_code (and keep stderr as a defaulted field after state or keep it as
a keyword-only with a default) — update the ExecuteResult declaration (the
dataclass and its field definitions: value, stdout, state, stderr, exit_code) so
state appears before stderr and stderr retains its default "" to avoid breaking
external positional call sites.

---

Outside diff comments:
In `@FAQ.md`:
- Around line 206-210: Update the IO operations bullet to reflect the current
API by adding the new IO functions IO.sleep, IO.time, and IO.stderr to the
existing list (which currently mentions print, read_line, read_file, write_file,
args, exit, get_env); locate the FAQ.md bullet that enumerates IO operations and
expand it to include IO.sleep, IO.time, and IO.stderr so the documentation
matches the codebase.
🪄 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: 4dedca6d-4f11-435c-9e9f-f8fab5ad5273

📥 Commits

Reviewing files that changed from the base of the PR and between 62abd51 and 88493b1.

⛔ Files ignored due to path filters (6)
  • docs/SKILL.md is excluded by !docs/**
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • tests/conformance/ch07_io_time_stderr.vera is excluded by !**/*.vera
📒 Files selected for processing (22)
  • AGENTS.md
  • CHANGELOG.md
  • CLAUDE.md
  • FAQ.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_skill_examples.py
  • spec/12-runtime.md
  • tests/conformance/manifest.json
  • tests/test_browser.py
  • tests/test_codegen.py
  • vera/__init__.py
  • vera/browser/harness.mjs
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/environment.py
  • vera/wasm/context.py

Comment thread ROADMAP.md Outdated
Comment thread spec/12-runtime.md
Comment thread TESTING.md Outdated
Comment thread tests/conformance/manifest.json
Comment thread tests/test_codegen.py Outdated
Comment thread tests/test_codegen.py
Comment thread vera/browser/harness.mjs
Comment thread vera/codegen/api.py
Nine findings; eight fixed, one already handled by the PR itself.

Fixed:
- ExecuteResult field order restored: `stderr` moved to the end of
  the dataclass (after `exit_code`) so the public positional
  constructor shape — `(value, stdout, state, exit_code)` — stays
  backwards-compatible for external callers.  Default `""`
  preserves the pre-#463 behaviour.
- spec/12-runtime.md: "seven host function bindings" → "ten";
  harness registration example extended to include `vera.sleep`,
  `vera.time`, `vera.stderr`.
- TESTING.md: test_codegen.py IO coverage description extended to
  include `sleep, time, stderr`.
- tests/conformance/ch07_io_sleep.vera added at level `verify` —
  exercises the type/contract path for IO.sleep without depending
  on wall-clock timing.  Unique manifest entry; spec §7.2 ref.
- test_io_stderr_default_not_captured: added `assert
  exec_result.stdout == ""` — confirms no cross-stream leakage
  when a program calls only IO.stderr.
- test_io_time_returns_positive_nat: replaced the hardcoded
  `> 1_700_000_000_000` threshold with a bracketed comparison
  against `time.time()` captured before/after execution.  Stable
  on hosts with skewed or frozen clocks.
- vera/browser/harness.mjs: header comment now describes the
  emitted JSON shape with all current fields including `stderr`.
- ROADMAP.md: retrospective mentions of `#463` and `#480` removed
  from the reshape paragraph and the Phase 4a context; standalone
  references replaced with generic "iterative WASM pattern shared
  by array_map/array_filter/array_fold".  ROADMAP now lists only
  future work, with completed items in HISTORY.md.
- FAQ.md: IO operations bullet expanded to include `sleep, time,
  stderr`.

Also bundled:
- uv.lock regenerated after the v0.0.113 → v0.0.114 bump (unblocks
  the CI lint job that was failing on `uv lock --check`).

Doc-count sweep reconciles the new ch07_io_sleep.vera addition:
74 → 75 conformance programs, 3,351 → 3,356 tests, and drift in
test_codegen.py line count.

Co-Authored-By: Claude <noreply@anthropic.invalid>
@aallan

aallan commented Apr 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Fixed in c90c7c3 — the FAQ's "Full IO operations" bullet now reads: (print, read_line, read_file, write_file, args, exit, get_env, sleep, time, stderr).

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@aallan: Confirmed — the updated bullet correctly enumerates all 10 IO operations, including the three new additions (sleep, time, stderr). Thanks for the fix!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 `@spec/12-runtime.md`:
- Around line 572-574: Update the browser public API snippet in Section 12.9.2
so it matches the runtime and Section 12.9.3 by adding the stderr helpers to the
exported/imported names; specifically include getStderr alongside getStdout,
getState, and resetState (referencing the getStderr symbol) so the example shows
the same public API surface as documented elsewhere. Ensure the snippet
imports/exports getStderr in the same style as the other helpers and that any
example usage reflects reading the stderr capture via getStderr().
- Around line 42-44: Section 12.2.3 currently overstates that any IO operation
other than `print` and `exit` requires `$alloc`; update the paragraph to state
that only IO imports which allocate or return heap data require `$alloc`, and
explicitly note that `vera.sleep`, `vera.time`, and `vera.stderr` (which map to
`IO.sleep`, `IO.time`, `IO.stderr`) do not allocate or return heap data and
therefore do not require `$alloc`; reference the `$alloc` symbol and the IO
import names (`vera.sleep`, `vera.time`, `vera.stderr`, `IO.sleep`, `IO.time`,
`IO.stderr`) and adjust the example/contract text in section 12.2.3 accordingly
so the claim matches these cases.

In `@TESTING.md`:
- Line 9: Summary: The aggregate test count in TESTING.md is inconsistent with
the component counts; update either the total "3,356" or the component numbers
"3,344 passed, 11 skipped" so they sum correctly. Fix: open TESTING.md, locate
the table row containing "3,356 across 28 files (~34,000 lines of test code;
3,344 passed, 11 skipped)" and adjust the numeric values so the sum matches
(change the total to 3,355 or modify the passed/skipped counts to sum to 3,356),
and then commit the corrected markdown.
- Around line 109-110: The TESTING.md prose mismatches the table: after raising
the `verify` count to 4 the paragraph still states “Three programs” and lists
only three files; update the paragraph under the `verify`/`run` table to reflect
four programs by either adding the missing fourth program to the list or
changing the sentence to the correct count, and ensure the listed filenames
match the actual verify-level test files referenced by the `verify` table entry.
- Line 76: The table row for `test_conformance.py` lists 375 tests but the "Via
pytest" example still says `365 tests`; update the "Via pytest" example string
(the text containing "Via pytest" and "365 tests") to `375 tests` so both
references match, and scan TESTING.md for any other occurrences of `365 tests`
or the conformance total and make them consistent with `375` (reference:
`test_conformance.py` and the "Via pytest" example text).
🪄 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: 0a20afc7-9111-494d-b99d-7fde89c6f3fe

📥 Commits

Reviewing files that changed from the base of the PR and between 88493b1 and c90c7c3.

⛔ Files ignored due to path filters (5)
  • docs/SKILL.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • tests/conformance/ch07_io_sleep.vera is excluded by !**/*.vera
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (12)
  • AGENTS.md
  • CLAUDE.md
  • FAQ.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • spec/12-runtime.md
  • tests/conformance/manifest.json
  • tests/test_codegen.py
  • vera/browser/harness.mjs
  • vera/codegen/api.py

Comment thread spec/12-runtime.md
Comment thread spec/12-runtime.md
Comment thread spec/12-runtime.md
Comment thread TESTING.md Outdated
Comment thread TESTING.md
Comment thread TESTING.md
aallan and others added 2 commits April 17, 2026 22:24
Six new findings, all addressed:

spec/12-runtime.md:
- §12.9.2 Public API snippet now imports and uses `getStderr`
  alongside `getStdout`, matching §12.9.3 and the runtime's
  actual exports.
- §12.2.3's `$alloc` requirement paragraph no longer over-claims
  "any IO operation other than print and exit" — sleep, time, and
  stderr also don't allocate.  Revised wording enumerates the
  five operations that do require `$alloc`
  (read_line / read_file / write_file / args / get_env) and names
  the five that don't.

TESTING.md:
- Summary row arithmetic fixed: 3,344 passed + 12 skipped = 3,356
  (was `11 skipped`, off by one).
- Prose at §Conformance Levels updated: "Four programs at verify
  level" (was "Three") with `ch07_io_sleep` added to the list.
- "Via pytest (parametrized — 365 tests)" → "375 tests" to match
  the `test_conformance.py` row in the main table.
- Hook count 24 → 25 (both prose and table) after adding the new
  pre-push hook below.

Hygiene bump: add `uv-lock-check` pre-push hook (local preventative
for the CI lint failure this PR itself hit).  CI already runs
`uv lock --check` in the lint job; adding the same check locally
at pre-push catches version-bump lockfile staleness before the
push goes out.  Mirrors the pattern established by #478's
CHANGELOG-at-pre-push hook.  Also documented in CONTRIBUTING.md
with its own subsection.

Co-Authored-By: Claude <noreply@anthropic.invalid>
§12.6.1's pipeline summary now reads `ExecuteResult (value +
stdout + stderr + state)` (was missing stderr).  §12.6.4 retitled
"Stdout and Stderr Capture" and extended with a paragraph
explaining the opt-in nature of capture: `IO.stderr` writes go to
the host's real stderr by default, and `execute(capture_stderr=
True)` routes them into `ExecuteResult.stderr` for test
inspection.  When the flag is False, `ExecuteResult.stderr` is an
empty string — preserves the pre-#463 result shape for
backward compatibility.

Also regenerated site assets (docs/llms.txt etc.) so they mirror
the updated spec.

Co-Authored-By: Claude <noreply@anthropic.invalid>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 `@CONTRIBUTING.md`:
- Line 88: The sentence claiming "23 run on every commit after `pre-commit
install`" is inaccurate because many commit-stage hooks are file/type-gated in
`.pre-commit-config.yaml`; update the wording to say "23 commit-stage hooks" (or
equivalent) and clarify that some of those run only when matching files are
staged, keep the note that the two push-stage hooks are
`check-changelog-updated` and `uv-lock-check`, and ensure the Markdown review
rule `**/*.md` remains consistent with the actual gating behavior described.

In `@TESTING.md`:
- Line 9: The summary line under the "Tests" table showing "12 skipped"
conflicts with the "Skipped-tests" breakdown that lists and enumerates 11 cases;
update the markdown so both places report the same skipped count: either add the
missing skipped case to the "Skipped-tests" section or change the table summary
from "12 skipped" to "11 skipped" (ensure the table row labeled "**Tests**" and
the "Skipped-tests" enumeration match exactly), and run a quick grep of the
enumerated case headings to confirm all skipped items are present and correctly
numbered.
- Line 377: The statement claiming "23 run on every commit" is inaccurate
because many hooks use trigger filters (`files`/`types`) and only run when those
filters match; update the sentence that starts "Every push is checked by 25
hooks..." to say that there are 25 configured hooks (23 that are configured to
run on commits but are subject to per-hook `files`/`types` filters, and
2—`check-changelog-updated`, `uv-lock-check`—that run only at push time with
`--hook-type pre-push`), and make the same clarification where the numeric claim
appears again (the other occurrence referencing 406) so the README reflects
trigger-based execution rather than "every commit".
🪄 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: c389ac76-2e35-4794-aba8-b5784567a3f4

📥 Commits

Reviewing files that changed from the base of the PR and between c90c7c3 and d8e8a4f.

📒 Files selected for processing (4)
  • .pre-commit-config.yaml
  • CONTRIBUTING.md
  • TESTING.md
  • spec/12-runtime.md

Comment thread CONTRIBUTING.md Outdated
Comment thread TESTING.md
Comment thread TESTING.md Outdated
Three CodeRabbit findings:

1. CONTRIBUTING.md line 88 / TESTING.md line 377 both claimed
   "23 run on every commit after `pre-commit install`" — inaccurate
   because most commit-stage hooks use per-hook `files:` / `types:`
   filters (grep of `.pre-commit-config.yaml` finds ten such
   filters).  Revised wording: "23 are configured at the commit
   stage … Many commit-stage hooks use per-hook `files:` / `types:`
   filters and only fire when matching files are staged — a docs-
   only commit triggers a small subset, a compiler-level commit
   triggers most."

2. TESTING.md Skipped-tests enumeration was stale: summary said
   12 skipped, table listed only 11.  The missing row is
   `test_run[ch07_io_sleep]` — the verify-level conformance test
   added earlier in this PR auto-skips its `run` stage, same
   pattern as the other verify-level programs.  Added the row and
   updated the intro sentence "reports 12 skipped tests".

3. `scripts/check_doc_counts.py` regex widened to accept
   "checked by N configured hooks" as well as "checked by N hooks",
   so the new wording stays enforceable.

Co-Authored-By: Claude <noreply@anthropic.invalid>
@aallan

aallan commented Apr 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Three follow-up findings addressed in e63f72d:

CONTRIBUTING.md line 88 and TESTING.md line 377 — both claimed "23 run on every commit", which is inaccurate. Many commit-stage hooks use per-hook files: / types: filters (grep of .pre-commit-config.yaml finds ten such filters). Revised wording: "23 are configured at the commit stage … Many commit-stage hooks use per-hook files: / types: filters and only fire when matching files are staged — a docs-only commit triggers a small subset, a compiler-level commit triggers most."

TESTING.md line 9 — summary-vs-enumeration mismatch (12 skipped in the table, 11 rows in §Skipped-tests). The missing 12th skip is test_run[ch07_io_sleep] — my verify-level conformance addition in this PR auto-skips its run stage, same pattern as the other verify-level programs. Added the row to the level-limited skips table and updated the intro sentence to 12 skipped.

Also widened the regex in scripts/check_doc_counts.py so the new "N configured hooks" wording stays enforceable (accepts both "N hooks" and "N configured hooks").

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check CONTRIBUTING.md around line 88
echo "=== CONTRIBUTING.md lines 82-95 ==="
sed -n '82,95p' CONTRIBUTING.md

echo ""
echo "=== TESTING.md lines 1-20 (summary table) ==="
sed -n '1,20p' TESTING.md

echo ""
echo "=== TESTING.md lines 370-390 (hook wording) ==="
sed -n '370,390p' TESTING.md

echo ""
echo "=== scripts/check_doc_counts.py hooks regex ==="
grep -n "hooks\|configured" scripts/check_doc_counts.py | head -30

echo ""
echo "=== TESTING.md skipped tests section ==="
grep -n "ch07_io_sleep\|skipped\|level.limited\|test_run" TESTING.md | head -30

Length of output: 6669


🏁 Script executed:

#!/bin/bash
echo "=== check_doc_counts.py lines 235-255 (CONTRIBUTING.md hooks check) ==="
sed -n '235,255p' scripts/check_doc_counts.py

Length of output: 798


@aallan: All three fixes look correct — the TESTING.md table now has the test_run[ch07_io_sleep] row, the 12 skipped summary matches, and both docs use the qualified filter wording. However there is one remaining gap.

The check_doc_counts.py regex was widened for TESTING.md (line 199, (?:configured )? added), but the corresponding check for CONTRIBUTING.md at line 241 still uses the old narrow pattern:

# line 241 — still the old pattern
m = re.search(r"checked by (\d+) hooks", contrib_md)

CONTRIBUTING.md now reads "checked by 25 configured hooks", which does not match r"checked by (\d+) hooks". Because the check is guarded by if m:, a non-match silently skips the validation entirely — the hook-count enforcement for CONTRIBUTING.md is now a no-op.

The fix is one character group, mirroring line 199:

m = re.search(r"checked by (\d+) (?:configured )?hooks", contrib_md)

🧠 Learnings used
Learnt from: aallan
Repo: aallan/vera PR: 415
File: TESTING.md:402-413
Timestamp: 2026-03-29T11:10:02.330Z
Learning: In `aallan/vera` `TESTING.md`, the CI Pipeline section states "seven parallel jobs" which refers to the 7 top-level YAML job keys in `.github/workflows/ci.yml` (test, typecheck, lint, security, dependency-audit, sbom, browser-parity). The CI table has 8 rows because the single `test` job is intentionally split into two documentation rows (plain matrix row and a coverage row) for clarity. `check_doc_counts.py` counts and validates 7 YAML job keys. Do NOT flag "seven" as inconsistent with the 8-row table — they are measuring different things by design.

Learnt from: CR
Repo: aallan/vera PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-09T17:55:24.158Z
Learning: Pre-commit hooks run mypy type checking on the compiler, pytest test suite, conformance suite validation, and example validation on every commit.

Learnt from: CR
Repo: aallan/vera PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2026-04-16T21:55:02.926Z
Learning: Update relevant documentation and tests in pull requests

Learnt from: CR
Repo: aallan/vera PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-10T16:09:54.506Z
Learning: Pre-commit hooks must run mypy, pytest, trailing whitespace checks, and validate all examples

Learnt from: aallan
Repo: aallan/vera PR: 491
File: tests/test_codegen.py:0-0
Timestamp: 2026-04-17T21:10:46.688Z
Learning: Repo aallan/vera: For IO.time tests (e.g., tests/test_codegen.py::TestIOOperations::test_io_time_returns_positive_nat), avoid fixed epoch thresholds. Capture host time immediately before and after execute(...), and assert before_ms <= observed_ms <= after_ms to validate wiring without flakiness.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 `@scripts/check_doc_counts.py`:
- Line 199: The CONTRIBUTING.md hook-count regex is stricter than the TESTING.md
one; update the parsing so both accept the optional "configured " token. Replace
the existing literal pattern used for CONTRIBUTING.md (the one matching "checked
by (\d+) hooks") with the same flexible pattern r"checked by (\d+) (?:configured
)?hooks" (or factor both into a shared constant) so counts like "checked by 25
configured hooks" are recognized; ensure the change is applied where
CONTRIBUTING.md is parsed in scripts/check_doc_counts.py.
🪄 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: 93a164ae-c1e5-4478-b262-8a320dec2d0e

📥 Commits

Reviewing files that changed from the base of the PR and between d8e8a4f and e63f72d.

📒 Files selected for processing (3)
  • CONTRIBUTING.md
  • TESTING.md
  • scripts/check_doc_counts.py

Comment thread scripts/check_doc_counts.py
The previous CodeRabbit round widened the TESTING.md regex to
accept "checked by N configured hooks" but missed the parallel
site for CONTRIBUTING.md (line 241). Both files now contain the
"configured hooks" wording, so the strict regex was silently
failing to match in CONTRIBUTING.md — the `if m:` guard then
skipped the check entirely, which would let stale counts pass
undetected.

Both regexes now read `r"checked by (\d+) (?:configured )?hooks"`
and accept either wording. Verified by manual `re.search` against
the live CONTRIBUTING.md text and by re-running
`scripts/check_doc_counts.py` (still consistent at 25 hooks).

Co-Authored-By: Claude <noreply@anthropic.invalid>
@aallan

aallan commented Apr 18, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Fixed in 6aea03bscripts/check_doc_counts.py line 241 (CONTRIBUTING.md regex) now reads r"checked by (\d+) (?:configured )?hooks", matching the TESTING.md regex from the previous round.

Good catch — verified that without this fix the strict regex didn't match "checked by 25 configured hooks" and the if m: guard silently skipped the assertion entirely. Stale counts would have gone undetected. Both check sites now accept either phrasing and would catch drift in either file.

Kept the patterns inline rather than extracting a shared constant — the script has many similar inline regexes and factoring just this one out would be inconsistent. Happy to revisit if a third site appears.

@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@aallan: Confirmed — the fix is correct. Both check sites now use the (?:configured )? optional group, so neither can silently pass on a wording change. The inline-pattern rationale is sound given the script's existing style; the "revisit if a third site appears" heuristic is a sensible bar.


🧠 Learnings used
Learnt from: aallan
Repo: aallan/vera PR: 415
File: TESTING.md:402-413
Timestamp: 2026-03-29T11:10:02.330Z
Learning: In `aallan/vera` `TESTING.md`, the CI Pipeline section states "seven parallel jobs" which refers to the 7 top-level YAML job keys in `.github/workflows/ci.yml` (test, typecheck, lint, security, dependency-audit, sbom, browser-parity). The CI table has 8 rows because the single `test` job is intentionally split into two documentation rows (plain matrix row and a coverage row) for clarity. `check_doc_counts.py` counts and validates 7 YAML job keys. Do NOT flag "seven" as inconsistent with the 8-row table — they are measuring different things by design.

Learnt from: aallan
Repo: aallan/vera PR: 0
File: :0-0
Timestamp: 2026-03-29T10:15:51.538Z
Learning: In aallan/vera, general housekeeping PRs (CI hardening, documentation, tooling) may acceptably bundle incidental code fixes (e.g. adding `vera --version` CLI support, HTTP Content-Type headers, urlopen timeouts) without requiring them to be split into separate PRs. Do not flag such bundling as a scope concern in this repository.

Learnt from: aallan
Repo: aallan/vera PR: 482
File: vera/README.md:94-94
Timestamp: 2026-04-16T22:33:46.817Z
Learning: In `vera/README.md`, the `wasm/` module table and the `Files:` summary line in the "Code Generation" section both intentionally exclude `__init__.py`. As of PR `#482`, the documented count is 18 modules (19 actual files including `__init__.py` which is 6 lines). Do not flag the difference between documented count and `ls vera/wasm/*.py | wc -l` as an inconsistency — `__init__.py` is always omitted by design.

Learnt from: aallan
Repo: aallan/vera PR: 414
File: codecov.yml:23-26
Timestamp: 2026-03-28T22:44:04.224Z
Learning: In the `codecov.yml` for `aallan/vera`, the Python **project** check uses `target: auto` (compares to base commit) with `threshold: 0.5%` as the hard enforcement floor — this is the real CI gate for coverage regressions. The Python **patch** check is deliberately set to `informational: true` because `coverage.py` does not mark multi-line expression continuation lines as executable; Codecov counts every `+` diff line (including continuation lines) as needing coverage, which systematically under-reports patch coverage on any PR that reformats code into multi-line calls. Enforcing a hard patch target would cause spurious CI failures on every refactor.

Learnt from: aallan
Repo: aallan/vera PR: 408
File: scripts/build_site.py:42-63
Timestamp: 2026-03-28T11:27:11.407Z
Learning: In repo aallan/vera, `scripts/check_site_assets.py` performs a byte-for-byte comparison of committed `docs/llms.txt` and `docs/index.md` against freshly-regenerated output from `build_site.py`. This is the canonical freshness check for generated site assets and is strictly stronger than regex-based count validation in `check_doc_counts.py`. Do not suggest adding redundant count checks to `check_doc_counts.py` for these files.

Learnt from: aallan
Repo: aallan/vera PR: 414
File: CHANGELOG.md:11-11
Timestamp: 2026-03-28T22:37:26.037Z
Learning: In `CHANGELOG.md` of aallan/vera, the `###` sub-section headings (e.g. `### Added`, `### Fixed`, `### Changed`) are written with no blank line between the heading and the immediately-following bullet list. This is the consistent, intentional style across all 20+ such headings in the file. markdownlint MD022 ("below" blank-line variant) is not enforced in CI. Do not flag the absence of a blank line below these headings as a style issue.

Learnt from: aallan
Repo: aallan/vera PR: 491
File: tests/test_codegen.py:0-0
Timestamp: 2026-04-17T21:10:46.688Z
Learning: Repo aallan/vera: For IO.time tests (e.g., tests/test_codegen.py::TestIOOperations::test_io_time_returns_positive_nat), avoid fixed epoch thresholds. Capture host time immediately before and after execute(...), and assert before_ms <= observed_ms <= after_ms to validate wiring without flakiness.

@aallan aallan merged commit b23bd56 into main Apr 18, 2026
19 checks passed
@aallan aallan deleted the io-sleep-time-stderr branch April 18, 2026 07:08
aallan added a commit that referenced this pull request Apr 20, 2026
Seven findings across two reviews; five fixed, one declined, plus
the CI lint failure resolved.

Fixed:
- ROADMAP "Where we are" paragraph now lists Random in the algebraic
  effects enumeration; mirrored in README's "Key features delivered"
  line so both docs are in sync.
- ROADMAP bottom "**by the numbers**" bold string updated from
  "113 tagged releases" to "115 tagged releases (as of v0.0.115)" —
  matches README's v0.0.115 count and makes the datum explicit.
- random_int test seeded with `random.seed(0)` and now asserts
  `low in produced and high in produced` — explicitly verifies the
  inclusive-range contract rather than just the cardinality of the
  produced set.
- random_bool test also seeded; replaces the probabilistic
  `25 <= total <= 75` bound with a deterministic
  `{0, 1}.issubset(observed)` — no more flake risk.
- All four Random tests gain WAT import-gating assertions: each
  asserts the op-specific `$vera.random_*` import IS present, and
  the other two Random imports are NOT — confirms
  `_random_ops_used` gating works op-by-op and catches regressions
  where all three imports would be emitted unconditionally.
- Browser runtime's `random_int` gains a safe-integer range guard:
  if `lowBig < Number.MIN_SAFE_INTEGER` or `highBig > Number
  .MAX_SAFE_INTEGER`, throws a descriptive Error ("bounds exceed
  JavaScript safe integer range…") instead of silently producing
  wrong values via BigInt→Number precision loss.  Also checks
  `highBig >= lowBig` with its own message.

Declined:
- "spec/12-runtime.md line 406: import says random_int, should say
  random_bool" — file already says "random_bool" in both places
  (verified via `git show 0f3049b:spec/12-runtime.md`); CodeRabbit
  misread the diff.  No change needed.

CI lint fix:
- SKILL.md allowlist line-number drift from the Random section
  insertion: 25 entries past the insertion point shifted by +1 or
  +35 (depending on which hunk they fell after).  `fix_allowlists
  .py --fix` failed to detect the drift (same silent-OK bug as on
  #491).  Manually updated all 25 entries; `check_skill_examples
  .py` now reports all 105 Vera blocks parse or are allowlisted.

Co-Authored-By: Claude <noreply@anthropic.invalid>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add IO.sleep, IO.time, and IO.stderr operations

1 participant