fieldtest: loop-recur — 10 examples, 6 findings (milestone CLOSE, clean on all axes)

Post-audit downstream-LLM-author field test of the shipped
loop/recur surface (DESIGN.md + public examples only). 3 real
iterative programs (Newton isqrt, Collatz, Euclidean gcd) + 5
plausible-mistake negatives + 2 no-termination probes, all run
through the public ail CLI. 0 bugs. 4 working findings on the
milestone's own axes: rejection diagnostics point-exact AND
self-fixing; recur tail-position threads through match/let/outer-if
(spec only showed if); loop composes as a value sub-expression +
byte-stable round-trip; no-termination boundary exact. This
empirically substantiates the "LLM author can now write iterative
programs" claim.

Two orthogonal non-blocking findings, neither in loop/recur scope,
both routed to P2 todos (refused the scope creep into a loop/recur
tidy): niladic (app f) spec_gap independently re-confirms the
existing mut-local-F3 roadmap item (the design-fork decision
deliberately NOT auto-ratified under /boss — parked,
priority-strengthened); module-level (doc) diagnostic-hint friction
(one-line tidy). Boss-verified independently (gcd->27;
recur-outside-loop fires exact).

The standalone loop/recur milestone is fully ratified and CLOSED:
3 iterations + tidy shipped, audit clean (drift resolved, bench
pristine carry-on), fieldtest clean on every axis. Roadmap P0
marked closed.
This commit is contained in:
2026-05-18 00:31:19 +02:00
parent 2ee97943bd
commit 2ed355c6fa
13 changed files with 471 additions and 18 deletions
+39 -18
View File
@@ -35,20 +35,22 @@ work progresses.
## P0 — In flight
- [~] **\[milestone\]** Standalone `loop` / `recur`strictly-additive
iteration surface, `recur` valid only in tail position. All three
iterations + a tidy shipped: iter 1 `a179ec3` (additive AST nodes),
- [x] **\[milestone\]** Standalone `loop` / `recur`CLOSED 2026-05-18.
Strictly-additive strict-iteration surface, `recur` tail-position-only,
no totality claim. Shipped: iter 1 `a179ec3` (additive AST nodes),
iter 2 `1566ce0` (typecheck), iter 3 `edd2558` (codegen + run-to-value
E2E), tidy (lambda-captures-loop-binder rejected at check, symmetric
to mut.4-tidy). Milestone-close `audit` clean: architect drift
resolved (the `[high]` codegen crash fixed in the tidy + two
`[medium]` doc-honesty edits folded in), bench pristine (25 metrics,
0 regressed — strictly-additive surface, no hot-path touched).
**Remaining: `fieldtest`** (loop/recur is user-visible Form-A
surface) — the only step before full close.
- context: `docs/specs/2026-05-17-loop-recur.md`; principles entry
`docs/specs/2026-05-17-llm-surface-discipline.md` §6.2; revert
rationale `docs/specs/2026-05-16-iteration-discipline-revert.md`.
E2E), tidy `39380d3`/`2ee9794` (lambda-captures-loop-binder rejected
at check). Milestone-close `audit` clean (architect drift resolved,
bench pristine 25/0 carry-on); `fieldtest` 2026-05-18 clean on all
four milestone axes (0 bugs; diagnostics point-exact+self-fixing,
recur-tail through match/let, loop-as-value-subexpr byte-stable,
no-termination exact). Two orthogonal non-blocking findings routed
to P2 todos (niladic `(app f)` — re-confirms mut-local F3; module
`(doc)` diagnostic hint). Stays here briefly for context; remove
once stale (full record in `docs/journals/INDEX.md`).
- context: `docs/specs/2026-05-17-loop-recur.md`;
`docs/specs/2026-05-18-fieldtest-loop-recur.md`; principles entry
`docs/specs/2026-05-17-llm-surface-discipline.md` §6.2.
## P1 — Next
@@ -294,11 +296,30 @@ work progresses.
- [ ] **\[todo\]** Zero-arg `(app f)` rejected at parse — the Form-A
parser refuses an application with an empty argument list, so a
nullary call has no surface form. Surfaced by the mut-local
fieldtest (F3). Decide: accept `(app f)` as the nullary-call
surface, or ratify in DESIGN.md that nullary functions are
expressed differently (and say how). Not a blocker; no current
corpus program needs it, but an LLM author reaches for it.
- context: `docs/specs/2026-05-15-fieldtest-mut-local.md` finding F3.
fieldtest (F3) AND **independently re-confirmed by the loop/recur
fieldtest** (2026-05-18, finding spec_gap): the maximally-natural
infinite-event-loop shape is a niladic `run_forever : fn() -> Int`
called `(app run_forever)`, which dies at parse before reaching
loop/recur semantics. Two independent fieldtests hitting the same
gap raises the priority signal. Decide: accept `(app f)` as the
nullary-call surface, or ratify in DESIGN.md that nullary functions
are expressed differently (and say how). DESIGN.md's `Term::App`
`args:[Term...]` states no minimum, so the surface "expected at
least one argument" rule is unbacked by spec — a genuine design
fork, deliberately NOT auto-ratified under autonomous orchestration.
Not a blocker; no current corpus program needs it, but an LLM
author reaches for it.
- context: `docs/specs/2026-05-15-fieldtest-mut-local.md` finding F3;
`docs/specs/2026-05-18-fieldtest-loop-recur.md` spec_gap.
- [ ] **\[todo\]** Module-level `(doc …)` diagnostic omits where `doc`
belongs — a `(module NAME (doc "…") (fn …))` is correctly rejected
(`unknown def head \`doc\``) but the message lists valid def heads
without saying doc strings attach *inside* `fn`/`data` defs; an
author can read it and conclude doc strings are unsupported. One-line
tidy: append a hint like "(doc strings attach inside `fn`/`data`
defs, not at module level)". Low cost; verbose-diagnostic-philosophy
gap, not a bug.
- context: `docs/specs/2026-05-18-fieldtest-loop-recur.md` friction.
- [ ] **\[todo\]** Workspace search beyond entry-module's directory —
`load_workspace` only finds sibling `.ail.json` files in the same
directory as the entry module, so any consumer of prelude/std in a