diff --git a/docs/plans/2026-05-14-iter-rpe.1.md b/docs/plans/2026-05-14-iter-rpe.1.md new file mode 100644 index 0000000..f556f3a --- /dev/null +++ b/docs/plans/2026-05-14-iter-rpe.1.md @@ -0,0 +1,749 @@ +# rpe.1 — Retire per-type print effect-ops — Implementation Plan + +> **Parent spec:** `docs/specs/2026-05-14-retire-per-type-print-effects.md` +> +> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement` +> to run this plan. Steps use `- [ ]` checkboxes for tracking. + +**Goal:** Delete `io/print_int`, `io/print_bool`, `io/print_float` +from the compiler and migrate every corpus call site to the +polymorphic `print` helper shipped in iter 24.3. + +**Architecture:** Four-site lockstep deletion (check builtins, +codegen lower_effect_op, codegen synth.rs effect_op_ret table, +plus three doc-comment touch-ups), preceded by exhaustive corpus +migration so that the deletion does not strand any live caller. +A RED test pins the deletion gate; per-task `cargo test --workspace` +gates each step. Bench-latency drift is expected per spec §C4 (a) +and is ratified at iter close. + +**Tech Stack:** `ailang-check`, `ailang-codegen`, `ailang-surface`, +`ailang-prose`, `ailang-core`, `ail` CLI; the snapshot-regenerated +`.prose.txt` artefacts; `bench/run.sh` latency harness baselines. + +--- + +## Errata vs. spec (incorporated below; spec is unchanged) + +Recon surfaced four spec-vs-code mismatches; this plan absorbs +them without amending the spec: + +1. **Spec §C2 says "three install_io_print__signature tests + (lines ~593–633)".** Only `install_io_print_float_signature` + exists (builtins.rs:600). The plan deletes the one that + exists, not three. +2. **Spec §D1 says "98 examples/*.ail files".** Actual: 92 `.ail` + + 6 `.prose.txt` snapshot siblings = 98. The plan migrates + both, but a `.prose.txt` is regenerated, not text-substituted. +3. **`crates/ailang-codegen/src/synth.rs:215`** carries a parallel + effect-op `ret` table (`builtin_effect_op_ret`) the spec §C2 + does not name. Lockstep delete with the codegen arms. +4. **Five test bodies + four doc-comment sites** carry + `io/print_int` as incidental scaffolding (`crates/ailang-check/src/lib.rs`, + `crates/ailang-core/tests/spec_drift.rs`, `design_schema_drift.rs`, + `crates/ailang-surface/{src/lex.rs, src/parse.rs}`, + `crates/ailang-prose/src/lib.rs`, `crates/ail/src/main.rs`, + `crates/ailang-core/specs/form_a.md`, `runtime/str.c` + comment). Plan covers them in Task 5 and Task 6. + +The spec's §B/§C1/§D1 invariants are unaffected. + +--- + +## Files this plan creates or modifies + +**Create:** + +- `crates/ailang-check/tests/no_per_type_print_ops.rs` — RED test + (Task 1). Model on `crates/ailang-check/tests/show_dispatch_pin.rs`. + +**Modify — corpus (Task 2 + Task 3):** + +- `examples/.ail` × 92 — bulk substitution. Full list at + `/tmp/migration_targets.txt` (built by Task 2 Step 1; see Task 2 + for the canonical query the LLM re-runs). +- `examples/.prose.txt` × 6 — regenerate via `ail prose`. + Files: `bench_list_sum.prose.txt`, `ordering_match.prose.txt`, + `rc_app_let_partial_drop_leak.prose.txt`, + `rc_match_arm_partial_drop_leak.prose.txt`, + `rc_own_param_drop.prose.txt`, + `test_22b3_default_e2e.prose.txt`. + +**Modify — compiler core (Task 4):** + +- `crates/ailang-check/src/builtins.rs`: + - line 5 — module-doc-comment swap `io/print_int` → `io/print_str`. + - line 40 — `EffectOpSig.ret` doc swap. + - lines 269-276 — delete `env.effect_ops.insert("io/print_int", ...)`. + - lines 277-284 — delete `env.effect_ops.insert("io/print_bool", ...)`. + - lines 293-302 — delete floats-iter comment + `io/print_float` insert. + - line 349 — delete `io/print_int` row in `list()`. + - line 350 — delete `io/print_bool` row. + - line 352 — delete `io/print_float` row. (Line 351 — `io/print_str` — survives.) + - lines 593-635 — delete `install_io_print_float_signature` test fn (incl. its 7-line doc-comment). +- `crates/ailang-codegen/src/lib.rs`: + - lines 2302-2323 — delete `"io/print_int" => { ... }` arm. + - lines 2324-2345 — delete `"io/print_float" => { ... }` arm. + - lines 2373-2411 — delete `"io/print_bool" => { ... }` arm. + - lines 3580-3617 — delete `lowers_io_print_float` test (incl. doc-comment). + - format-string interns (`"fmt_int"`, `"%lld\n"`, `"fmt_float"`, `"%g\n"`, `"fmt_true"`, `"fmt_false"`) orphan and may be deleted in the same edit if they are no longer referenced. Verify by `grep` post-edit. +- `crates/ailang-codegen/src/synth.rs:215` — remove `"io/print_int" | "io/print_bool" | "io/print_float"` from the `match name` arm; keep `"io/print_str"`. + +**Modify — incidental-scaffolding test bodies (Task 5):** + +- `crates/ailang-check/src/lib.rs`: + - lines 3871-3877 (undeclared-effect-diagnostic test) — swap `op: "io/print_int"` → `op: "io/print_str"`; if the arg type was `Int`, change it to a `Str` literal that types as Str. + - lines 5047-5051 (tail-call-in-tail-position test) — same shape. +- `crates/ailang-core/tests/spec_drift.rs` lines 64-71 — swap to `io/print_str` with a `Str` arg. +- `crates/ailang-core/tests/design_schema_drift.rs` lines 87-94 — same. +- `crates/ailang-surface/src/lex.rs`: + - lines 18-20 — module-doc-comment swap. + - line 320 — `tokenize("+ == <= io/print_int std_list.map")` swap to `io/print_str`. + - line 328 — corresponding `assert_eq!` vector swap. +- `crates/ailang-prose/src/lib.rs` lines 1585-1593 — round-trip test asserting `render_term(&t) == "do io/print_int(5)"`: swap to `io/print_str` with a Str arg + matching assert. + +**Modify — doc-comment touch-ups (Task 6):** + +- `crates/ailang-surface/src/parse.rs:1377` — diagnostic example swap. +- `crates/ail/src/main.rs:30-31, 1419-1420` — CLI doc-comments swap. +- `runtime/str.c:117-118` — re-anchor on `%g` directly. +- `crates/ailang-core/specs/form_a.md:433-434` — surface-spec example: migrate to either `(do io/print_str s)` with a `Str` literal, or `(app print xs)` — pick the latter to advertise the post-iter idiom. + +**Modify — DESIGN.md (Task 7):** + +- `docs/DESIGN.md`: + - line 331 — Decision 11 example swap. + - lines 1990-1992 — past-tense rewrite ("retired 2026-05-14 in iter rpe.1"). + - lines 2024-2025 — Heap-Str primitives output sentence rewrite per spec §C3. + - line 2331 — effect-op invocation comment example swap. + - lines 2565-2577 — Float NaN-rendering paragraph rewrite (anchor on `float_to_str`). + - line 2679 — "What is supported (effect ops)" list, keep only `io/print_str`. + - line 2699 — "(IO effect ops)" list, keep only `io/print_str`. + +**Modify — incidental E2E test comments (Task 8):** + +- `crates/ail/tests/eq_ord_e2e.rs:66, 76` — update comments to name `print` semantics. +- `crates/ail/tests/e2e.rs:2674-2683` (`int_arg_to_effect_op_does_not_rc_track`) — fixture migrates in Task 2; doc-comment migrates here. +- `crates/ail/tests/floats_e2e.rs:7` — module doc-comment swap. + +**Modify — bench baselines (Task 9, conditional):** + +- `bench/baseline.json` "latency" block (lines 70-92) — ratify if drift. +- `bench/baseline.json" throughput block (lines 1-69, 93) — ratify if drift. +- `bench/baseline_cross_lang.json` — ratify if drift. +- `bench/baseline_compile.json` — expected exit-0; ratify only if unexpected drift. + +**Modify — roadmap (Task 10):** + +- `docs/roadmap.md` P2 section lines 80-90 — strike entry with `- [x]` + date. + +--- + +## Task 1: RED test — `no_per_type_print_ops.rs` + +**Files:** +- Create: `crates/ailang-check/tests/no_per_type_print_ops.rs` + +- [ ] **Step 1: Write the failing test** + +```rust +//! Hard gate for iter rpe.1: the per-type print effect-ops +//! `io/print_int`, `io/print_bool`, `io/print_float` are retired +//! and must NOT appear in the builtin registry. The polymorphic +//! `print` helper (prelude, iter 24.3) is the canonical replacement; +//! `io/print_str` survives as the byte-channel primitive. + +use ailang_check::build_check_env; +use ailang_surface::load_workspace; +use std::path::PathBuf; + +fn fixture_dir() -> PathBuf { + let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + d.pop(); + d.pop(); + d.join("examples") +} + +#[test] +fn per_type_print_effect_ops_are_retired() { + // Any workspace will do; we only inspect the post-install + // builtin registry. `hello.ail` is the smallest surviving + // fixture (uses `io/print_str` only). + let ws_path = fixture_dir().join("hello.ail"); + let ws = load_workspace(&ws_path).expect("workspace loads"); + let env = build_check_env(&ws); + + for retired in ["io/print_int", "io/print_bool", "io/print_float"] { + assert!( + !env.effect_ops.contains_key(retired), + "{retired} must not appear in builtin registry post-rpe.1; \ + use polymorphic `print` (Show-based) instead" + ); + } + + // Survivor pin: io/print_str stays. + assert!( + env.effect_ops.contains_key("io/print_str"), + "io/print_str must survive — it is the canonical byte-channel \ + primitive and used internally by `print`" + ); +} +``` + +- [ ] **Step 2: Verify the test fails RED** + +Run: `cargo test --workspace -p ailang-check --test no_per_type_print_ops` +Expected: FAIL on the first `assert!` because `io/print_int` +still appears in `env.effect_ops` post-Task-2. + +This gate flips to GREEN at Task 4 close. + +--- + +## Task 2: Corpus `.ail` migration — 92 fixtures + +**Files:** +- Modify: 92 `examples/.ail` files (full list deterministic + from `grep -rl "io/print_int\|io/print_bool\|io/print_float" + examples/ | grep "\.ail$"`). + +- [ ] **Step 1: Build the canonical target list** + +Run: +```bash +grep -rl "io/print_int\|io/print_bool\|io/print_float" examples/ \ + | grep "\.ail$" > /tmp/rpe1_targets.txt && \ + wc -l /tmp/rpe1_targets.txt +``` +Expected: `92 /tmp/rpe1_targets.txt`. + +- [ ] **Step 2: Bulk-substitute each fixture** + +For each path in `/tmp/rpe1_targets.txt`, transform every +occurrence of: +``` +(do io/print_int ) +``` +to: +``` +(app print ) +``` +And the same for `io/print_bool`, `io/print_float`. + +The transformation is regular: parenthesis-balance is preserved +(both `do` and `app` consume their parentheses identically), +the head is replaced (`io/print_` → `print`), and the leading +keyword is replaced (`do` → `app`). The argument expression is +copied verbatim. + +Implementation: read each file, apply the regex +`\(do io/print_(int|bool|float) ` → `(app print `, write back. +Or process file-by-file with Edit-tool calls; either is fine. + +- [ ] **Step 3: Verify each migrated fixture parses + round-trips** + +Run: +```bash +cargo run -p ail -- check examples/ 2>&1 +``` +Expected: exit 0. The pre-iter compiler still knows +`io/print_int|bool|float`, so existing fixtures stay green; the +migrated form (using polymorphic `print`) ALSO type-checks because +`print` is in the prelude (iter 24.3). Both paths are green +simultaneously in this iter window. + +Pragmatic shortcut: run `cargo test --workspace` at Step 4 instead +of per-fixture `ail check`. + +- [ ] **Step 4: `cargo test --workspace` green** + +Run: `cargo test --workspace` +Expected: 0 failures. The migrated fixtures' E2E test wrappers +still produce the same stdout (output bytes identical between +`(do io/print_ x)` and `(app print x)` per spec §"Data flow"). + +If any fixture has a stdout-comparison test that now produces +different bytes, that is a real divergence and the spec's +"output bytes identical" claim has a counterexample — STOP +and investigate before continuing. + +--- + +## Task 3: Prose snapshot regeneration — 6 `.prose.txt` files + +**Files:** +- Modify: `examples/bench_list_sum.prose.txt`, + `examples/ordering_match.prose.txt`, + `examples/rc_app_let_partial_drop_leak.prose.txt`, + `examples/rc_match_arm_partial_drop_leak.prose.txt`, + `examples/rc_own_param_drop.prose.txt`, + `examples/test_22b3_default_e2e.prose.txt`. + +- [ ] **Step 1: Regenerate each prose snapshot** + +For each fixture above, run: +```bash +cargo run -p ail -- prose examples/.ail \ + > examples/.prose.txt +``` + +The `ail prose` subcommand emits Form-B; this file becomes the +committed snapshot used by `crates/ailang-prose/tests/snapshot.rs`. + +- [ ] **Step 2: `cargo test --workspace -p ailang-prose` green** + +Run: `cargo test --workspace -p ailang-prose` +Expected: 0 failures. The snapshot test will compare the +regenerated `.prose.txt` to the prose-renderer's current output +of the migrated fixture; they must match because we generated +them with the very command the test uses. + +- [ ] **Step 3: `cargo test --workspace` green** + +Run: `cargo test --workspace` +Expected: 0 failures (no regression in any other crate). + +--- + +## Task 4: Compiler-side deletion (lockstep) + +**Files:** +- Modify: `crates/ailang-check/src/builtins.rs` + (lines 5, 40, 269-302, 349, 350, 352, 593-635) +- Modify: `crates/ailang-codegen/src/lib.rs` + (lines 2302-2323, 2324-2345, 2373-2411, 3580-3617) +- Modify: `crates/ailang-codegen/src/synth.rs:215` + +- [ ] **Step 1: Delete the three `env.effect_ops.insert` blocks** + +In `crates/ailang-check/src/builtins.rs`, delete: +- lines 269-276 (`io/print_int` block) +- lines 277-284 (`io/print_bool` block) +- lines 293-302 (floats-iter comment + `io/print_float` block) + +Keep the surviving `io/print_str` block (between or after; verify +by reading the file post-edit). + +- [ ] **Step 2: Delete the three `list()` rows** + +In `crates/ailang-check/src/builtins.rs`, delete: +- line 349 (`io/print_int` row) +- line 350 (`io/print_bool` row) +- line 352 (`io/print_float` row) + +Keep line 351 (`io/print_str` row). Verify by re-reading. + +- [ ] **Step 3: Delete the `install_io_print_float_signature` test** + +In `crates/ailang-check/src/builtins.rs`, delete lines 593-635 +(test fn + its 7-line doc-comment). This is the ONE test that +exists matching the spec's "three install_io_print__signature" +description — see Errata #1 above. + +- [ ] **Step 4: Swap the module-doc-comments** + +In `crates/ailang-check/src/builtins.rs`: +- line 5 — swap `io/print_int` reference to `io/print_str`. +- line 40 — same swap in `EffectOpSig.ret` doc. + +- [ ] **Step 5: Delete the three codegen `lower_effect_op` arms** + +In `crates/ailang-codegen/src/lib.rs`: +- lines 2302-2323 — delete `"io/print_int" => { ... }` arm. +- lines 2324-2345 — delete `"io/print_float" => { ... }` arm. +- lines 2373-2411 — delete `"io/print_bool" => { ... }` arm. + +Keep the `"io/print_str"` arm (lines 2346-2372). Verify by +re-reading. + +After deletion, search the file for orphaned format-string +interns (`"fmt_int"`, `"%lld\n"`, `"fmt_float"`, `"%g\n"`, +`"fmt_true"`, `"fmt_false"`). If they are no longer referenced +anywhere in the crate, delete the corresponding `out.push_str` +or `intern` calls too. If they ARE still referenced (e.g. +`"%g\n"` shared with another path), leave them. + +- [ ] **Step 6: Delete the `lowers_io_print_float` test** + +In `crates/ailang-codegen/src/lib.rs`, delete lines 3580-3617 +(test + doc-comment). + +- [ ] **Step 7: Update `synth.rs` builtin_effect_op_ret** + +In `crates/ailang-codegen/src/synth.rs:215`, remove +`"io/print_int" | "io/print_bool" | "io/print_float"` from the +match arm pattern. Keep `"io/print_str"`. The match arm body +(returning `Type::unit()`) stays unchanged for the surviving +op. + +- [ ] **Step 8: RED test flips GREEN** + +Run: `cargo test --workspace -p ailang-check --test no_per_type_print_ops` +Expected: PASS. The Task-1 RED test is now satisfied because +the three retired ops no longer appear in `env.effect_ops`. + +- [ ] **Step 9: `cargo test --workspace` green** + +Run: `cargo test --workspace` +Expected: 0 failures. Any failure here means a stale `io/print_` +reference survived in a fixture or test body — those are addressed +in Task 5; expect Task-5 sites to fail and proceed to Task 5. + +--- + +## Task 5: In-tree test-body migration + +**Files:** +- Modify: `crates/ailang-check/src/lib.rs:3871-3877, 5047-5051` +- Modify: `crates/ailang-core/tests/spec_drift.rs:64-71` +- Modify: `crates/ailang-core/tests/design_schema_drift.rs:87-94` +- Modify: `crates/ailang-surface/src/lex.rs:320, 328` +- Modify: `crates/ailang-prose/src/lib.rs:1585-1593` + +- [ ] **Step 1: Migrate `ailang-check/src/lib.rs:3871-3877`** + +The site is an undeclared-effect-diagnostic test. The op name +is incidental — the test asserts "calling an unknown effect-op +produces diagnostic X". Swap `op: "io/print_int"` → `op: +"io/print_str"` AND change the arg from an `Int` literal to a +`Str` literal so the swapped op type-checks. Then change the +expected diagnostic's `effect_op` field if it's pinned by name. + +If the test is asserting "unknown effect-op", the spirit is +preserved by inventing a never-registered name like +`"io/print_unknown"` and the test continues to assert "diagnostic +produced". Use whichever shape is closest to the original intent +— inspect the test body to decide. + +- [ ] **Step 2: Migrate `ailang-check/src/lib.rs:5047-5051`** + +Tail-call-in-tail-position test. Same shape as Step 1; the op +is incidental. Swap to `io/print_str` + `Str` arg. + +- [ ] **Step 3: Migrate `spec_drift.rs:64-71`** + +JSON-shape drift test on `Term::Do`. The op identity is the +serialised payload's key, not the load-bearing assertion. +Swap to `io/print_str` + `Str` arg. + +- [ ] **Step 4: Migrate `design_schema_drift.rs:87-94`** + +Same shape as Step 3. + +- [ ] **Step 5: Migrate `lex.rs:320, 328`** + +`tokenize("+ == <= io/print_int std_list.map")` exercises that +the lexer treats `io/print_int` as a single identifier-with-slash +token. The op name is incidental; the assertion is on the token +shape. Swap to `io/print_str`. Update the `assert_eq!` vector +at line 328 to match. + +- [ ] **Step 6: Migrate `prose/src/lib.rs:1585-1593`** + +`do_renders_with_keyword_and_op` round-trip test. Asserts +`render_term(&t) == "do io/print_int(5)"`. Swap to +`render_term(&t) == "do io/print_str(\"5\")"` (with `Str` literal +arg) OR pick a different surviving op if `io/print_str` makes +the test awkward. + +- [ ] **Step 7: `cargo test --workspace` green** + +Run: `cargo test --workspace` +Expected: 0 failures. Task 5 closes the test-body migration set. + +--- + +## Task 6: Documentation-comment touch-ups + +**Files:** +- Modify: `crates/ailang-surface/src/lex.rs:18-20` +- Modify: `crates/ailang-surface/src/parse.rs:1377` +- Modify: `crates/ail/src/main.rs:30-31, 1419-1420` +- Modify: `runtime/str.c:117-118` +- Modify: `crates/ailang-core/specs/form_a.md:433-434` + +- [ ] **Step 1: Swap `lex.rs:18-20` module-doc-comment** + +Replace the `io/print_int` example with `io/print_str` in the +qualified-name-tokenisation explanation. + +- [ ] **Step 2: Swap `parse.rs:1377` diagnostic example** + +`expect_ident("effect op (e.g. `io/print_int`)")` → swap to +`io/print_str`. + +- [ ] **Step 3: Swap `main.rs:30-31` and `:1419-1420`** + +`main.rs:30-31`: `builtins` subcommand doc — list a single +`io/print_str` (drop the per-type ops). +`main.rs:1419-1420`: `walk_term` Do-arm format-string comment +— swap to `io/print_str`. + +- [ ] **Step 4: Re-anchor `runtime/str.c:117-118`** + +Original: `matches io/print_float's libc rendering for IEEE +consistency`. Rewrite to: `matches the same %g format used +throughout the runtime for Float text rendering` (or similar — +the anchor is the format, not the now-retired effect-op). + +- [ ] **Step 5: Migrate `form_a.md:433-434`** + +The surface-spec example shows `(do io/print_int n)`. Migrate +to `(app print n)` to advertise the post-iter canonical idiom. + +- [ ] **Step 6: `cargo test --workspace` green** + +Run: `cargo test --workspace` +Expected: 0 failures. (Doc-comment changes can break tests if +they're embedded in doc-tests; verify.) + +--- + +## Task 7: DESIGN.md sweep + +**Files:** +- Modify: `docs/DESIGN.md` lines 331, 1990-1992, 2024-2025, 2331, + 2565-2577, 2679, 2699. + +- [ ] **Step 1: Line 331 — Decision 11 example** + +Original (excerpt): "names like `io/print_int`". Swap to +`io/print_str`. + +- [ ] **Step 2: Lines 1990-1992 — Polymorphic print past-tense rewrite** + +Original: +> Routing through `print` replaces the ad-hoc `io/print_int|bool|float` +> idiom for new code; retiring the per-type effect-ops is queued +> as a P2 follow-up. + +Rewrite: +> Routing through `print` replaces the ad-hoc per-type print +> effect-ops; the `io/print_int`, `io/print_bool`, `io/print_float` +> primitives were retired 2026-05-14 in iter rpe.1, leaving +> `io/print_str` as the only surviving direct-output effect-op. + +- [ ] **Step 3: Lines 2024-2025 — Heap-Str primitives output sentence** + +Original: +> Primitive output goes through `io/print_int` / `io/print_bool` / +> `io/print_str` directly. + +Rewrite: +> Primitive output for `Str` values goes through `io/print_str` +> directly; values of other primitive types route through the +> polymorphic `print` helper (§"Polymorphic print"), which feeds +> the heap-Str result of `show x` into `io/print_str`. The +> per-type effect-ops `io/print_int`, `io/print_bool`, +> `io/print_float` were retired in iter rpe.1 (2026-05-14). + +- [ ] **Step 4: Line 2331 — Effect-op invocation comment example** + +Original: `// Effect-op invocation. \`op\` is "/" +(e.g. "io/print_int").` +Rewrite: `// Effect-op invocation. \`op\` is "/" +(e.g. "io/print_str").` + +- [ ] **Step 5: Lines 2565-2577 — Float NaN-rendering paragraph** + +The paragraph anchors NaN textual semantics on `io/print_float` +and contrasts with `do print x`. After iter rpe.1, both ends of +that contrast reach `%g` via `float_to_str` + `io/print_str`. + +Rewrite the paragraph to anchor on `float_to_str` (the canonical +heap-Str producer for Float) rather than the now-retired +`io/print_float`. The `%g` semantics are unchanged; this is a +re-anchoring, not a semantic change. + +- [ ] **Step 6: Line 2679 — "What is supported (effect ops)"** + +Original: `(\`io/print_int\`, \`io/print_bool\`, \`io/print_str\`)` +Rewrite: `(\`io/print_str\`)`. Add a sentence: `Per-type print +ops (\`io/print_int\`, \`io/print_bool\`, \`io/print_float\`) +were retired in iter rpe.1; polymorphic \`print\` is the canonical +output path.` + +- [ ] **Step 7: Line 2699 — "(IO effect ops)" list** + +Original: `the IO effect ops (\`io/print_int|bool|str|float\`)` +Rewrite: `the IO effect op \`io/print_str\``. + +- [ ] **Step 8: `cargo test --workspace` green** + +Run: `cargo test --workspace` +Expected: 0 failures. The `design_schema_drift` test (mentioned +in Task 5) reads DESIGN.md; verify it still passes after these +swaps. + +--- + +## Task 8: Incidental E2E test-comment touch-ups + +**Files:** +- Modify: `crates/ail/tests/eq_ord_e2e.rs:66, 76` +- Modify: `crates/ail/tests/e2e.rs:2674-2683` +- Modify: `crates/ail/tests/floats_e2e.rs:7` + +- [ ] **Step 1: Update `eq_ord_e2e.rs` comments** + +Lines 66 and 76 reference `io/print_int` emitting `\n`. +The asserted stdout is unchanged (post-migration `(app print x)` +at `x : Int` also emits `\n`). Update the comments to +reference `print` semantics. The test body itself does not +change (it was migrated in Task 2 via the fixture migration). + +- [ ] **Step 2: Update `e2e.rs:2674-2683` (int_arg_to_effect_op test)** + +The test pins the RC-tracking property "Int args passed to an +effect-op are not consumed". The fixture `int_to_print_int_borrow.ail` +migrates in Task 2. The test name and doc-comment lose +grounding once `io/print_int` is gone. + +Two options: +- Update the doc-comment to explain that the test now pins + "Int args passed to `print` (the polymorphic helper) are not + consumed" — the property is the same. +- Rename the test if Boss prefers (e.g. + `int_arg_to_print_does_not_rc_track`). Renaming changes a + test identity; lean toward keeping the name and updating the + doc. + +Pick keep-the-name-and-update-the-doc. The test pins the +property, not the identifier. + +- [ ] **Step 3: Update `floats_e2e.rs:7` module doc-comment** + +Lists `io/print_float` as a covered op. Swap to `print` / Show +Float. + +- [ ] **Step 4: `cargo test --workspace` green** + +Run: `cargo test --workspace` +Expected: 0 failures. + +--- + +## Task 9: Bench run + conditional ratification + +**Files:** +- Conditional Modify: `bench/baseline.json`, `bench/baseline_cross_lang.json`, + `bench/baseline_compile.json`. + +- [ ] **Step 1: Run `bench/check.py`** + +Run: `python3 bench/check.py` +Expected exit code: `0` (compile-time-symmetric). If `1` +(drift), investigate before ratifying — the spec did not +anticipate a check-perf change. + +- [ ] **Step 2: Run `bench/compile_check.py`** + +Run: `python3 bench/compile_check.py` +Expected exit code: `0`. Same rationale. + +- [ ] **Step 3: Run `bench/cross_lang.py`** + +Run: `python3 bench/cross_lang.py` +Expected exit code: `0` OR `1` (legitimate drift if any bench +fixture's hot loop now allocates a heap-Str per print). + +- [ ] **Step 4: Conditional ratification — if any script returned `1`** + +For each script that returned `1`: +1. Inspect the per-fixture report to identify which fixtures + moved. +2. Run `python3