# iter it.3 — retire tail-app/tail-do + corpus migration

**Date:** 2026-05-15
**Started from:** c992eb93df4c5ec0a0686c924535cb9951be552f
**Status:** BLOCKED
**Tasks completed:** 1 of 7 (Task 1 complete; BLOCKED at the
Task-1.3 → Task-2 boundary before any destructive task)

## Summary

Third and terminal iteration of the iteration-discipline milestone
(destructive: retire `tail-app`/`tail-do`, migrate the corpus).
Task 1 (the non-destructive pre-migration oracle + the spec-delegated
class-(b) live enumeration) ran to completion: 40 recursive corpus
fixtures classified, 40 behavioural oracles captured under
`bench/it3-oracle/`, `MANIFEST.tsv` written. Task 1.3's mandated
live sweep surfaced a **real spec-signal** that the plan's own
self-review (lines 550-560) and the carrier explicitly defined as a
BLOCKED-to-Boss condition, not a silent reshape: **six fixtures
contain a recursive fn that resists BOTH a natural `(loop)`/`recur`
form AND a structural-plain form.** Per DD-2/DD-3 (migrate before
remove; Task 4's completeness gate must not be softened and a RED
there is a Task-2 defect, never a restored exemption) there is **no
in-plan migration target** for these six. The destructive tasks
(4/5) were therefore not started — correctly, since the migration
they depend on is impossible for this subset and DD-3 forbids
working around it. No production code was modified; the working
tree contains only the new `bench/it3-oracle/` artefacts.

## Per-task notes

- iter it.3.1: Pre-migration oracle + migration-set enumeration —
  **complete.** `cargo build -p ail` clean at HEAD c992eb9.
  Class-(a): 23 `(tail-app|tail-do)` fixtures (recon-confirmed).
  Class-(b) live sweep (Task 1.3, spec-delegated): read
  `verify_structural_recursion` + the two it.2 exemptions
  (`lib.rs:3027` `if !*tail` grandfather; `lib.rs:3197`
  `if cand.is_empty() && group.members.is_empty()` no-ADT skip),
  then classified every self-recursive corpus fn against the
  post-Task-4 verdict logic (`lib.rs:3219-3267`). Authoritative
  result: 13 no-ADT-candidate single-recursion fns are
  loop-migratable; 11 fixtures recurse structurally on an ADT param
  (clean before AND after Task 4 — no migration, only the redundant
  `tail-app` marker if class-(a) is dropped); **6 fixtures have a
  no-ADT-candidate DOUBLE non-tail recursion (binary-tree build)
  that is neither structural nor loop-expressible.** Oracles
  captured for all 40 (`bench/it3-oracle/<stem>.out` + `MANIFEST.tsv`).
  Task 1.5 fallout list recorded below for the eventual unblocked
  re-dispatch. No code changed (Task 1 is non-destructive by spec).

- iter it.3.2 … it.3.7: not started. Task 2 (migration) has no
  valid action for the 6 blocked fixtures; Tasks 4/5 are
  destructive and gated behind a complete Task-2 migration that
  cannot be completed. Halted at the spec-signal per carrier.

## Concerns

- **The RC-RSS / 18g.1 load-bearing risk (DD-4 / Task 5.5 / 5b) was
  NOT reached.** The iter blocked before Task 5, so the 18g.1
  husk-dec deletion and the RC-RSS bench gate were never exercised.
  No RC-RSS evidence exists from this dispatch. The milestone-close
  audit must NOT treat the RC-RSS contingency as resolved — it is
  untested. When the spec defect below is resolved and it.3 is
  re-dispatched, Task 5.5 (and conditionally 5b) is still the
  milestone's load-bearing risk and must be run + its evidence
  journalled then.

## Known debt

- `bench/it3-oracle/` currently holds 40 `.out` snapshots +
  `MANIFEST.tsv` (41 files). They are the Task-1 deliverable and
  stay valid for a re-dispatch from the same `start_sha`. Boss
  call at commit whether to keep them under `bench/` (plan §"Files
  this plan creates": "deleted in Task 6 … or kept under `bench/`
  if cheap") — but do NOT delete before the re-dispatch; the
  re-dispatched Task 7.3 diffs against them.
- The 11 class-(a) `plaincall` fixtures (structural on an ADT param,
  carrying only a redundant `tail-app` marker) and the 11
  `already-structural` fixtures need no semantic migration — only
  marker-stripping (class-a) — and are unaffected by the blocker.
  Recorded so the re-dispatch does not re-derive this.

## Blocked detail

**Task:** 1 (the Task-1.3 spec-delegated sweep) → halts entry to
Task 2 / Task 4.

**Reason:** `spec-ambiguous` / spec defect surfaced by the mandated
live sweep — six corpus fixtures contain a recursive fn that is
**neither structural recursion (no ADT-typed parameter — `cand`
empty in `verify_structural_recursion`) nor expressible as
`(loop …)`/`recur`** (the recursion is two non-tail self-calls
building both children of a binary `Tree`; `recur` is
tail-position-only with a single back-edge, so a balanced-tree
build cannot be a tail loop). After Task 4 removes the
no-ADT-candidate skip (`lib.rs:3197`, mandated by DD-3 and the
spec's it.3 acceptance bullet "no grandfather/no-candidate skip
remains"), each of these deterministically fires
`CheckError::NonStructuralRecursion` (verdict logic `lib.rs:3219-3267`:
`cand` empty ⇒ `self_clear=false` ⇒ NSR). DD-3 forbids softening
Task 4 or restoring the exemption; the carrier and project memory
(`feedback_dont_adapt_tests_to_bugs`) forbid reshaping the
LLM-natural fixture (e.g. changing `depth: Int` to an ADT-encoded
`Nat`, or hand-rolling an explicit stack/continuation). There is
**no in-plan migration target**, so the destructive split cannot
proceed.

**The six fixtures (fn :: shape):**

1. `examples/bench_tree_walk.ail` :: `build_tree`
   (`(if (== depth 0) (Tree Leaf) (Tree Node 1 (build_tree (- depth 1)) (build_tree (- depth 1))))`)
2. `examples/bench_latency_explicit.ail` :: `build_tree` (same shape; file is class-(a) for other fns but `build_tree` itself is non-tail double recursion)
3. `examples/bench_latency_implicit.ail` :: `build_tree` (same)
4. `examples/rc_let_owned_app_leak.ail` :: `build` (same Tree shape on `d: Int`)
5. `examples/rc_pin_recurse_implicit.ail` :: `build` (same; the RC-regression-guard fixture from it.2 Concerns §2)
6. `examples/rc_let_alias_implicit_param.ail` :: `build` (same; the RC-regression-guard fixture from it.2 Concerns §2)

This is exactly the spec-signal the it.2 journal §1 foreshadowed
(it named `bench_latency_explicit:build_tree` as a no-ADT-candidate
case "whose migration to `(loop …)` is the destructive it.3 corpus
pass") and the plan self-review's "Recorded risk" anticipated
("a genuine non-structural recursion with no natural loop form …
surfaces as a Task-2 BLOCKED to the Boss with the fixture named").
The spec/plan assumed every no-ADT-candidate recursion is a
loopable counter/accumulator; balanced-binary-tree construction by
double recursion on a depth counter is the counter-example.

**Suggested next step:** Boss-level spec decision required (this is
not an implementer or planner judgement call — see
`feedback_spec_over_plan_patches`: a spec defect goes back through
`brainstorm`, not a fourth plan patch). Candidate resolutions for
the Boss to weigh, language-first:
(a) **Add a structural accommodation for tree recursion** — extend
`verify_structural_recursion` so a non-tail recursive call on a
*structurally-derived smaller value* is guarded even when the
decreasing quantity is a primitive depth counter feeding an ADT
build (i.e. recognise the `build_tree` shape as bounded). This
keeps the six fixtures verbatim and is the only option that does
not reshape LLM-natural code, but it widens it.2's structural
checker — a Decision-10/Decision-3 spec change, brainstorm-gated.
(b) **Carve the six into a permanent allow-list / carve-out** (like
the existing `.ail.json` carve-outs) with an explicit
"constructor-blocked recursion, depth-bounded by construction"
annotation, leaving the no-ADT-candidate skip retired for
everything else. Requires a spec sentence defining the carve-out
predicate.
(c) **Retire the bench fixtures that exist only to exercise the
removed `musttail`/RC-RSS path** (`bench_latency_*`,
`rc_pin_recurse_implicit`, `rc_let_alias_implicit_param`,
`rc_let_owned_app_leak`) and replace `bench_tree_walk`'s
`build_tree` coverage with a structural equivalent — but this is
the "adapt the fixture to dodge" path the carrier and
`feedback_dont_adapt_tests_to_bugs` explicitly forbid for
language-limitation cases; named only for completeness, not
recommended.
Once the Boss resolves the spec, re-dispatch it.3 from the same
`start_sha` (the `bench/it3-oracle/` artefacts remain valid).

## Files touched

Created (untracked, non-code):
- `bench/it3-oracle/MANIFEST.tsv` (40-fixture classification)
- `bench/it3-oracle/<stem>.out` × 40 (behavioural oracle:
  `ail run` stdout for `main` fixtures, `ail check` output for
  check-only)

No production code, no spec, no test files modified (Task 1 is
non-destructive by plan; the BLOCKED halted before any code-edit
task).

## Task 1.5 fallout map (recorded for the unblocked re-dispatch)

Tail-app-specific tests the plan's Task 6.4 will delete:
`crates/ailang-prose` `app_tail_renders_with_keyword`,
`binop_with_tail_flag_keeps_prefix_form`,
`not_with_tail_flag_keeps_prefix_form`;
`crates/ailang-core/tests/spec_drift.rs::spec_mentions_tail_variants`;
`crates/ailang-check` `TailCallNotInTailPosition` pins
(`tail_call_in_tail_position_is_accepted` /
`..._non_tail_position_is_rejected` if present); any
`iter14e_*musttail*` e2e. (Enumeration deferred to the
re-dispatch's Task 1.5 against the then-current tree, since the
exact test names will drift; recorded here as the recon seed.)

## Stats

bench/orchestrator-stats/2026-05-15-iter-it.3.json
