Files
AILang/docs/journals/2026-05-12-iter-rt.1.md
T
Brummel 098fa7e9be iter rt.1: roundtrip-invariant audit tests — 3 new tests, all PASS first-shot
Three new reader-only tests pin the .ail.json / .ailx bijection
plus AST-variant coverage. All three passed first observation
across the current corpus — no roundtrip, schema-coverage, or
CLI-drift gaps surfaced.

- every_ailx_fixture_matches_its_json_counterpart (replaces the
  3-pair handwritten exhibits): 57 paired .ailx fixtures pass.
- every_ast_variant_is_observed_in_the_fixture_corpus (new): 34/34
  AST variants exercised across 136 fixtures; exhaustive matches
  without _ wildcard so AST drift fails the build.
- cli_render_then_parse_preserves_canonical_bytes_on_every_fixture
  (new): 136 fixtures round-trip through ail render → tempfile →
  ail parse with BLAKE3 identity on canonical bytes.

No production code, no DESIGN.md changes (those follow in later
iterations). Tests are pure readers of the repo per spec
acceptance #7 — tempfile crate added as workspace dep for the CLI
roundtrip's intermediate file outside the repo.
2026-05-12 09:31:38 +02:00

5.7 KiB

iter rt.1 — Roundtrip-Invariant audit tests

Date: 2026-05-12 Started from: 10ccd1406c Status: DONE Tasks completed: 4 of 4

Summary

Three new tests landed; one existing test (handwritten exhibits) was replaced with a dynamic counterpart. No production code or DESIGN.md changes (those would be later iterations if any gap surfaced). All three new tests passed on first observation — the corpus carries enough breadth that the spec-intended audit deliverable is the clean signal "no current gap" rather than a defect list.

Per-task notes

  • iter rt.1.1: crates/ailang-surface/tests/round_trip.rs — replaced static three-pair handwritten_exhibits_match_json_fixtures with dynamic every_ailx_fixture_matches_its_json_counterpart (and helper list_ailx_fixtures). Module doc rewritten to drop the "three hand-written exhibits" phrasing.
  • iter rt.1.2: Cargo.tomltempfile = "3" added to [workspace.dependencies]. crates/ail/Cargo.toml — new [dev-dependencies] section with tempfile.workspace = true and blake3.workspace = true.
  • iter rt.1.3: crates/ailang-core/tests/schema_coverage.rs — new file. Exhaustive AST visitor (Def, Term, Pattern, Literal, Type, ParamMode) against the fixture corpus; flat VariantTag enum + EXPECTED_VARIANTS array; assertion that every variant is observed at least once. No _ wildcards in outer match arms — AST drift fails the build, not the test.
  • iter rt.1.4: crates/ail/tests/roundtrip_cli.rs — new file. Subprocess pipeline ail render → tempfile → ail parse, BLAKE3 hash identity on canonical bytes, for every examples/*.ail.json.

Audit findings

  • every_ailx_fixture_matches_its_json_counterpart: PASS. .ailx cross-check ok (57 paired, 0 parse-only). All 57 .ailx fixtures parse and produce canonical bytes byte-equal to their same-stem .ail.json counterpart. Today no .ailx exists without a JSON counterpart.

  • every_ast_variant_is_observed_in_the_fixture_corpus: PASS. schema coverage ok: 34 variants observed across 136 fixtures. All 34 expected VariantTag entries (5 Def + 13 Term + 4 Pattern + 5 Literal + 4 Type + 3 ParamMode) are exercised by at least one fixture. No load failures across the 136-file corpus.

  • cli_render_then_parse_preserves_canonical_bytes_on_every_fixture: PASS. CLI roundtrip ok for 136 fixtures. Every examples/*.ail.json survives ail render → tempfile → ail parse with BLAKE3 hash identity on canonical bytes.

  • cargo test --workspace --quiet (Step 4.5): PASS, no regressions in the pre-existing test suite.

Concerns

  • Plan-text errata in Task 3's visitor scaffold. The plan in docs/plans/rt.1.md Step 3.2 destructured AST nodes with field names that do not match crates/ailang-core/src/ast.rs (the authoritative source). Step 3.4 of the plan explicitly anticipated this and prescribed inline correction by reading ast.rs and adjusting the destructuring. The corrections applied:

    • Def::Fn(fd): dropped the for m in &fd.params { visit_param_mode(m, …) } loop (fd.params: Vec<String> is parameter names, not modes).
    • Def::Const(cd): walks cd.value (the actual field name), not cd.body.
    • Def::Class(cd): walks m.ty plus optional m.default: Option<Term> on each ClassMethod; no m.body/m.params fields exist on ClassMethod.
    • Def::Instance(id): walks id.type_: Type (singular) and id.methods; no id.args: Vec<Type> field exists. InstanceMethod has only name and body.
    • Term::App: { callee, args, .. } (not { f, args }).
    • Term::Let: { value, body, .. } (not { bound, body }).
    • Term::LetRec: { ty, body, in_term, .. } (per actual AST: name, ty, params, body, in_term), not the plan's { bindings, body }.
    • Term::If: { cond, then, else_ } (not { cond, then_b, else_b }).
    • Term::Do: { args, .. } only (no stmts/ret fields).
    • Term::Lam: { param_tys, ret_ty, body, .. } (no modes field exists on Lam).
    • Term::Seq: { lhs, rhs } (not { stmts, ret }).
    • ParamMode discovery routed through visit_type on Type::Fn { param_modes, ret_mode, .. } — the only place ParamMode appears in the AST. The plan tried to visit ParamMode from Def::Fn.params (wrong type) and Term::Lam.modes (non-existent field).

    The spec-compliance intent of Step 3.2 ("every AST enum variant is visited at least once via an exhaustive match with no _ wildcard") is preserved; only the destructuring patterns adapted to the actual AST. All 34 expected variants observe coverage in the corpus, and the build remains a tripwire for future AST additions.

  • Plan template note: a future planner/brainstorm pass on tests-against-AST might cheaply read ast.rs and emit a destructuring-correct scaffold instead of relying on the implementer to repair under the Step 3.4 escape hatch. Not rt.1 work.

Known debt

  • None added by this iter. The three new tests are pure readers and add no production-code surface; their existence pins three invariants that future iters can use to detect regression rather than rediscover.

Files touched

  • Cargo.toml — workspace dep tempfile = "3".
  • Cargo.lock — auto-updated by cargo for tempfile.
  • crates/ail/Cargo.toml — new [dev-dependencies] section.
  • crates/ailang-surface/tests/round_trip.rs — module doc rewrite; handwritten_exhibits_match_json_fixturesevery_ailx_fixture_matches_its_json_counterpart (+ helper).
  • crates/ailang-core/tests/schema_coverage.rs — new file.
  • crates/ail/tests/roundtrip_cli.rs — new file.

Stats

bench/orchestrator-stats/2026-05-12-iter-rt.1.json