# rpe.1 — Retire per-type print effect-ops — Implementation Plan > **Parent spec:** `docs/specs/0027-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` - Modify: `crates/ail/tests/e2e.rs` — six IR-shape tests at lines 1408, 1628, 1710, 1800, 1936, 2013 (Cat B test-harness patch, see below). **Cat B test-harness patch (added 2026-05-14 after the rpe.1 BLOCKED orchestrator run):** Six IR-shape tests in `crates/ail/tests/e2e.rs` build their own `lifted_ws` via a `lift_letrecs` loop and lower it via `lower_workspace_with_alloc`, skipping `monomorphise_workspace`. Pre-migration this was fine — the fixtures used `(do io/print_int x)`, a directly-recognised effect-op. Post-migration they use `(app print x)` (polymorphic free fn), which requires mono to resolve to `print__Int`. Without the mono pass, lowering errors with `UnknownVar("print")`. The fix is identical at all six sites: replace the home-rolled `desugar+lift` loop with a single call to `ailang_check::monomorphise_workspace(&ws)?` and lower the resulting `MonomorphisedWorkspace`. Sites to patch: - `reuse_as_demo_under_rc_uses_inplace_rewrite` (line 1408) - `alloc_rc_borrow_only_recursive_list_drop` (line 1628) - `alloc_rc_partial_drop_skips_moved_keeps_wildcarded` (line 1710) - `alloc_rc_own_param_dec_at_fn_return` (line 1800) - `iter18e_drop_iterative_emits_worklist_body_no_self_recursion` (line 1936) - `iter18e_no_annotation_keeps_recursive_drop_body` (line 2013) - [ ] **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