roadmap: iteration-discipline status + the it.3-surfaced blocking fork

it.1/it.2 shipped; it.2's D1 resolved the original two open spec
questions (same-family mutual rule; foldl=structural). it.3 BLOCKED
on a fundamental design-thesis gap: the totality story recognised
only ADT-structural recursion; the corpus sweep proved a second
canonical total scheme (Int-bounded well-founded recursion, incl.
branching build(d-1),build(d-1)) is overlooked and the milestone
as-specified makes it inexpressible — failing its own
feature-acceptance gate. Records the full option analysis with a
recommendation (A1: trichotomy + a documented non-negative-entry
obligation), flagged as a USER decision because it trades against
the purity pillar the user co-designed. Next: user decision →
it.2b → it.3 re-dispatch from c992eb9.
This commit is contained in:
2026-05-15 16:08:15 +02:00
parent 305ffd1367
commit d189f688b6
+88 -21
View File
@@ -40,10 +40,17 @@ clean. Pick the next milestone from P2.)_
## P1 — Next
- [ ] **\[milestone\]** Iteration discipline — structural recursion +
- [~] **\[milestone\]** Iteration discipline — structural recursion +
named `loop`/`recur`; recursion-by-call restricted; `tail-app`
retired. The iteration story of the language, decided in principle
2026-05-15. Two constructs survive, one is removed:
retired. Spec `docs/specs/2026-05-15-iteration-discipline.md`
(approved fda9b78; scope-corrected 2018178/10a0595). **Status
2026-05-15: it.1 SHIPPED (96db54d — loop/recur additive), it.2
SHIPPED (a4be1e5 — guardedness checker + first real Diverge
effect), it.3 BLOCKED on a surfaced fundamental design fork (see
"The blocking fork" below — needs a user design decision + a new
iteration it.2b before it.3 can re-dispatch).** The iteration
story of the language, decided in principle 2026-05-15. Two
constructs survive, one is removed:
- **Structural recursion stays — and becomes total-by-construction.**
A recursive call is permitted iff it is structurally guarded:
each recursive call is on a constructor-smaller component of an
@@ -85,30 +92,90 @@ clean. Pick the next milestone from P2.)_
LLM-natural *and* their bug class is closed by construction; a bare
`while` is exactly what clause 3 rejects).
**The open spec question (the real brainstorm work).** Not "whether"
but the precise definition of "structurally smaller" / the
guardedness check. Two hard points: (1) *mutual* structural
recursion (needs a measure / size over the ADT, not just a
one-argument syntactic decrease); (2) the accumulator-carrying
structural walk — `foldl`-shaped, simultaneously structural *and*
tail-shaped — the spec must decide whether it counts as structural
recursion (a plain call) or must be expressed as `loop`/`recur`.
This is the load-bearing spec decision; brainstorm + grounding-check
own it.
**Original open spec questions — RESOLVED in it.2 (a4be1e5).**
(1) *mutual* structural recursion → the conservative same-ADT-family
rule (connected-components union-find over the type-reference
graph; admits tree/forest, even/odd-over-Nat, mutual JSON; a
general size-measure ordering stays out of scope). (2) the
`foldl`-shaped accumulator-carrying structural walk → **counts as
structural recursion** (plain, pure, total — only the structural
argument must decrease; accumulator positions are unconstrained).
Both shipped and pinned.
**The blocking fork (it.3-surfaced 2026-05-15 — needs a USER
design decision; touches the purity pillar the user co-designed).**
The milestone's totality thesis recognised exactly ONE
total-by-construction recursion scheme: structural-over-an-acyclic-
ADT (Decision 10). The it.3 mandated corpus sweep proved this is
*incomplete*. Six corpus fixtures contain the single most
LLM-natural tree-builder shape:
`build(d: Int) = if d==0 then Leaf else Node(1, build(d-1), build(d-1))`.
It is provably terminating, but: **not** ADT-structural (`d` is
`Int`, no constructor-smaller component) and **not** `loop`/`recur`-
able (it is *branching/double* recursion; `recur` is a single
tail-position back-edge — a balanced-tree build is not a tail
loop; the only `loop` form is a hand-rolled explicit-stack
worklist, which no LLM writes). So the milestone *as-specified*
makes a trivially-total, maximally-natural function
**inexpressible** — failing its own feature-acceptance criterion
(clause 1: the LLM reaches for `build(d-1),build(d-1)`; clause 2:
the milestone would *remove* expressivity). This is a real
design-thesis gap, not a migration nuisance: there is a SECOND
canonical total-by-construction scheme the design overlooked —
**well-founded recursion on a strictly-decreasing non-negative
integer measure** (`f(0)=base; f(n)=…f(n-1)…`, branching allowed).
**Decision the user must make** (it trades against the purity
pillar — Int ≠ ; AILang has no `Nat`/refinement types, so
Int-bounded recursion is total only for *non-negative entry*):
- **A1 (RECOMMENDED).** Make the totality story a *trichotomy*:
ADT-structural | Int-bounded-well-founded | explicit `loop`/`recur`.
Accept Int-bounded recursion (recursive call on `p - <pos-lit>`
with a non-recursing base guard, branching allowed) as a
*second total-by-construction class*, with one documented
obligation: non-negative entry (a negative seed diverges — the
same precondition class as array-bounds the language already
lives with). Restores the entire `f(n-1)` recursion family (the
second-most-common shape in any corpus); keeps all 6 fixtures
verbatim; the trichotomy is *more* complete, not less principled.
Rationale is the language's own gate: a milestone that makes
`build(d)` inexpressible cannot pass the feature-acceptance
criterion. *Why this is a user call:* it concedes "total by
construction, sign nothing" → "total by construction modulo one
documented non-negative-entry obligation" — a dilution of a
purity pillar the user personally weighted in the original
Socratic design dialogue.
- **A2.** Introduce a `Nat` type; accept Int-bounded recursion
only on `Nat` — preserves zero-precondition purity, but `Nat`
is its own sub-milestone and an LLM writes `depth: Int` unless
the prelude makes `Nat` the natural default (clause-1 friction).
- **A3 / B / C (not recommended).** Strict-dichotomy +
explicit-worklist (fails clause 1 — no LLM hand-rolls a stack
for tree-build); a narrow carve-out predicate (the patch smell);
reshaping the LLM-natural fixtures (forbidden —
`feedback_dont_adapt_tests_to_bugs`).
**If A1 (or A2): new iteration it.2b** — additive widening of the
it.2 guardedness checker to also accept Int-bounded (incl.
branching) recursion as total; no removal. After it.2b the 6
fixtures check clean with zero migration; it.3 then re-dispatches
from c992eb9 (the `bench/it3-oracle/` 40-fixture behavioural
oracle is preserved and reused; nothing in it.3 was destructive).
The it.3 RC-RSS/18g.1 load-bearing risk (Task 5.5/5b) is still
UNTESTED — must not be treated as resolved by the milestone-close
audit.
**Relation to Stateful islands.** This entry owns the iteration
story and *supersedes* the "while-loops legal" scope bullet of the
Stateful-islands milestone below. The genuinely-escaping
mutable-state path (`ref a` / `MutArray a` / `Stateful a b` /
`pipe`) is unaffected and stays in Stateful-islands — that is the
deliberately-fenced streaming escape hatch for *escaping* state,
not the default iteration story, and F1 no longer motivates it.
`pipe`) is unaffected and stays in Stateful-islands.
- context: 2026-05-15 chat — design convergence from the
mut-local fieldtest (F1) through the fenced-`while` /
`!Mut`-visibility dead end to the structural-recursion +
named-loop synthesis. Pending brainstorm produces
`docs/specs/<date>-iteration-discipline.md`.
- context: spec `docs/specs/2026-05-15-iteration-discipline.md`;
per-iter journals 2026-05-15-iter-it.1 (shipped), -it.2
(shipped), -it.3 (BLOCKED — the design fork above). Next action
is a USER decision on the blocking fork (A1 recommended), then
iteration it.2b, then it.3 re-dispatch from c992eb9.
- [x] **\[milestone\]** Heap-`Str` ABI — runtime infrastructure for
malloc-backed, refcounted `Str` values alongside the existing