Skip to content

Add Json standard library type (#58)#350

Merged
aallan merged 6 commits into
mainfrom
feat/json-type
Mar 25, 2026
Merged

Add Json standard library type (#58)#350
aallan merged 6 commits into
mainfrom
feat/json-type

Conversation

@aallan

@aallan aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • Json ADT: `JNull`, `JBool(Bool)`, `JNumber(Float64)`, `JString(String)`, `JArray(Array)`, `JObject(Map<String, Json>)` — injected via prelude, conditional on usage
  • Host imports: `json_parse(String -> Result<Json, String>)`, `json_stringify(Json -> String)` — Python `json` module / JS `JSON` API
  • Vera utilities: `json_get`, `json_array_get`, `json_array_length`, `json_keys`, `json_has_field`, `json_type` — pure Vera source in prelude using `match` + Map builtins
  • WASM serialization: New `vera/wasm/json_serde.py` — bidirectional Python<->WASM Json ADT marshalling
  • Browser runtime: `writeJson`/`readJson` + host bindings in `runtime.mjs`
  • Conditional injection: Json ADT and utilities only injected when user code references Json (avoids memory/import overhead for non-Json programs)

New files

File Purpose
`vera/wasm/json_serde.py` WASM memory marshalling for Json ADT
`tests/conformance/ch09_json.vera` Conformance test (28 test functions)
`examples/json.vera` Weather API scenario example

Documentation

Closes #58.

Test plan

  • All 2,967 tests pass (33 new: 13 checker + 20 codegen)
  • All 21 pre-commit hooks pass
  • 61 conformance programs pass
  • 27 examples pass check + verify
  • mypy clean
  • CI passes
  • Browser parity tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a built-in Json ADT with parse/stringify, query helpers and browser/runtime host support.
  • Documentation

    • Updated README, spec, roadmap, skill, testing and CHANGELOG to document Json usage, examples, limitations and bumped version to v0.0.98.
  • Tests

    • Added ch09_json conformance example and expanded unit/integration tests; updated expected example/conformance counts.
  • Chore

    • Minor changelog reordering and housekeeping updates.

Json ADT: JNull, JBool, JNumber, JString, JArray, JObject.
Two host imports (json_parse, json_stringify) plus six pure Vera
utility functions injected via prelude. JObject wraps Map<String, Json>.
Conditional injection avoids overhead for non-Json programs.

New conformance test ch09_json (61 programs). New example json.vera.
33 new unit tests. Browser runtime support. Version bump to v0.0.98.

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

codecov Bot commented Mar 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.67532% with 136 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.41%. Comparing base (a774288) to head (8f1c3c5).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
vera/browser/runtime.mjs 11.76% 120 Missing ⚠️
vera/wasm/json_serde.py 92.00% 8 Missing ⚠️
vera/wasm/inference.py 50.00% 4 Missing ⚠️
vera/prelude.py 96.29% 2 Missing ⚠️
vera/wasm/calls.py 91.66% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #350      +/-   ##
==========================================
- Coverage   91.98%   91.41%   -0.57%     
==========================================
  Files          47       48       +1     
  Lines       17950    18333     +383     
  Branches      211      213       +2     
==========================================
+ Hits        16511    16760     +249     
- Misses       1435     1569     +134     
  Partials        4        4              
Flag Coverage Δ
javascript 56.18% <11.76%> (-3.54%) ⬇️
python 95.36% <93.57%> (-0.02%) ⬇️

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 Mar 25, 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
📝 Walkthrough

Walkthrough

Adds a standard-library Json ADT with eight built-ins, prelude injection, WASM host-imports and serde, compiler/codegen + WASM plumbing to track and emit JSON ops, browser runtime bindings, tests and conformance entry, and documentation/script updates for v0.0.98.

Changes

Cohort / File(s) Summary
Docs & Releases
CHANGELOG.md, README.md, ROADMAP.md, SKILL.md, TESTING.md, CLAUDE.md
Documented Json ADT and built-ins, bumped v0.0.98 and example/conformance counts, added README bug notes and roadmap updates.
Specification
spec/09-standard-library.md
Promoted Json to standard-prelude ADT; specified constructors and eight built-ins; updated examples and refinement usage.
Prelude & TypeEnv
vera/prelude.py, vera/environment.py
Injects Json ADT and JSON combinators conditionally; registers JSON builtin signatures; shields injection when user defines non-standard Json.
Compiler Core & Codegen
vera/codegen/...
vera/codegen/api.py, vera/codegen/core.py, vera/codegen/compilability.py, vera/codegen/modules.py, vera/codegen/monomorphize.py, vera/codegen/assembly.py, vera/codegen/functions.py
Tracks json_ops_used through compilation, maps builtin return types, marks modules as needing alloc/memory, emits WAT imports, and propagates tracking on early-exit paths.
WASM Translation & Context
vera/wasm/{context.py,inference.py,calls.py}
Adds _json_ops_used tracking, maps Vera↔WASM types for json_parse/json_stringify, translates calls to host imports and marks alloc requirement.
WASM Serde (Python)
vera/wasm/json_serde.py
New bidirectional serde: write_json/read_json with tag layout, little-endian reads/writes, map-handle allocation and recursive encoding/decoding.
Browser Runtime (JS)
vera/browser/runtime.mjs
Adds writeJson/readJson marshalling, json_parse/json_stringify host imports, and little-endian f64 read/write helpers; uses opaque map handles for objects.
WASM codegen glue
vera/codegen/api.py, vera/codegen/assembly.py
CompileResult gained json_ops_used; conditional host-import blocks and alloc/memory flags added; $alloc export condition extended when JSON ops used.
Tests & Conformance
tests/{test_checker.py,test_codegen.py,test_readme.py,test_verifier.py}, tests/conformance/manifest.json
Added TestJsonChecker and TestJsonCollection, updated verifier totals and README allowlist indices, and added ch09_json conformance entry.
Build Validation Scripts
scripts/{check_readme_examples.py,check_skill_examples.py,check_spec_examples.py}
Adjusted allowlist entries / line numbers for moved/added JSON docs and examples; no control flow changes.

Sequence Diagram(s)

sequenceDiagram
    participant User as User Code
    participant Wasm as WASM Module
    participant Host as Host Import Layer (Py/JS)
    participant Parser as Parser (json.loads / JSON.parse)
    participant Heap as WASM Heap

    User->>Wasm: call exported fn -> invokes json_parse/path
    Wasm->>Host: call imports.vera.json_parse(ptr,len)
    Host->>Heap: read_string(ptr,len)
    Heap-->>Host: UTF-8 bytes
    Host->>Parser: parse string
    alt parse success
        Parser-->>Host: native value (dict/array/...)
        Host->>Host: writeJson(native) -> node_ptr
        Host->>Heap: alloc Result::Ok(node_ptr) -> res_ptr
    else parse failure
        Parser-->>Host: throws/error
        Host->>Heap: alloc Result::Err(err_ptr) -> res_ptr
    end
    Host-->>Wasm: return res_ptr
    Wasm-->>User: Result<Json,String>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

compiler, tests, spec, ci, docs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarises the main change: adding a Json standard library type as specified in issue #58, with clear indication of scope and issue reference.
Linked Issues check ✅ Passed All objectives from issue #58 are met: Json ADT with six constructors (JNull, JBool, JNumber, JString, JArray, JObject), parse/stringify operations (json_parse, json_stringify), pure-Vera utilities (json_get, json_array_get, etc.), integration with standard library Map, and conditional prelude injection.
Out of Scope Changes check ✅ Passed All changes are scoped to Json type implementation: documentation updates (CHANGELOG, ROADMAP, SKILL, README, TESTING), spec updates (09-standard-library.md), type environment and prelude registration, WASM codegen infrastructure, browser/Python host bindings, and conformance/test coverage. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 90.82% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/json-type

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

@aallan

aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

1 similar comment
@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 16

Caution

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

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

206-212: ⚠️ Potential issue | 🟡 Minor

This summary still disagrees with the prose below.

The table says there are 7 Tier 3 contracts out of 146 total, but the next paragraph still says "The 4 remaining Tier 3 contracts". Please update them together.

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

In `@TESTING.md` around lines 206 - 212, The prose and the metrics table disagree:
the table lists "Tier 3 (runtime) | 7 contracts" and "Total | 146", while the
paragraph mentions "The 4 remaining Tier 3 contracts"; update the prose to match
the table (change the phrase "The 4 remaining Tier 3 contracts" to "The 7
remaining Tier 3 contracts") or, if the prose is correct, update the table
values to reflect the accurate counts; ensure the "Tier 3 (runtime)" count and
the total (146) are consistent across both the table and the subsequent
paragraph.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Around line 636-638: Update the three README bug rows to be platform-scoped
and clearer: change the `json_stringify` and `json_parse` entries to indicate
the limitation applies to the Python host (see vera/codegen/api.py which
delegates to the platform JSON module) rather than project-wide, and reword the
`json_get` row to explain that nested JObject returns an opaque handle (not a
deep copy) because JSON values are immutable in the current model; reference the
symbols `json_stringify`, `json_parse`, and `json_get` when editing the table
rows so the scope and immutability rationale are explicit.

In `@spec/09-standard-library.md`:
- Around line 14-15: Remove "Markdown" from the "Future types" bullet so the
overview matches §9.7.3 and existing runtime/tests; specifically edit the
bullets mentioning "Json type" and "Future types" (the lines containing `Json`
and `Markdown`) and either delete the `Markdown` entry or reclassify it as a
current/implemented type per §9.7.3 to keep the spec consistent.
- Line 1535: The paragraph incorrectly describes Json as opaque runtime handles
like Map/Set and claims all JSON functions are host imports; update it to state
that only json_parse and json_stringify are host imports, that query helper
functions are injected Vera functions, and that Json values are ordinary
heap-allocated ADT nodes in WASM memory rather than i32 handles; reference the
symbols json_parse, json_stringify, Json, Map, and Set to clarify the correct
implementation and contrast the real representation with Map/Set handles.

In `@TESTING.md`:
- Around line 9-12: The headline test LOC in the table row "| **Tests** | 2,967
across 26 files (~33,100 lines of test code) |" is stale; update the "~33,100
lines of test code" figure to the current total (~34,013) (or re-run your
line-count script and replace the number with the exact result), and ensure any
other occurrences of the old LOC value in TESTING.md are updated to match.

In `@tests/test_checker.py`:
- Around line 1923-2029: Add tests in TestJsonChecker for the missing built-ins:
create happy-path and wrong-argument tests for json_array_get,
json_array_length, and json_keys following the existing pattern using _check_ok
and _check_err. For each, add a test method (e.g., test_json_array_get /
test_json_array_get_wrong_type, test_json_array_length /
test_json_array_length_wrong_type, test_json_keys / test_json_keys_wrong_type)
that mirrors the style of test_json_array/test_json_object: use a small snippet
that passes correct types to assert _check_ok (e.g., json_array_get(`@Json.0`, 0)
-> `@Option`<Json>, json_array_length(`@Json.0`) -> `@Int`, json_keys(`@Json.0`) ->
`@Array`<String>) and a sibling _check_err case that calls the same builtin with
an incorrect argument type (e.g., String or Int where Json is expected) and
asserts a "type" error; place these methods inside the TestJsonChecker class
alongside the other json tests.

In `@tests/test_codegen.py`:
- Around line 8871-8884: Add a negative-index edge case to the existing
json_array_get OOB test: update or add a test (e.g.,
test_json_array_get_out_of_bounds) that calls json_array_get on a
JArray([JNull]) with index -1 and asserts the result is None (same expected
behavior as the positive OOB case). Locate the test function named
test_json_array_get_out_of_bounds in tests/test_codegen.py and add the extra
assertion or a separate test following the same pattern as the existing positive
OOB check to validate the lower-bound behavior.
- Around line 8643-8695: The tests use string_length(json_type(...)) which only
verifies length and can mask wrong values; update each test (e.g.,
test_json_type_null, test_json_type_bool, test_json_type_number,
test_json_type_string, test_json_type_array, test_json_type_object) to assert
the exact returned JSON type string from json_type (e.g., "null", "bool",
"number", "string", "array", "object") instead of or in addition to asserting
its length — locate occurrences of string_length(json_type(...)) and replace/add
an assertion that the evaluated json_type(...) equals the exact expected string
for the corresponding JNull, JBool, JNumber, JString, JArray, and JObject cases.

In `@vera/browser/runtime.mjs`:
- Around line 1595-1601: The current imports.vera.json_stringify handler calls
JSON.stringify(readJson(ptr)) and passes the result directly to allocString, but
JSON.stringify can return undefined (which would break allocString). Fix
imports.vera.json_stringify by capturing the result of JSON.stringify(value),
checking typeof text === "string" and if not, coerce a safe string (e.g.,
String(value) or "undefined") before calling allocString; reference the
functions readJson, JSON.stringify, allocString and the handler
imports.vera.json_stringify to locate the change.

In `@vera/codegen/assembly.py`:
- Around line 118-120: The current code sets self._needs_alloc and
self._needs_memory when self._json_ops_used is true but the alloc export
decision doesn't consider self._json_ops_used; update the alloc export gating so
that the export of the allocator (the $alloc export decision in the export/build
logic) also checks self._json_ops_used in addition to existing conditions.
Locate the export/alloc gating code in vera/codegen/assembly.py (the logic that
decides whether to export $alloc) and add self._json_ops_used to its predicate
so modules that only use JSON host ops will export $alloc and host-side
marshalling can allocate into WASM memory.

In `@vera/codegen/monomorphize.py`:
- Around line 73-75: The json_parse builtin's return was recorded only as
"Result" causing monomorphiser to drop generic parameter info; update the
_BUILTIN_PARAMETERIZED_RETURNS mapping in monomorphize.py to include a
parameterised return entry for "json_parse" (e.g. the Result generic shape with
its type variables) so _get_arg_type_info can resolve Result<T, E> correctly
during monomorphisation; locate the _BUILTIN_PARAMETERIZED_RETURNS structure and
add a keyed entry for "json_parse" that describes its parameterised return form
consistent with other builtins.

In `@vera/prelude.py`:
- Around line 555-573: The code unconditionally injects JSON combinators when
any JSON name is referenced, which breaks if the user has a custom/shadowed data
Json type; add a standard-shape guard like the Option/Result pattern: implement
a helper (e.g. _has_standard_json(user_data_names, program) or similar) that
verifies the user has the canonical Json constructors (_json_ctors) and/or
standard Json shape, and change the combinator injection to only append
_JSON_COMBINATORS when not json_fn_names.issubset(user_names) AND
_has_standard_json(...) returns true (leave the existing logic that appends
_JSON_DATA when "Json" is absent unchanged).
- Around line 409-424: _source_mentions_json currently only examines ast.FnDecl
nodes and only looks for a limited set of names; update it to scan all top-level
declarations in the ast.Program (not just FnDecl) and use the existing
_node_mentions helper to detect Json usage in TypeAlias, Import/ImportFrom, and
other decl kinds, and extend json_names to include json_type and json_has_field
(and any other Json helpers) so imported/alias-based uses are detected and the
prelude is injected when needed.

In `@vera/wasm/calls.py`:
- Around line 4456-4462: Record the JSON host-import usage in
self._json_ops_used before emitting the call: when you build the instructions
that append "call $vera.json_parse", add self._json_ops_used.add("json_parse")
(or the exact key expected by vera/codegen/functions.py) immediately before
ins.append(...); do the same for the "call $vera.json_stringify" site (add
self._json_ops_used.add("json_stringify")). This ensures the translate_expr path
that sets self.needs_alloc and appends the call also registers the op in
self._json_ops_used so the import is propagated.

In `@vera/wasm/inference.py`:
- Around line 297-301: The Vera-type inference function _infer_fncall_vera_type
is missing cases for the JSON builtins json_parse and json_stringify, causing
_infer_vera_type to return None for those call expressions; update
_infer_fncall_vera_type to return the appropriate Vera types for json_parse
(e.g., a Vera Json/result pointer type matching the "i32" Wasm mapping used in
_infer_fncall_wasm_type) and json_stringify (e.g., a Vera string pair type
matching "i32_pair"), so JSON call expressions produce non-None types
consistently across _infer_fncall_wasm_type, _infer_fncall_vera_type, and
downstream codegen paths.

In `@vera/wasm/json_serde.py`:
- Around line 178-191: The code should fail fast instead of silently returning
{} or None: in the read_json path, when tag == _TAG_JOBJECT and
map_store.get(handle) returns missing, raise an exception (e.g., ValueError)
that includes the handle and ptr so the invalid layout is surfaced, and likewise
replace the final "return None" for unknown tags with an explicit exception that
includes the unexpected tag and ptr; update read_json (and callers if necessary)
to propagate these errors rather than converting them into empty dicts or nulls,
using the existing symbols _TAG_JOBJECT, read_i32, map_store and read_json to
locate the code to change.

---

Outside diff comments:
In `@TESTING.md`:
- Around line 206-212: The prose and the metrics table disagree: the table lists
"Tier 3 (runtime) | 7 contracts" and "Total | 146", while the paragraph mentions
"The 4 remaining Tier 3 contracts"; update the prose to match the table (change
the phrase "The 4 remaining Tier 3 contracts" to "The 7 remaining Tier 3
contracts") or, if the prose is correct, update the table values to reflect the
accurate counts; ensure the "Tier 3 (runtime)" count and the total (146) are
consistent across both the table and the subsequent paragraph.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d3928221-7c39-4f0e-a1cb-67d89e309f8f

📥 Commits

Reviewing files that changed from the base of the PR and between a774288 and 1de9060.

⛔ Files ignored due to path filters (4)
  • docs/index.html is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • examples/json.vera is excluded by !**/*.vera
  • tests/conformance/ch09_json.vera is excluded by !**/*.vera
📒 Files selected for processing (29)
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • scripts/check_readme_examples.py
  • scripts/check_skill_examples.py
  • scripts/check_spec_examples.py
  • spec/09-standard-library.md
  • tests/conformance/manifest.json
  • tests/test_checker.py
  • tests/test_codegen.py
  • tests/test_readme.py
  • tests/test_verifier.py
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/codegen/compilability.py
  • vera/codegen/core.py
  • vera/codegen/functions.py
  • vera/codegen/modules.py
  • vera/codegen/monomorphize.py
  • vera/environment.py
  • vera/prelude.py
  • vera/wasm/calls.py
  • vera/wasm/context.py
  • vera/wasm/inference.py
  • vera/wasm/json_serde.py

Comment thread README.md Outdated
Comment thread spec/09-standard-library.md Outdated
Comment thread spec/09-standard-library.md Outdated
Comment thread TESTING.md Outdated
Comment thread tests/test_checker.py
Comment thread vera/prelude.py
Comment thread vera/prelude.py
Comment thread vera/wasm/calls.py
Comment thread vera/wasm/inference.py
Comment thread vera/wasm/json_serde.py
Critical: Export $alloc for JSON-only modules (assembly.py), record
json_parse/json_stringify in _json_ops_used (calls.py).

Major: Fix spec §9.7.1 implementation description, add json_parse to
_BUILTIN_PARAMETERIZED_RETURNS, expand Json prelude gate to cover all
Json references, add shadowing guard for non-standard data Json, add
Vera-type inference for JSON builtins, add 4 checker/codegen tests,
strengthen json_type assertions, add allow_nan=False to json.dumps.

Minor: Narrow README bug descriptions, fix spec §9.1 Markdown status,
update TESTING.md counts and Tier 3 table, add negative-index test.

Trivial: Guard JSON.stringify undefined in browser runtime, warn on
unknown Json tags in json_serde.py.

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

♻️ Duplicate comments (5)
vera/wasm/json_serde.py (1)

178-180: ⚠️ Potential issue | 🟡 Minor

Missing warning for unknown map handle — silent {} fallback.

Line 180 silently falls back to an empty dict when the map handle is not found. Unlike the unknown tag case (which emits a warning), a missing handle could indicate memory corruption or a stale pointer, and warrants similar diagnostic output.

🛠️ Suggested fix: Add warning for missing handle
     if tag == _TAG_JOBJECT:
         handle = read_i32(caller, ptr + 4)
-        raw_map = map_store.get(handle, {})
+        raw_map = map_store.get(handle)
+        if raw_map is None:
+            import warnings
+            warnings.warn(
+                f"read_json: unknown map handle {handle} at pointer {ptr}; "
+                "possible memory corruption or stale reference",
+                RuntimeWarning,
+                stacklevel=2,
+            )
+            raw_map = {}
         obj: dict[str, Any] = {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vera/wasm/json_serde.py` around lines 178 - 180, When handling tag ==
_TAG_JOBJECT in the deserializer, don't silently fall back to {} on a missing
map handle: after computing handle = read_i32(caller, ptr + 4) and fetching
raw_map = map_store.get(handle, {}), detect the missing case (handle not in
map_store) and emit a warning/diagnostic (consistent with the unknown-tag
warning behavior) that includes the handle value and contextual info (e.g., ptr
and tag) so memory corruption or stale pointer issues are visible; keep the
fallback to {} but log the warning before returning.
tests/test_codegen.py (1)

8751-8776: ⚠️ Potential issue | 🟠 Major

Strengthen json_stringify tests to assert value semantics, not just string length.

These tests can still pass on incorrect serialisation with matching lengths. Please switch to parse-after-stringify assertions (round-trip), especially for JNull, JBool, and JNumber.

Proposed fix pattern
 def test_json_stringify_null(self) -> None:
-    """json_stringify(JNull) returns 'null' (length 4)."""
+    """json_stringify(JNull) round-trips to JNull."""
     source = """
 public fn main(-> `@Int`)
   requires(true) ensures(true) effects(pure)
-{ string_length(json_stringify(JNull)) }
+{
+  let `@Result`<Json, String> = json_parse(json_stringify(JNull));
+  match `@Result`<Json, String>.0 {
+    Ok(`@Json`) -> match `@Json.0` {
+      JNull -> 1,
+      _ -> 0
+    },
+    Err(_) -> 0
+  }
+}
 """
-    assert _run(source) == 4
+    assert _run(source) == 1

As per coding guidelines: “DO flag: Tests that don't assert anything meaningful” and “Add codegen/runtime tests in tests/test_codegen.py for built-in functions covering normal cases, edge cases (empty inputs, zero values), and composition with other built-ins”.

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

In `@tests/test_codegen.py` around lines 8751 - 8776, Replace the fragile
string-length assertions in test_json_stringify_null, test_json_stringify_bool,
and test_json_stringify_number with round-trip assertions: call json_stringify
on the literal (JNull, JBool(true), JNumber(42.0)), then parse that output back
with json_parse and assert equality to the original AST (e.g. assert
_run("json_parse(json_stringify(JNull))") == JNull). Keep references to the same
test functions (test_json_stringify_null, test_json_stringify_bool,
test_json_stringify_number) and builtins (json_stringify, json_parse, JNull,
JBool, JNumber, _run) so the tests verify semantic equivalence rather than
string length.
tests/test_checker.py (1)

2014-2036: ⚠️ Potential issue | 🟠 Major

Add missing wrong-type checker cases for the remaining Json built-ins.

Line 2014 onwards adds happy-path coverage for json_array_get, json_array_length, and json_keys, but there are no corresponding wrong-argument type tests. This leaves these signatures under-protected versus regressions.

Proposed test additions
 class TestJsonChecker:
@@
     def test_json_array_get(self) -> None:
         """json_array_get returns Option<Json>."""
         _check_ok("""
 private fn foo(`@Json` -> `@Option`<Json>)
   requires(true) ensures(true) effects(pure)
 { json_array_get(`@Json.0`, 0) }
 """)
+
+    def test_json_array_get_wrong_index_type(self) -> None:
+        """json_array_get with non-Int index produces error."""
+        _check_err("""
+private fn foo(`@Json` -> `@Option`<Json>)
+  requires(true) ensures(true) effects(pure)
+{ json_array_get(`@Json.0`, "0") }
+""", "type")
@@
     def test_json_array_length(self) -> None:
         """json_array_length returns Int."""
         _check_ok("""
 private fn foo(`@Json` -> `@Int`)
   requires(true) ensures(true) effects(pure)
 { json_array_length(`@Json.0`) }
 """)
+
+    def test_json_array_length_wrong_type(self) -> None:
+        """json_array_length with non-Json arg produces error."""
+        _check_err("""
+private fn foo(`@Unit` -> `@Int`)
+  requires(true) ensures(true) effects(pure)
+{ json_array_length(42) }
+""", "type")
@@
     def test_json_keys(self) -> None:
         """json_keys returns Array<String>."""
         _check_ok("""
 private fn foo(`@Json` -> `@Array`<String>)
   requires(true) ensures(true) effects(pure)
 { json_keys(`@Json.0`) }
 """)
+
+    def test_json_keys_wrong_type(self) -> None:
+        """json_keys with non-Json arg produces error."""
+        _check_err("""
+private fn foo(`@Unit` -> `@Array`<String>)
+  requires(true) ensures(true) effects(pure)
+{ json_keys(42) }
+""", "type")

As per coding guidelines: "Add type checker tests in tests/test_checker.py for built-in functions, with at minimum one test with correct types and one with a wrong argument type".

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

In `@tests/test_checker.py` around lines 2014 - 2036, Add negative type-check
tests for the three Json built-ins to complement the existing happy-paths: in
tests/test_checker.py add one test each (mirroring test_json_array_get,
test_json_array_length, test_json_keys) that calls json_array_get,
json_array_length, and json_keys with a wrong argument type so the checker fails
(e.g., pass an Int where a Json is expected for json_array_get and
json_array_length, and pass an Int instead of Json for json_keys), using the
project’s existing wrong-type assertion helper (the same style as other built-in
tests) so the type checker rejects these calls.
vera/codegen/api.py (1)

1558-1566: ⚠️ Potential issue | 🟠 Major

Do not rely on Python’s JSON defaults if this needs browser parity.

_json.loads() still accepts NaN / Infinity, and _json.dumps(..., allow_nan=False) still leaves Python’s default spaced separators in place. Because json_stringify returns String, those defaults are observable in user code and can diverge from the browser bindings. Reject non-finite constants explicitly on parse and choose one canonical serialisation policy that both runtimes implement.

Do Python's `json.loads` / `json.dumps` defaults and JavaScript's `JSON.parse` / `JSON.stringify` behave differently for `NaN` / `Infinity` and object or array whitespace? Cite the official Python `json` docs and MDN.

Also applies to: 1597-1599

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

In `@vera/codegen/api.py` around lines 1558 - 1566, Change JSON handling to match
browser behavior by rejecting non-finite numeric constants on parse and
producing a compact, deterministic serialization on stringify: in
host_json_parse (which calls _json.loads) pass a parse_constant that raises so
any NaN/Infinity tokens are rejected (or post-validate parsed numbers with
math.isfinite and return an error via _alloc_result_err_string), and in the
corresponding stringify implementation (the json_stringify path around lines
~1597) call _json.dumps with allow_nan=False and separators=(',', ':') to avoid
Python's spaced separators; update both host_json_parse and the stringify
function to return errors via the existing
_alloc_result_err_string/_alloc_result_ok_string helpers when invalid values are
encountered.
vera/prelude.py (1)

397-410: ⚠️ Potential issue | 🟠 Major

Validate the full Json shape before enabling the helpers.

_has_standard_json() currently accepts any data Json whose constructor names match, including cases like data Json<T> { ... }, JBool(String), or JArray(Array<Int>). The injected helpers then type-check against @Json, JBool(@Bool), JArray(@array<Json>), etc., and fail on the user-defined variant. Please mirror the Option / Result guard here by checking zero type parameters, constructor arity, and field types for all six constructors.

Also applies to: 596-599

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

In `@vera/prelude.py` around lines 397 - 410, The current
_has_standard_json(program: ast.Program) only checks constructor names; instead,
validate the full Json shape like the Option/Result guard by ensuring the
DataDecl for "Json" has no type parameters (decl.type_params empty) and each
constructor has the exact arity and field types expected: JNull with zero
fields, JBool with one field of type Bool, JNumber with one field of type
Number, JString with one field of type String, JArray with one field of type
Array<Json>, and JObject with one field matching the expected object
representation (e.g. Array<(String, Json)> or whatever the prelude uses); locate
these checks via program.declarations -> decl (ast.DataDecl) -> decl.name ==
"Json" and iterate decl.constructors (c.name, c.fields) to compare arity and
field AST types before returning True.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@vera/browser/runtime.mjs`:
- Around line 1553-1580: In readJson, unknown tag values are currently swallowed
and return null; update the function (readJson) to emit a console.warn when an
unrecognized tag is encountered, including the tag value and ptr (and optionally
related readI32/readF64 outputs) for debugging parity with the Python
RuntimeWarning, then continue returning null so behavior is unchanged; place the
warning just before the final return null to cover unexpected tags while
preserving existing control flow.

In `@vera/codegen/monomorphize.py`:
- Around line 86-90: The mapping _BUILTIN_PARAMETERIZED_RETURNS is missing
entries for json_get, json_array_get, and json_keys, so inner generic types are
lost during monomorphisation; add entries to that dict to preserve inner type
parameters (for example: "json_get": ("Option", ("Json",)), "json_array_get":
("Option", ("Json",)), and "json_keys": ("Option", ("Array", "String"))),
updating the _BUILTIN_PARAMETERIZED_RETURNS dict so _get_arg_type_info can bind
inner generics correctly for these functions.

In `@vera/wasm/json_serde.py`:
- Line 27: Replace the deprecated typing.Callable import with
collections.abc.Callable: update the import line in json_serde.py to continue
importing Any from typing but import Callable from collections.abc so references
to Callable in this module (e.g., type hints used in functions/classes) use the
modern, Python 3.9+ compatible symbol.

---

Duplicate comments:
In `@tests/test_checker.py`:
- Around line 2014-2036: Add negative type-check tests for the three Json
built-ins to complement the existing happy-paths: in tests/test_checker.py add
one test each (mirroring test_json_array_get, test_json_array_length,
test_json_keys) that calls json_array_get, json_array_length, and json_keys with
a wrong argument type so the checker fails (e.g., pass an Int where a Json is
expected for json_array_get and json_array_length, and pass an Int instead of
Json for json_keys), using the project’s existing wrong-type assertion helper
(the same style as other built-in tests) so the type checker rejects these
calls.

In `@tests/test_codegen.py`:
- Around line 8751-8776: Replace the fragile string-length assertions in
test_json_stringify_null, test_json_stringify_bool, and
test_json_stringify_number with round-trip assertions: call json_stringify on
the literal (JNull, JBool(true), JNumber(42.0)), then parse that output back
with json_parse and assert equality to the original AST (e.g. assert
_run("json_parse(json_stringify(JNull))") == JNull). Keep references to the same
test functions (test_json_stringify_null, test_json_stringify_bool,
test_json_stringify_number) and builtins (json_stringify, json_parse, JNull,
JBool, JNumber, _run) so the tests verify semantic equivalence rather than
string length.

In `@vera/codegen/api.py`:
- Around line 1558-1566: Change JSON handling to match browser behavior by
rejecting non-finite numeric constants on parse and producing a compact,
deterministic serialization on stringify: in host_json_parse (which calls
_json.loads) pass a parse_constant that raises so any NaN/Infinity tokens are
rejected (or post-validate parsed numbers with math.isfinite and return an error
via _alloc_result_err_string), and in the corresponding stringify implementation
(the json_stringify path around lines ~1597) call _json.dumps with
allow_nan=False and separators=(',', ':') to avoid Python's spaced separators;
update both host_json_parse and the stringify function to return errors via the
existing _alloc_result_err_string/_alloc_result_ok_string helpers when invalid
values are encountered.

In `@vera/prelude.py`:
- Around line 397-410: The current _has_standard_json(program: ast.Program) only
checks constructor names; instead, validate the full Json shape like the
Option/Result guard by ensuring the DataDecl for "Json" has no type parameters
(decl.type_params empty) and each constructor has the exact arity and field
types expected: JNull with zero fields, JBool with one field of type Bool,
JNumber with one field of type Number, JString with one field of type String,
JArray with one field of type Array<Json>, and JObject with one field matching
the expected object representation (e.g. Array<(String, Json)> or whatever the
prelude uses); locate these checks via program.declarations -> decl
(ast.DataDecl) -> decl.name == "Json" and iterate decl.constructors (c.name,
c.fields) to compare arity and field AST types before returning True.

In `@vera/wasm/json_serde.py`:
- Around line 178-180: When handling tag == _TAG_JOBJECT in the deserializer,
don't silently fall back to {} on a missing map handle: after computing handle =
read_i32(caller, ptr + 4) and fetching raw_map = map_store.get(handle, {}),
detect the missing case (handle not in map_store) and emit a warning/diagnostic
(consistent with the unknown-tag warning behavior) that includes the handle
value and contextual info (e.g., ptr and tag) so memory corruption or stale
pointer issues are visible; keep the fallback to {} but log the warning before
returning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3947d9b2-9a9f-45c3-be57-ddf0ee3cb024

📥 Commits

Reviewing files that changed from the base of the PR and between 1de9060 and fb20cbf.

📒 Files selected for processing (12)
  • README.md
  • TESTING.md
  • spec/09-standard-library.md
  • tests/test_checker.py
  • tests/test_codegen.py
  • vera/browser/runtime.mjs
  • vera/codegen/api.py
  • vera/codegen/assembly.py
  • vera/codegen/monomorphize.py
  • vera/prelude.py
  • vera/wasm/calls.py
  • vera/wasm/json_serde.py

Comment thread vera/browser/runtime.mjs
Comment thread vera/codegen/monomorphize.py
Comment thread vera/wasm/json_serde.py Outdated
aallan and others added 2 commits March 25, 2026 15:37
- Add wrong-type checker tests: json_array_length, json_keys,
  json_array_get_wrong_index_type
- Add json_parse/json_stringify to _infer_fncall_vera_type (inference.py)
- Add JObject handle warning in json_serde.py read_json
- Add 8 codegen tests: stringify for each Json type, parse with
  string values, nested objects, roundtrip
- json_serde.py coverage: 58% → 92%
- Fix TESTING.md counts (2982 tests)

Co-Authored-By: Claude <noreply@anthropic.invalid>
- Add console.warn for unknown tag in readJson (runtime.mjs)
- Add json_get, json_array_get, json_keys to _BUILTIN_PARAMETERIZED_RETURNS
- Replace deprecated typing.Callable with collections.abc.Callable

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: 4

♻️ Duplicate comments (2)
vera/wasm/json_serde.py (1)

27-27: 🧹 Nitpick | 🔵 Trivial

Use collections.abc.Callable for modern Python typing imports.

This has already been raised earlier and still applies at this line.

♻️ Suggested update
-from typing import Any, Callable
+from collections.abc import Callable
+from typing import Any
#!/bin/bash
# Verify legacy Callable imports in compiler Python modules.
rg -n 'from typing import .*Callable|typing\.Callable' vera --type py
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vera/wasm/json_serde.py` at line 27, The import is using typing.Callable
which is deprecated for modern typing; update the import in json_serde.py so
that Callable is imported from collections.abc instead of typing (e.g., replace
the current "from typing import Any, Callable" with an import that brings
Callable from collections.abc while keeping Any from typing), ensuring
references to Callable in the module remain unchanged.
tests/test_codegen.py (1)

8751-8776: ⚠️ Potential issue | 🟠 Major

Strengthen json_stringify tests beyond length/non-empty checks.

These assertions can pass with incorrect output of the same length (or any non-empty garbage), so they won’t reliably catch regressions in serialisation behaviour.

Proposed test-hardening pattern
 def test_json_stringify_number(self) -> None:
-    """json_stringify(JNumber(42.0)) returns '42.0' (length 4)."""
+    """json_stringify(JNumber(42.0)) round-trips to JNumber(42.x)."""
     source = """
 public fn main(-> `@Int`)
   requires(true) ensures(true) effects(pure)
-{ string_length(json_stringify(JNumber(42.0))) }
+{
+  let `@Result`<Json, String> = json_parse(json_stringify(JNumber(42.0)));
+  match `@Result`<Json, String>.0 {
+    Ok(`@Json`) -> match `@Json.0` {
+      JNumber(`@Float64`) -> if floor(`@Float64.0`) == 42 then { 1 } else { 0 },
+      _ -> 0
+    },
+    Err(_) -> 0
+  }
+}
 """
-    assert _run(source) == 4
+    assert _run(source) == 1

As per coding guidelines: “DO flag: Tests that don't assert anything meaningful”.

Also applies to: 8901-8955, 8990-9004

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

In `@tests/test_codegen.py` around lines 8751 - 8776, The tests (e.g.,
test_json_stringify_null, test_json_stringify_bool, test_json_stringify_number)
only assert string lengths which can hide incorrect serialization; update each
to assert the exact serialized string returned by json_stringify for the given
input (e.g., JNull -> "null", JBool(true) -> "true", JNumber(42.0) -> "42.0")
rather than just string_length or non-empty checks, and apply the same
exact-value assertions to the other json_stringify tests flagged in the review
to ensure meaningful verification of serialization behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@TESTING.md`:
- Around line 11-12: Update the stale example counts to reflect the current 29
verified example programs: change the count in TESTING.md (where it currently
says 27) and update the hardcoded value in scripts/build_site.py (the
constant/markup at the site-generation location that currently shows 25) to 29;
if build_site.py computes or exposes an examples count variable, replace the
literal with a dynamic computation from the examples/ directory or update the
constant to 29 so both documentation and site generation match the actual
examples/ count.

In `@tests/test_codegen.py`:
- Around line 8630-8633: The generated WAT can reference host imports for
json_parse/json_stringify even when those helpers were never registered, so
update the JSON host-import helper creation in vera/wasm/calls.py to record
usage by calling self._json_ops_used.add("json_parse") and
self._json_ops_used.add("json_stringify") at the point the helpers are created
(so their imports will be injected when used), and add explicit codegen
assertions in tests/test_codegen.py (e.g. in TestJsonCollection) that generate
WAT for cases that do and do not use json_parse/json_stringify and assert the
corresponding $vera.json_parse/$vera.json_stringify imports are present only
when used.
- Around line 8971-8988: The test test_json_parse_nested_object is asserting the
wrong property: after json_get(..., "inner") it checks json_has_field(...,
"inner") which doesn't validate the nested content; update the test to assert
the nested object contains the field "x" and that its value equals 1 (use
json_has_field and json_get on the returned `@Json` for "x" and compare the
extracted value to 1) so the test verifies the actual parsed nested value
produced by json_parse/json_get.

In `@vera/wasm/json_serde.py`:
- Around line 9-14: Update the module docstring so its example call signatures
match the actual function APIs: change the write_json and read_json signatures
shown in the top-of-file docstring to list the real parameters used by the
implementations (e.g., the same parameter names and order as the write_json
function and the read_json function in this file). Locate the docstring near the
top of vera/wasm/json_serde.py and replace the outdated example lines with
signatures that exactly match the implemented write_json(caller, alloc,
write_i32, write_f64, alloc_string, map_alloc, map_insert_ss, value) → ptr and
read_json(caller, read_i32, read_f64, read_string, map_store) → Any (or the
precise parameter names used in the current definitions) so callers and
maintainers see correct usage.

---

Duplicate comments:
In `@tests/test_codegen.py`:
- Around line 8751-8776: The tests (e.g., test_json_stringify_null,
test_json_stringify_bool, test_json_stringify_number) only assert string lengths
which can hide incorrect serialization; update each to assert the exact
serialized string returned by json_stringify for the given input (e.g., JNull ->
"null", JBool(true) -> "true", JNumber(42.0) -> "42.0") rather than just
string_length or non-empty checks, and apply the same exact-value assertions to
the other json_stringify tests flagged in the review to ensure meaningful
verification of serialization behavior.

In `@vera/wasm/json_serde.py`:
- Line 27: The import is using typing.Callable which is deprecated for modern
typing; update the import in json_serde.py so that Callable is imported from
collections.abc instead of typing (e.g., replace the current "from typing import
Any, Callable" with an import that brings Callable from collections.abc while
keeping Any from typing), ensuring references to Callable in the module remain
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2405240d-e193-4993-8cb6-862cf09463c2

📥 Commits

Reviewing files that changed from the base of the PR and between fb20cbf and e57025c.

📒 Files selected for processing (5)
  • TESTING.md
  • tests/test_checker.py
  • tests/test_codegen.py
  • vera/wasm/inference.py
  • vera/wasm/json_serde.py

Comment thread TESTING.md
Comment thread tests/test_codegen.py
Comment thread tests/test_codegen.py
Comment thread vera/wasm/json_serde.py

@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

♻️ Duplicate comments (1)
vera/wasm/json_serde.py (1)

8-14: ⚠️ Potential issue | 🟡 Minor

Synchronise the module docstring signatures with the actual APIs.

The documented call signatures are stale (map_insert_ss is not a parameter; read_json is missing ptr).

📝 Suggested doc fix
 Write direction (Python → WASM):
   write_json(caller, alloc, write_i32, write_f64, alloc_string,
-             map_alloc, map_insert_ss, value) → ptr
+             map_alloc, value) → ptr

 Read direction (WASM → Python):
-  read_json(caller, read_i32, read_f64, read_string, map_store) → Any
+  read_json(caller, ptr, read_i32, read_f64, read_string, map_store) → Any
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vera/wasm/json_serde.py` around lines 8 - 14, Update the module docstring so
its call signatures match the actual function definitions: edit the write_json
and read_json signatures in the top docstring to exactly reflect the parameter
names used by the implemented functions (refer to write_json and read_json
symbols), remove the stale map_insert_ss parameter name if it is not present in
the implementation and replace it with the real map-insert parameter name, and
add the missing ptr argument to read_json so the read direction signature shows
read_json(..., ptr) → Any.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@vera/browser/runtime.mjs`:
- Around line 1568-1577: The code path handling tag === 5 silently returns an
empty object when a JObject handle lookup in mapStore (using handle =
readI32(ptr + 4)) is missing; update this branch to emit a warning (e.g.,
console.warn or the module logger) including the missing handle value and any
contextual info (ptr or tag) so memory/layout drift is visible, but keep the
current behavior of returning {} and still calling readJson for existing entries
(symbols to locate: the tag === 5 branch, readI32, mapStore.get(handle), and
readJson).

---

Duplicate comments:
In `@vera/wasm/json_serde.py`:
- Around line 8-14: Update the module docstring so its call signatures match the
actual function definitions: edit the write_json and read_json signatures in the
top docstring to exactly reflect the parameter names used by the implemented
functions (refer to write_json and read_json symbols), remove the stale
map_insert_ss parameter name if it is not present in the implementation and
replace it with the real map-insert parameter name, and add the missing ptr
argument to read_json so the read direction signature shows read_json(..., ptr)
→ Any.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c6966486-595e-4c95-b7ff-f23b1af2522c

📥 Commits

Reviewing files that changed from the base of the PR and between e57025c and 459ec2c.

📒 Files selected for processing (3)
  • vera/browser/runtime.mjs
  • vera/codegen/monomorphize.py
  • vera/wasm/json_serde.py

Comment thread vera/browser/runtime.mjs
- Fix test_json_parse_nested_object to check "x" not "inner"
- Add WAT import assertions: json_parse, json_stringify, no-imports
- Add wrong-type test: json_custom_data_shadows_prelude
- Add console.warn for missing JObject handle in readJson (runtime.mjs)
- Fix json_serde.py docstring to match actual signatures
- Add json_get/json_array_get/json_keys to _BUILTIN_PARAMETERIZED_RETURNS
- Fix TESTING.md counts (2986 tests)

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: 2

♻️ Duplicate comments (2)
tests/test_codegen.py (2)

9005-9023: ⚠️ Potential issue | 🟠 Major

Validate nested value content, not only field existence.

After fetching "inner", this test only checks that "x" exists. It can still pass when "x" has the wrong type/value.

Proposed fix
-        Some(`@Json`) -> if json_has_field(`@Json.0`, "x") then { 1 } else { 0 },
+        Some(`@Json`) ->
+          match json_get(`@Json.0`, "x") {
+            Some(`@Json`) -> match `@Json.0` {
+              JNumber(`@Float64`) -> if floor(`@Float64.0`) == 1 then { 1 } else { 0 },
+              _ -> 0
+            },
+            None -> 0
+          },

As per coding guidelines: “DO flag: Tests that don't assert anything meaningful”.

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

In `@tests/test_codegen.py` around lines 9005 - 9023, Update the
test_json_parse_nested_object to validate the nested value content not just
existence: after json_parse and json_get(`@Json.0`, "inner") return Some(`@Json`),
call json_get(`@Json.0`, "x") and assert the retrieved value is the numeric 1
(e.g., match the Some(`@Json`) and check the contained number equals 1) instead of
only using json_has_field(`@Json.0`, "x"); this ensures the test checks actual
value/type correctness for the "x" field.

8785-8811: ⚠️ Potential issue | 🟠 Major

Strengthen stringify assertions beyond length/non-empty checks.

Several tests still pass on incorrect JSON payloads as long as output length is non-zero (or expected length). Prefer parse-back and structural/value assertions.

Proposed tightening pattern
     def test_json_stringify_number(self) -> None:
-        """json_stringify(JNumber(42.0)) returns '42.0' (length 4)."""
+        """json_stringify(JNumber(42.0)) round-trips as JNumber(42.x)."""
         source = """
 public fn main(-> `@Int`)
   requires(true) ensures(true) effects(pure)
-{ string_length(json_stringify(JNumber(42.0))) }
+{
+  let `@Result`<Json, String> = json_parse(json_stringify(JNumber(42.0)));
+  match `@Result`<Json, String>.0 {
+    Ok(`@Json`) -> match `@Json.0` {
+      JNumber(`@Float64`) -> if floor(`@Float64.0`) == 42 then { 1 } else { 0 },
+      _ -> 0
+    },
+    Err(`@String`) -> 0
+  }
+}
 """
-        assert _run(source) == 4
+        assert _run(source) == 1

As per coding guidelines: “DO flag: Tests that don't assert anything meaningful”.

Also applies to: 8935-8989, 9024-9038

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

In `@tests/test_codegen.py` around lines 8785 - 8811, The tests (e.g.,
test_json_stringify_null, test_json_stringify_bool, test_json_stringify_number)
currently only assert string_length(json_stringify(...)) which can pass for
incorrect output; change them to assert the actual JSON output or its parsed
structure by calling _run to get the string from
json_stringify(JNull/JBool/JNumber) and then either compare the exact string
(e.g., "null", "true", "42.0") or parse it with Python's json.loads and assert
the resulting value/type equals the expected (None/True/42.0); update any
similar tests mentioned (lines ~8935-8989, 9024-9038) to follow this stricter
pattern and reference json_stringify, JNull, JBool, JNumber, _run, and
string_length when making the replacements.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@TESTING.md`:
- Line 76: Update the command snippet comment that still says "300 tests" to
match the table which lists test_conformance.py as 305 tests: locate the command
snippet in TESTING.md referencing test_conformance.py and change the numeric
comment from 300 to 305 (and update any other occurrences of the old count in
that snippet), ensuring the comment text now accurately reflects
test_conformance.py = 305 tests.

In `@tests/test_checker.py`:
- Around line 1990-2036: Add missing wrong-argument checker tests for the JSON
built-ins: update the test methods test_json_get, test_json_has_field, and
test_json_type to include a second `_check_err(..., "type")` case each that
passes an incorrect argument type to the builtin (e.g., pass an Int or String
where a Json is expected, or vice versa) to assert the type checker rejects the
call; reference the functions json_get, json_has_field, and json_type and use
`_check_err` with the "type" message to mirror the existing built-in test
pattern.

---

Duplicate comments:
In `@tests/test_codegen.py`:
- Around line 9005-9023: Update the test_json_parse_nested_object to validate
the nested value content not just existence: after json_parse and
json_get(`@Json.0`, "inner") return Some(`@Json`), call json_get(`@Json.0`, "x") and
assert the retrieved value is the numeric 1 (e.g., match the Some(`@Json`) and
check the contained number equals 1) instead of only using
json_has_field(`@Json.0`, "x"); this ensures the test checks actual value/type
correctness for the "x" field.
- Around line 8785-8811: The tests (e.g., test_json_stringify_null,
test_json_stringify_bool, test_json_stringify_number) currently only assert
string_length(json_stringify(...)) which can pass for incorrect output; change
them to assert the actual JSON output or its parsed structure by calling _run to
get the string from json_stringify(JNull/JBool/JNumber) and then either compare
the exact string (e.g., "null", "true", "42.0") or parse it with Python's
json.loads and assert the resulting value/type equals the expected
(None/True/42.0); update any similar tests mentioned (lines ~8935-8989,
9024-9038) to follow this stricter pattern and reference json_stringify, JNull,
JBool, JNumber, _run, and string_length when making the replacements.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ca7533ca-f915-47b5-8f9e-3a7655b1c962

📥 Commits

Reviewing files that changed from the base of the PR and between 459ec2c and 1bfb6b5.

📒 Files selected for processing (5)
  • TESTING.md
  • tests/test_checker.py
  • tests/test_codegen.py
  • vera/browser/runtime.mjs
  • vera/wasm/json_serde.py

Comment thread TESTING.md
Comment thread tests/test_checker.py
- Fix TESTING.md conformance count 300→305
- Add wrong-type tests: json_get, json_has_field, json_type
- Add codegen test for custom data Json (exercises _has_standard_json)
- Reply to example count (27 is correct), WAT imports (already fixed),
  nested object test (already fixed)
- prelude.py coverage: 405-410 now covered

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

aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Re: TESTING.md example count — 27 is correct. ls examples/*.vera | wc -l returns 27. The check_doc_counts.py script validates this automatically.

Re: WAT imports for json_parse/json_stringify — already fixed in fb20cbf (_json_ops_used.add() calls). WAT assertion tests added in 1bfb6b5 (test_json_parse_wat_import, test_json_stringify_wat_import, test_json_no_imports_when_unused).

Re: test_json_parse_nested_object — fixed in 1bfb6b5 to check json_has_field(@Json.0, "x") instead of "inner".

Re: TESTING.md conformance count — fixed 300→305 in 8f1c3c5.

Re: wrong-type checker tests — added test_json_get_wrong_type, test_json_has_field_wrong_type, test_json_type_wrong_type in 8f1c3c5.

Re: readJson missing JObject handle — added console.warn in 1bfb6b5.

Re: json_serde.py docstring — fixed in 1bfb6b5 to match actual signatures.

@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown

Tip

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

@aallan Thanks for the thorough rundown — all seven items confirmed resolved:

  • TESTING.md example count (27): Acknowledged — check_doc_counts.py auto-validates this, so no future drift concern.
  • WAT imports (json_parse/json_stringify): _json_ops_used.add() tracking in fb20cbf + WAT assertion tests (test_json_parse_wat_import, test_json_stringify_wat_import, test_json_no_imports_when_unused) in 1bfb6b5. ✅
  • test_json_parse_nested_object predicate: Corrected to json_has_field(@Json.0, "x") in 1bfb6b5. ✅
  • TESTING.md conformance count 300→305: Fixed in 8f1c3c5. ✅
  • Wrong-type checker tests: test_json_get_wrong_type, test_json_has_field_wrong_type, test_json_type_wrong_type added in 8f1c3c5. ✅
  • readJson missing JObject handle: console.warn added in 1bfb6b5. ✅
  • json_serde.py docstring signatures: Corrected in 1bfb6b5. ✅

The JSON ADT implementation looks solid end-to-end. Well done.


✏️ Learnings added
Learnt from: aallan
URL: https://github.com/aallan/vera/pull/350

Timestamp: 2026-03-25T16:24:36.285Z
Learning: In repo aallan/vera, the script `scripts/check_doc_counts.py` automatically validates that documented example and conformance program counts in TESTING.md, CLAUDE.md, and related docs match the actual file counts (`ls examples/*.vera | wc -l`, etc.). Do not flag count mismatches as bugs without first verifying against this script.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: aallan/vera PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:19:45.796Z
Learning: Applies to .coderabbit.yaml : CodeRabbit configuration for this repository enables AI code review on pull requests. Use `coderabbitai` prefix when responding to review comments, and commands like `coderabbitai pause`, `coderabbitai review`, `coderabbitai full review`, `coderabbitai generate unit tests` to control the bot.

Learnt from: aallan
Repo: aallan/vera PR: 350
File: vera/wasm/calls.py:4456-4463
Timestamp: 2026-03-25T14:53:21.202Z
Learning: Repo aallan/vera: In vera/wasm/calls.py, JSON host-import helpers must record usage via self._json_ops_used.add("json_parse") and self._json_ops_used.add("json_stringify") inside CallsMixin._translate_json_parse/_translate_json_stringify so codegen propagates result.json_ops_used and conditional imports are injected in assembly/api; otherwise WAT may reference $vera.json_parse/$vera.json_stringify without matching imports.

Learnt from: CR
Repo: aallan/vera PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-24T11:56:57.789Z
Learning: Use conformance suite programs in tests/conformance/ as minimal working examples for each Vera language feature, checking manifest.json to map features to programs

Learnt from: CR
Repo: aallan/vera PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:19:45.796Z
Learning: Applies to examples/*.vera : All example Vera programs must pass both `vera check` and `vera verify`. The validation script `scripts/check_examples.py` tests all examples automatically.

Learnt from: CR
Repo: aallan/vera PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:19:45.796Z
Learning: Pre-commit hooks run mypy + pytest + conformance suite + example validation on every commit. All 60 conformance programs in `tests/conformance/` must pass their declared level. All 26 examples in `examples/` must pass `vera check` and `vera verify`. All tests must pass: `pytest tests/ -v`. Type checking must be clean: `mypy vera/`.

@aallan

aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Addressing two outstanding review-body items:

TESTING.md example count (29): The count is 27, not 29. Verified: ls examples/*.vera | wc -l returns 27. The check_doc_counts.py script validates this on every commit and would fail if wrong. The build_site.py script reads the count dynamically from CLAUDE.md which also says 27. CodeRabbit may have miscounted.

WAT imports for json_parse/json_stringify: Both issues were already fixed:

  • _json_ops_used.add() calls were added in commit fb20cbf (lines 4460/4477 of calls.py)
  • WAT assertion tests were added in commit 1bfb6b5: test_json_parse_wat_import, test_json_stringify_wat_import, and test_json_no_imports_when_unused (verifies imports are absent when not used)

@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown

Tip

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

@aallan — Apologies for the miscounting error; 27 is correct and I should have caught that against the check_doc_counts.py guard before flagging it. Both items are confirmed:

  • Example count: 27 ✅ — check_doc_counts.py enforces this on every commit, so any drift would break CI immediately.
  • WAT imports (json_parse/json_stringify): Fixed in fb20cbf (_json_ops_used.add() at lines 4460/4477 of calls.py) + WAT assertion tests (test_json_parse_wat_import, test_json_stringify_wat_import, test_json_no_imports_when_unused) in 1bfb6b5

Nothing further outstanding here.


🧠 Learnings used
Learnt from: aallan
Repo: aallan/vera PR: 350
File: vera/wasm/calls.py:4456-4463
Timestamp: 2026-03-25T14:53:21.202Z
Learning: Repo aallan/vera: In vera/wasm/calls.py, JSON host-import helpers must record usage via self._json_ops_used.add("json_parse") and self._json_ops_used.add("json_stringify") inside CallsMixin._translate_json_parse/_translate_json_stringify so codegen propagates result.json_ops_used and conditional imports are injected in assembly/api; otherwise WAT may reference $vera.json_parse/$vera.json_stringify without matching imports.

Learnt from: aallan
Repo: aallan/vera PR: 0
File: :0-0
Timestamp: 2026-03-25T16:24:36.285Z
Learning: In repo aallan/vera, the script `scripts/check_doc_counts.py` automatically validates that documented example and conformance program counts in TESTING.md, CLAUDE.md, and related docs match the actual file counts (`ls examples/*.vera | wc -l`, etc.). Do not flag count mismatches as bugs without first verifying against this script.

Learnt from: CR
Repo: aallan/vera PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:19:45.796Z
Learning: Applies to .coderabbit.yaml : CodeRabbit configuration for this repository enables AI code review on pull requests. Use `coderabbitai` prefix when responding to review comments, and commands like `coderabbitai pause`, `coderabbitai review`, `coderabbitai full review`, `coderabbitai generate unit tests` to control the bot.

@aallan

aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner Author

Merging with codecov/patch at 93.57% (target 95.37%). The remaining ~2% gap is defensive code that cannot be exercised without injecting corrupt data:

  • vera/wasm/json_serde.py lines 183-190, 202-209: warnings.warn for missing JObject handles and unknown ADT tags — these fire on memory corruption, not normal operation
  • vera/prelude.py lines 454, 460: Edge-case branches in _source_mentions_json for programs that reference Json only through type aliases or imported values (no direct constructor/function usage)

Project coverage is 95.36% (-0.02% from baseline). All 2,990 tests pass.

@aallan aallan merged commit 991cf65 into main Mar 25, 2026
26 of 28 checks passed
@aallan aallan deleted the feat/json-type branch March 25, 2026 16:32
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.

JSON standard library type

1 participant