Files
AILang/docs/plans/0063-iter-rpe.1.md
Brummel 832375f2ac convention: counter-prefix file naming across docs/specs/, docs/plans/, design/contracts/, design/models/
All 176 files in the four accumulating directories now use a
zero-padded 4-digit counter prefix that reflects creation order
(`NNNN-slug.md`). The counter is assigned per directory in strict
git-log creation order; ties broken alphabetically by original name.
The old `YYYY-MM-DD-` prefix on docs/specs/ and docs/plans/ files is
dropped — the date is recoverable from git log and the counter
carries the ordering.

A file's counter is stable for the life of the file: never reassigned,
never reused, never compacted. Deleted files retire their counter;
subsequent files do not fill the gap. This is the property that lets
cross-references stay literal — refs use the full filename including
the counter (`design/contracts/0007-honesty-rule.md`) so they grep
cleanly and resolve directly without a glob step.

313 cross-references updated across .md/.rs/.toml/.c/.json files
(test pins, include_str! paths, design-INDEX entries, baseline notes,
runtime C comments, inter-contract markdown links incl. bare basename
and `../models/foo.md` forms).

CLAUDE.md gets a new "File-naming convention" section spelling out
the rule and rationale. skills/brainstorm/SKILL.md and
skills/planner/SKILL.md updated so new spec/plan creation produces
counter-prefixed names from the start.

The full test suite (cargo test --workspace) passes.
2026-05-28 13:31:31 +02:00

29 KiB
Raw Permalink Blame History

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 ~593633)". 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/<name>.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/<name>.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_intio/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

//! 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/<name>.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:

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 <ARG>)

to:

(app print <ARG>)

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_<T>print), and the leading keyword is replaced (doapp). 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:

cargo run -p ail -- check examples/<each-fixture> 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_<T> 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:

cargo run -p ail -- prose examples/<stem>.ail \
  > examples/<stem>.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_<T> 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 <int>\n. The asserted stdout is unchanged (post-migration (app print x) at x : Int also emits <int>\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 <script> --update-baseline to write the new baseline.
  3. Note in the per-iter journal: which baseline was updated, the magnitude of the drift, and the language reason (post-iter print heap-alloc-per-iteration is the canonical output path; old baseline reflected the retired direct-printf path).
  • Step 5: All scripts green at final state

After any ratification, re-run all three scripts; all must return 0.


Task 10: Roadmap strike + per-iter journal + stats

Files:

  • Modify: docs/roadmap.md P2 lines 80-90 (entry strike).

  • Create: docs/journals/2026-05-14-iter-rpe.1.md (per-iter journal).

  • Create: bench/orchestrator-stats/2026-05-14-iter-rpe.1.json (stats file per implement/SKILL.md).

  • Step 1: Strike the roadmap entry

In docs/roadmap.md lines 80-90 (the Retire io/print_int / io/print_bool / io/print_float P2 entry), change - [ ]- [x] and append a one-line tail naming this iter:

- [x] **\[milestone\]** Retire `io/print_int` / `io/print_bool` /
  `io/print_float` effect-ops + migrate example corpus to `print`.
  Shipped 2026-05-14 as iter rpe.1.
  - context: per-iter journal `docs/journals/2026-05-14-iter-rpe.1.md`.
  • Step 2: Write the per-iter journal

Standard journal shape: # iter rpe.1 — Retire per-type print effect-ops, sections: Summary / Decisions / Files touched / Verification / Concerns / Known debt.

The journal records the §C4 (a) decision (migrate all, including bench-latency fixtures) and any bench-baseline ratifications from Task 9.

  • Step 3: Write the stats file

JSON file at bench/orchestrator-stats/2026-05-14-iter-rpe.1.json recording: tasks completed, retries per task, blocked count, duration, cargo test --workspace final count.

  • Step 4: Final cargo test --workspace + clippy + doc

Run, in parallel:

  • cargo test --workspace
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo doc --workspace --no-deps 2>&1 | grep -c '^warning:'

Expected: tests green; clippy zero (the iter-clippy-sweep baseline holds); doc zero (the rustdoc-sweep baseline holds).

Any clippy / rustdoc regression introduced by this iter is itself a Task-N debt; fix in-task before declaring DONE.


Self-review

  1. Spec coverage: every §C/§D/§G/§I-named element has a task — RED test (Task 1, spec §G2), corpus migration (Task 2, spec §C1 + §D1), prose snapshot regen (Task 3, §D1 extension via Errata #2), four-site deletion (Task 4, spec §C2 + Errata #3), test-body migration (Task 5, Errata #4), doc-comment touch-ups (Task 6, Errata #4), DESIGN.md sweep (Task 7, spec §C3), E2E-comment touch-ups (Task 8, partial Errata #4), bench ratification (Task 9, spec §C4 (a) + §G3), roadmap strike + journal + stats (Task 10, spec §I task 7).

  2. Placeholder scan: zero "TBD" / "TODO" / "similar to Task".

  3. Type consistency: env.effect_ops (the BTreeMap on Env), lower_effect_op (the codegen dispatcher), build_check_env (entry point used by the RED test) all match real symbol names in the workspace.

  4. Step granularity: every numbered step is 25 minutes. Task 2 Step 2 is "92 file edits" but they are mechanical and the LLM can stream them with one Edit-tool call per file.

  5. No commit steps: zero git commit calls in any task template. The orchestrator-agent (or implement-skill) leaves the working tree dirty; Boss commits.