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.
29 KiB
rpe.1 — Retire per-type print effect-ops — Implementation Plan
Parent spec:
docs/specs/0027-retire-per-type-print-effects.mdFor agentic workers: REQUIRED SUB-SKILL: use
skills/implementto 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:
- Spec §C2 says "three install_io_print__signature tests
(lines ~593–633)". Only
install_io_print_float_signatureexists (builtins.rs:600). The plan deletes the one that exists, not three. - Spec §D1 says "98 examples/*.ail files". Actual: 92
.ail- 6
.prose.txtsnapshot siblings = 98. The plan migrates both, but a.prose.txtis regenerated, not text-substituted.
- 6
crates/ailang-codegen/src/synth.rs:215carries a parallel effect-oprettable (builtin_effect_op_ret) the spec §C2 does not name. Lockstep delete with the codegen arms.- Five test bodies + four doc-comment sites carry
io/print_intas 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.ccomment). 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 oncrates/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 viaail 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.retdoc 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_floatinsert. - line 349 — delete
io/print_introw inlist(). - line 350 — delete
io/print_boolrow. - line 352 — delete
io/print_floatrow. (Line 351 —io/print_str— survives.) - lines 593-635 — delete
install_io_print_float_signaturetest fn (incl. its 7-line doc-comment).
- line 5 — module-doc-comment swap
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_floattest (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 bygreppost-edit.
- lines 2302-2323 — delete
crates/ailang-codegen/src/synth.rs:215— remove"io/print_int" | "io/print_bool" | "io/print_float"from thematch namearm; 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 wasInt, change it to aStrliteral that types as Str. - lines 5047-5051 (tail-call-in-tail-position test) — same shape.
- lines 3871-3877 (undeclared-effect-diagnostic test) — swap
crates/ailang-core/tests/spec_drift.rslines 64-71 — swap toio/print_strwith aStrarg.crates/ailang-core/tests/design_schema_drift.rslines 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 toio/print_str. - line 328 — corresponding
assert_eq!vector swap.
crates/ailang-prose/src/lib.rslines 1585-1593 — round-trip test assertingrender_term(&t) == "do io/print_int(5)": swap toio/print_strwith 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%gdirectly.crates/ailang-core/specs/form_a.md:433-434— surface-spec example: migrate to either(do io/print_str s)with aStrliteral, 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 nameprintsemantics.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.mdP2 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>.ailfiles (full list deterministic fromgrep -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 (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:
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 --workspacegreen
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-prosegreen
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 --workspacegreen
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.insertblocks
In crates/ailang-check/src/builtins.rs, delete:
- lines 269-276 (
io/print_intblock) - lines 277-284 (
io/print_boolblock) - lines 293-302 (floats-iter comment +
io/print_floatblock)
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_introw) - line 350 (
io/print_boolrow) - line 352 (
io/print_floatrow)
Keep line 351 (io/print_str row). Verify by re-reading.
- Step 3: Delete the
install_io_print_float_signaturetest
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_intreference toio/print_str. -
line 40 — same swap in
EffectOpSig.retdoc. -
Step 5: Delete the three codegen
lower_effect_oparms
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_floattest
In crates/ailang-codegen/src/lib.rs, delete lines 3580-3617
(test + doc-comment).
- Step 7: Update
synth.rsbuiltin_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 --workspacegreen
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 --workspacegreen
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-20module-doc-comment
Replace the io/print_int example with io/print_str in the
qualified-name-tokenisation explanation.
- Step 2: Swap
parse.rs:1377diagnostic example
expect_ident("effect op (e.g. io/print_int)") → swap to
io/print_str.
- Step 3: Swap
main.rs:30-31and: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 --workspacegreen
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.mdlines 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
io/print_int|bool|floatidiom for new code; retiring the per-type effect-ops is queued as a P2 follow-up.
Rewrite:
Routing through
io/print_int,io/print_bool,io/print_floatprimitives were retired 2026-05-14 in iter rpe.1, leavingio/print_stras 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_strdirectly.
Rewrite:
Primitive output for
Strvalues goes throughio/print_strdirectly; values of other primitive types route through the polymorphicshow xintoio/print_str. The per-type effect-opsio/print_int,io/print_bool,io/print_floatwere 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 --workspacegreen
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.rscomments
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:7module doc-comment
Lists io/print_float as a covered op. Swap to print / Show
Float.
- Step 4:
cargo test --workspacegreen
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:
- Inspect the per-fixture report to identify which fixtures moved.
- Run
python3 <script> --update-baselineto write the new baseline. - Note in the per-iter journal: which baseline was updated,
the magnitude of the drift, and the language reason
(post-iter
printheap-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.mdP2 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 perimplement/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 --workspacecargo clippy --workspace --all-targets -- -D warningscargo 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
-
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).
-
Placeholder scan: zero "TBD" / "TODO" / "similar to Task".
-
Type consistency:
env.effect_ops(the BTreeMap onEnv),lower_effect_op(the codegen dispatcher),build_check_env(entry point used by the RED test) all match real symbol names in the workspace. -
Step granularity: every numbered step is 2–5 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.
-
No commit steps: zero
git commitcalls in any task template. The orchestrator-agent (or implement-skill) leaves the working tree dirty; Boss commits.