# iter eob.1 — Effect-op args walked as Borrow; heap-Str RC discipline closes **Date:** 2026-05-12 **Started from:** b5b0c2d7dcd8bec1819801257de7731edc542e0f **Status:** DONE **Tasks completed:** 7 of 7 ## Summary Land the language rule that `Term::Do.args[*]` are walked in `Position::Borrow` by the uniqueness and linearity passes, plus the two heap-Str-specific shape fixes (`ret_mode: Own` for `int_to_str` / `float_to_str`; Str carve-out in the App arm of `drop_symbol_for_binder`) that make the existing RED tests at `crates/ail/tests/e2e.rs` flip to GREEN under `--alloc=rc`. Both arg-position rules (Ctor = Consume, Do = Borrow) are anchored in DESIGN.md §Decision 10. The heap-str-abi milestone closes here: WhatsNew entry shipped, roadmap P1 entry checked off, downstream Post-22-Prelude `depends on:` line removed. The five plan layers landed first-shot in lockstep — no review re-loops fired on any of the 7 tasks. The pre-existing RED tests (`int_to_str_drop_balances_rc_stats`, `str_field_in_adt_drops_heap_str_correctly`) flipped to GREEN with the predicted concrete numbers (`allocs == 1, frees == 1, live == 0` and `allocs == 2, frees == 2, live == 0`). Two new positive tests pin the rule's coverage at the primitive boundary (Int through `io/print_int`: zero RC traffic) and at the linearity boundary (heap-Str through two sequential `io/print_str` calls: allocs == 1, frees == 1, live == 0). Workspace test --workspace + cross_lang.py + compile_check.py all green. check.py shows the same noisy latency-max + bump_s cluster the audit-cma and audit-ms journals both left pristine ("not coupled to milestone, baseline left untouched for second consecutive audit") — same precedent applies here; baseline left as-is. ## Per-task notes - iter eob.1.1: Term::Do arg-walks flip Consume → Borrow in both uniqueness.rs:289 and linearity.rs:506; linearity.rs:42 doc comment split into two bullets matching the new code shape. Inline clarifying comment added on the linearity-side flip mirroring the Ctor-arm convention. cargo build + workspace check-crate tests green. - iter eob.1.2: builtins.rs `float_to_str` (:200) + `int_to_str` (:210) and synth.rs `float_to_str` (:172) + `int_to_str` (:179) all flip `ret_mode: Implicit` → `ret_mode: Own` in lockstep. Workspace build clean. Target test now reports the predicted link-error shape "undefined value @drop_int_to_str_drop_rc_Str" — exactly the gap Task 3 closes. - iter eob.1.3: drop.rs App arm of `drop_symbol_for_binder` gets the Str carve-out `if name == "Str" return ailang_rc_dec`, symmetric to `field_drop_call`'s existing Str arm. The comment block mirrors `field_drop_call`'s wording. cargo build clean. - iter eob.1.4 (observational): both RED tests flip to GREEN with the predicted concrete numbers (allocs == 1, frees == 1 / allocs == 2, frees == 2; live == 0 on both). Full e2e suite (79 tests) all green — no other fixture's RC trace shifted unexpectedly. - iter eob.1.5: two new fixtures (`examples/int_to_print_int_borrow.ail.json`, `examples/heap_str_repeated_print_borrow.ail.json`) + two new e2e tests (`int_arg_to_effect_op_does_not_rc_track`, `heap_str_repeated_print_balances_rc_stats`) appended near e2e.rs:2625. Both new tests PASS first-shot with exact predicted RC stats. - iter eob.1.6: DESIGN.md §Decision 10 gets a new subsection `#### Arg-position policy for compound AST nodes` after the `ret_mode` block. The two arg-position rules (Ctor = Consume, Do = Borrow) are anchored together as language rules, not per-op annotations. One added linking paragraph explains how the Do = Borrow rule cooperates with the `ret_mode == Own` letbinder-trackability rule above to balance heap-Str RC discipline — within the plan's "Boss judgement on placement" carve-out. - iter eob.1.7: WhatsNew.md entry appended at bottom (user-facing wording, no internals). roadmap.md heap-Str ABI entry flipped `[ ]` → `[x]`. Post-22 Prelude `depends on:` line removed (the only dep was Heap-Str, which has shipped). cargo test workspace + cross_lang.py + compile_check.py all green. check.py noisy latency cluster left pristine per audit-cma/audit-ms precedent. ## Concerns (none — all 7 tasks DONE without review re-loops) ## Known debt - check.py shows recurring noisy `latency.implicit_at_rc.max_us` / `latency.explicit_at_rc.max_us` tail-jitter, plus `throughput.bench_list_sum.bump_s` swinging +11-15% across back-to-back runs (with reciprocal `gc_over_bump` improvement on the same fixture). Neither family is reachable from this iter's changes (bump_s on bench_list_sum has no effect-ops; the list-sum hot path has no int_to_str / float_to_str calls). The audit-cma and audit-ms journals already document the same noise cluster as "not coupled to milestone, baseline left pristine for two consecutive audits" — same precedent applies; baseline untouched. ## Files touched Code: - crates/ailang-check/src/uniqueness.rs (Term::Do Consume → Borrow) - crates/ailang-check/src/linearity.rs (Term::Do Consume → Borrow + doc-block split) - crates/ailang-check/src/builtins.rs (int_to_str / float_to_str ret_mode → Own) - crates/ailang-codegen/src/synth.rs (int_to_str / float_to_str ret_mode → Own) - crates/ailang-codegen/src/drop.rs (App-arm Str carve-out) Tests + fixtures: - crates/ail/tests/e2e.rs (2 new positive tests appended) - examples/int_to_print_int_borrow.ail.json (new) - examples/heap_str_repeated_print_borrow.ail.json (new) Docs: - docs/DESIGN.md (Decision 10: arg-position policy subsection) - docs/WhatsNew.md (heap-str-abi milestone close entry) - docs/roadmap.md (Heap-Str ABI P1 closed; Post-22 Prelude depends-on line removed) ## Stats bench/orchestrator-stats/2026-05-12-iter-eob.1.json