it.3 Task 1 (non-destructive oracle + spec-delegated class-(b) sweep) ran complete; zero production code changed. The mandated sweep surfaced that 6 corpus fixtures contain build(d:Int)=if d==0 then Leaf else Node(1,build(d-1),build(d-1)) — a terminating, maximally-LLM-natural, non-structural non-tail BRANCHING recursion the milestone's totality dichotomy makes inexpressible (not ADT-structural; not recur-able — branching is not a single tail back-edge). The milestone as-specified would fail its own feature-acceptance criterion. Orchestrator correctly bounced rather than patch a 4th time (spec_over_plan_patches). Records the blocked attempt + the reusable bench/it3-oracle/ (40-fixture behavioural oracle, valid for the post-decision re-dispatch). main forward-only; nothing reverted. Resolution (it.2b + a user purity-pillar decision) tracked in the roadmap.
9.4 KiB
iter it.3 — retire tail-app/tail-do + corpus migration
Date: 2026-05-15
Started from: c992eb93df
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 ailclean at HEADc992eb9. Class-(a): 23(tail-app|tail-do)fixtures (recon-confirmed). Class-(b) live sweep (Task 1.3, spec-delegated): readverify_structural_recursion+ the two it.2 exemptions (lib.rs:3027if !*tailgrandfather;lib.rs:3197if 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 redundanttail-appmarker 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.outsnapshots +MANIFEST.tsv(41 files). They are the Task-1 deliverable and stay valid for a re-dispatch from the samestart_sha. Boss call at commit whether to keep them underbench/(plan §"Files this plan creates": "deleted in Task 6 … or kept underbench/if cheap") — but do NOT delete before the re-dispatch; the re-dispatched Task 7.3 diffs against them.- The 11 class-(a)
plaincallfixtures (structural on an ADT param, carrying only a redundanttail-appmarker) and the 11already-structuralfixtures 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):
examples/bench_tree_walk.ail::build_tree((if (== depth 0) (Tree Leaf) (Tree Node 1 (build_tree (- depth 1)) (build_tree (- depth 1)))))examples/bench_latency_explicit.ail::build_tree(same shape; file is class-(a) for other fns butbuild_treeitself is non-tail double recursion)examples/bench_latency_implicit.ail::build_tree(same)examples/rc_let_owned_app_leak.ail::build(same Tree shape ond: Int)examples/rc_pin_recurse_implicit.ail::build(same; the RC-regression-guard fixture from it.2 Concerns §2)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 runstdout formainfixtures,ail checkoutput 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