Files
AILang/docs/roadmap.md
T
Brummel 37ac704bf3 iter revert: back out the Iteration-discipline milestone (it.1 + it.2)
One forward iteration; main never rewound. 1ff7e81 (the pre-9973546
commit) is the per-region byte oracle — every reverted source/test
file is byte-identical to it; crates/ailang-check/src/lib.rs fully so.

Root cause being corrected: the Iteration-discipline milestone was an
over-escalation of fieldtest finding F1 (a [friction] item whose own
minimal recommendation was a DESIGN.md note). Its totality dichotomy
made the maximally-LLM-natural build(d:Int)=Node(1,build(d-1),
build(d-1)) inexpressible (it.3 BLOCKED); the only in-thesis escape
(A1/it.2b) conceded the language's first documented-unenforced
totality precondition — a purity-pillar dilution the user rejected in
favour of a full revert + rebuild.

Removed: Term::Loop/Term::Recur/LoopBinder; the verify_structural_
recursion guardedness pass + term_contains_loop + Diverge-injection +
the transitively-it.2 module_fns plumbing; the five Recur*/
NonStructuralRecursion CheckError variants (+ code() + the 3 dedicated
ctx() arms); the it.1 codegen loop-header/phi/back-edge + parallel
block_terminated setter; all Loop/Recur walker arms; 16 it.1/it.2
fixtures; 2 pin files; bench/it3-oracle/. Restored: 2 RC fixtures to
1ff7e81 content.

Surgically kept (not in 1ff7e81, landed with the milestone but
independently sound): feature-acceptance clause 3 in DESIGN.md and
skills/brainstorm/SKILL.md, with its worked example de-claimed from
"shipped" to hypothetical-illustration form; the F3 P2 todo.
bench/orchestrator-stats/2026-05-15-iter-it.{1,2,3}.json kept as
historical record (like journals/plans).

Sole net addition: an honest F1/F4 documented-idiom note in DESIGN.md
(the tail-recursive accumulator fallback; examples/mut_counter.ail),
guarded by a doc-presence test — "a documentation note is not a
reshape", asserts nothing at the typecheck level.

Roadmap: the Iteration-discipline block + blocking-fork section
removed; the genuine total-Int-recursion ambition preserved as a
deferred P2 milestone sequenced behind a future Nat/refinement-types
milestone (not abandoned — correctly sequenced after the type
machinery it needs). 2026-05-15-iteration-discipline.md carries a
superseded header; it.1/it.2/it.3 journals + plans stay as history.

Correctness gate PRISTINE: 164 surviving 1ff7e81-era fixtures
ail check/ail run byte-identical to pre-milestone behaviour (verified
against a 1ff7e81 worktree reference compiler, zero drift);
cargo test --workspace 600/0; zero residual it.1/it.2 production
surface.

Spec docs/specs/2026-05-16-iteration-discipline-revert.md (b3853bf),
plan docs/plans/2026-05-16-iter-revert.md (abf0013).
2026-05-16 01:28:47 +02:00

466 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AILang Roadmap
Priority-ordered list of upcoming work — milestones, features, todos,
and ideas. The orchestrator maintains this file. The user can request
additions; the orchestrator chooses what to remove or reprioritise as
work progresses.
## Conventions
- One checkbox per entry. `- [ ]` is open; `- [~]` is in progress
(work has started — a plan exists, a branch is open, or commits are
landing); `- [x]` is done. A finished entry may stay briefly for
context, then is removed (with a one-line mirror in
`docs/journals/`).
- Each entry is tagged by **kind** and lives under a **priority**
bucket:
- **\[milestone\]** — big chunk that will get a `docs/specs/<milestone>.md`.
- **\[feature\]** — smaller addition inside a milestone, no full
spec.
- **\[todo\]** — concrete task that can run without a brainstorm
(cleanup, doc fix, mechanical refactor, test backfill).
- **\[idea\]** — not yet decision-ready, no commitment.
- Optional `depends on:` line names another entry that has to land
first.
- Optional `context:` line points to the journal entry (per-iter
file under `docs/journals/` or, for pre-2026-05-11 entries, the
archived `docs/journal-archive.md`) where the rationale lives.
The roadmap is intentionally terse; rationale stays in the
journals.
- Priority buckets:
- **P0** — in flight. Spec or plan already exists.
- **P1** — next up. Decision made; not yet started.
- **P2** — medium-term. Decided in principle, scheduled later.
- **P3** — ideas. No commitment; may be cut.
## P0 — In flight
_(empty — prelude-decouple milestone closed 2026-05-14, audit-pd
clean. Pick the next milestone from P2.)_
## P1 — Next
- [x] **\[milestone\]** Heap-`Str` ABI — runtime infrastructure for
malloc-backed, refcounted `Str` values alongside the existing
static `@.str_*` globals. Today the `Str` path is static-only
(DESIGN.md §"Float semantics" notes this explicitly), so any
primitive that needs to produce a `Str` at runtime — `int_to_str`,
`float_to_str` (currently type-installed but `CodegenError::Internal`
on call), eventual `Show.show`, future `++` on strings — cannot
ship. Scope: pick the representation (likely a `{rc_header, len,
bytes…}` slab consistent with the rest of the RC runtime), teach
codegen to accept both static and heap `Str` at the same ABI slot,
wire RC `inc`/`drop`/`clone`/`compare`/`eq` on the heap form, and
ship `int_to_str` + `float_to_str` as the first two callers so the
ABI gets exercised end-to-end. Unblocks Show + print rewire below.
- context: DESIGN.md §"Float semantics" (`float_to_str` is type-
installed, codegen-deferred); spec 2026-05-11-23-eq-ord-prelude
§"Show. Defers behind a heap-Str-ABI milestone"; spec
2026-05-10-fieldtest-floats §F4 ("dynamic Str allocation in the
runtime"); spec 2026-05-09-22-typeclasses §22b.4b ("Show#Int
needs an `int_to_str` primitive returning heap-allocated Str").
- [x] **\[milestone\]** Post-22 Prelude — Show + print rewire — shipped
2026-05-13 as iters 24.1 (heap-Str runtime + codegen for `bool_to_str`
+ `str_clone`, f38bad8), 24.2 (prelude `class Show` + four primitive
instances Int/Bool/Str/Float + 22b TShow/tshow migration), and 24.3
(polymorphic `fn print : forall a. Show a => (a borrow) -> () !IO`
+ positive 4-prim E2E + user-ADT E2E + Show-aware NoInstance
diagnostic + DESIGN.md amendments to §"Prelude (built-in) classes"
and §"Float semantics"). The `MethodNameCollision` workaround that
blocked the original spec retired in mq.3 (2026-05-13); spec
`docs/specs/2026-05-13-24-show-print.md` re-derived 24.2 + 24.3
against the post-mq architecture.
- context: spec `docs/specs/2026-05-13-24-show-print.md`; per-iter
journals 2026-05-12-iter-24.1, 2026-05-13-iter-24.2, 2026-05-13-iter-24.3.
## P2 — Medium-term
- [ ] **\[milestone\]** Iteration-totality story — structural +
Int-bounded total recursion with *enforced* non-negativity.
AILang's iteration story stays as-is (structural / tail recursion;
`tail-app` intact). The genuine ambition — make `f(n-1)`-family
recursion (incl. branching tree builders) total *by construction*
with the non-negative-entry precondition **enforced**, not merely
documented — is deferred here because doing it without a purity-
pillar concession requires refinement/`Nat` type machinery the
language has not built (Decision 4 keeps refinements opaque, no
SMT). Not abandoned; correctly sequenced after the type machinery.
- depends on: a future `Nat`/refinement-types milestone (no spec
yet).
- context: `docs/specs/2026-05-16-iteration-discipline-revert.md`
(why the 2026-05 attempt was reverted) and
`docs/journals/2026-05-15-iter-it.3.md` (the branching-builder
counter-example that surfaced the gap).
- [x] **\[milestone\]** Retire `io/print_int` / `io/print_bool` /
`io/print_float` effect-ops + migrate example corpus to `print`.
Shipped 2026-05-14 as iter rpe.1.
- context: per-iter journal `docs/journals/2026-05-14-iter-rpe.1.md`.
- [x] **\[todo\]** Author `examples/prelude.ail` alongside
`examples/prelude.ail.json`. (Satisfied 2026-05-13 by iter
form-a.0 — `examples/prelude.ail` rendered via `ail render`,
116 lines / 6386 bytes, round-trip-CI green.)
- [x] **\[milestone\]** Form-A as the default authoring surface for
examples and docs. (Closed 2026-05-13 by iter form-a.1.) Render every `examples/*.ail.json` to its
`.ail` sibling via `ail render`, **delete the now-redundant
`.ail.json`**, and regenerate the JSON-AST per `ail parse` at
build / test time. Same for inline JSON-AST blocks in `docs/`
markdown (~7 in DESIGN.md plus ~29 other md files) — convert to
Form-A snippets where the snippet's purpose is to show "what the
language looks like", not "what the schema is".
After this milestone the working tree contains exactly one
representation per program: the `.ail` source. The JSON-AST is a
build artefact, not a checked-in twin. Tests and benches that
currently glob `*.ail.json` either parse-then-consume or are
rewired to point at `.ail` directly. The round-trip invariant
flips role: today it gates that the two forms agree; afterwards
it gates that `parse` is deterministic.
Carve-outs that MUST stay JSON-AST (no Form-A counterpart, and
no `.ail` sibling shall be created — these are the only seven
files that remain `.ail.json`-only post-milestone):
- Fixtures that test canonical-form rejection — Form A would
reject them at parse, defeating the test:
`test_ct1_bare_xmod_rejected.ail.json`,
`test_ct1_qualified_class_rejected.ail.json`,
`test_ct1_bad_qualifier.ail.json`, `broken_unbound.ail.json`,
`test_22b2_invalid_superclass_param.ail.json`,
`test_22b2_kind_mismatch.ail.json`,
`test_22b2_unbound_constraint_var.ail.json`.
- DESIGN.md schema documentation blocks where the JSON-AST shape
*is* the point (Decision 11, ParamMode, canonical-form
invariants).
- Any other case where the structured form is the artefact under
discussion, not a vehicle for a program.
Touches CLAUDE.md ("source of truth is structured data") — the
language doctrine doesn't change (JSON-AST is still the canonical
hashable form), but the *authoring* doctrine does: authors write
`.ail`, the build derives JSON-AST. Sentence in CLAUDE.md needs
rewording in the same milestone.
Mechanical for the bulk; per-fixture judgement call only for the
carve-out list. Run as one milestone so the corpus flip-over
happens at a single, audit-gated point.
- context: post-Form-A-as-canonical-authoring corollary of the
`examples/prelude.ail` entry above. Generalises the same idea
to the rest of the corpus and follows through on the cross-
model authoring data (textual form cheaper, more first-try
hits) by treating Form A as the privileged surface in the
working tree, not just in flavour text.
- [ ] **\[feature\]** Operator routing through `Eq` / `Ord``==`,
`<` etc. resolved via the typeclass instead of the built-in
primitive comparators. No commitment; gated on bench re-baselining
to make sure the indirection doesn't tank latency.
- context: JOURNAL 2026-05-09
- depends on: Post-22 Prelude — Eq/Ord (shipped 23.5)
- [x] **\[todo\]** `types` / `ctor_index` overlay shape question —
decide whether the env's two parallel ctor maps should collapse
into one overlay, or stay split. Surfaced during the
env-construction unify audit.
- context: JOURNAL 2026-05-10 ("Audit close: env-construction unify"); closed by iter ctt.1 — DESIGN.md §"Env construction" anchors the split decision.
- [x] **\[todo\]** CLI human-mode diagnostic surface for `WorkspaceLoadError`.
Shipped 2026-05-14 as iter cli-diag-human — a new `load_workspace_human`
helper in `crates/ail/src/main.rs` routes 9 non-JSON
`ailang_surface::load_workspace(&path)?` call sites through
`workspace_error_to_diagnostic`, so the bracketed `[code]` prefix is
preserved across `ail check`, `build`, `run`, `emit-ir`, `prose`,
`describe`, `deps`, `diff`, `manifest`.
- context: per-iter journal `docs/journals/2026-05-14-iter-cli-diag-human.md`.
- [x] **\[todo\]** Retire dead `KindMismatch` arm — `validate_classdefs`'s
`walk_kind_mismatch` path is structurally unreachable through
well-formed schema post-ct.1 (the canonical-form validator catches
the malformed `Type::Con { name: param }` shape earlier). The
enum variant + `walk_kind_mismatch` helper stay as dead-but-defensive
code; a future tidy can delete both.
- context: JOURNAL 2026-05-11 ("Iteration ct.1"); closed by iter ctt.3 — variant + helper + dispatch + Display arm all deleted; canonical-form-rejection test stays green asserting `BareCrossModuleTypeRef`.
- [x] **\[todo\]** Re-key `Registry.type_def_module` to handle
bare-name-collision-across-modules — `BTreeMap<String, String>` keyed
by bare type name silently overwrites when two modules each define
`type Foo`; `normalize_type_for_registry` would then collapse `M.Foo`
and `N.Foo` to whichever insert won. Acceptable for current corpus
(distinct bare type names across modules), but the proper fix is to
key by `(owning_module, bare_name) → defining_module`.
- context: JOURNAL 2026-05-11 ("Iteration ct.1") — flagged by ct.1.5a quality reviewer.
- [ ] **\[feature\]** 22c — typeclass corpus expansion. User-defined
classes beyond the prelude four; multi-parameter classes; superclass
chains; richer instance bodies. Deferred from milestone 22.
- context: JOURNAL 2026-05-09
- [x] **\[milestone\]** Module-qualified class names + type-driven
method dispatch — retired the `MethodNameCollision` workaround;
shipped 2026-05-13 as iters mq.1 (canonical-form extension for
class-ref fields + workspace-internal qualification), mq.2 (type-
driven dispatch mechanism installed: `method_to_candidate_classes`
inverse index, multi-candidate `ResidualConstraint`,
`resolve_method_dispatch` 5-step rule, `AmbiguousMethodResolution` +
`UnknownClass` diagnostics), and mq.3 (retirement + multi-class E2E
+ `class-method-shadowed-by-fn` warning + DESIGN.md sync). Two
libraries can now each declare `class Eq` with their own `eq`;
cross-class method ambiguity is resolved at the call site via
type-driven dispatch with `<module>.<Class>.<method>` as the
disambiguation form.
- context: `docs/specs/2026-05-10-canonical-type-names.md` "Out of
scope: Class names" — the workaround was named there so it
stayed visible until this milestone retired it.
- [ ] **\[todo\]** Boehm full retirement — remove the transitional
Boehm GC path now that RC + uniqueness is the canonical memory
story.
- context: JOURNAL pre-22, "Boehm transitional"
- [ ] **\[feature\]** Closure-pair slab / pool — codegen tweak to
pool the env+code closure pairs instead of one-shot heap allocs.
Bench-gated.
- [ ] **\[todo\]** `FnDef::synthetic` flag — formalise the
monomorphiser-emitted FnDefs so downstream passes can tell
user-authored from synthesised at a glance. Currently inferred from
symbol naming.
- [ ] **\[todo\]** 21'h iteration — final 21' carry-over (latency
methodology pass). Numbering kept for continuity with the 21' arc.
- [ ] **\[todo\]** `io/print_float` always-emit-`.0` — surface
printer always emits `.` or `e/E` so re-lex routes to Float;
the runtime printer (`printf("%g\n", v)`) doesn't, so `2.0`
prints as `2` (Int-shaped). Asymmetric. Either switch the
runtime path to a `.0`-fallback printer (matching surface) or
document the `%g` contract in DESIGN.md §"Float semantics" so
the LLM-author knows `io/print_float`'s output is for-humans
not round-trip.
- context: `docs/specs/2026-05-10-fieldtest-floats.md` finding F1.
- [ ] **\[todo\]** Rustdoc warning sweep — `cargo doc --no-deps`
reports 16 pre-existing warnings (15 in `ailang-check`, 1 in
`ailang-core`: private-item links from public doc, unresolved
intra-crate links). All predate the design-md-consolidation
milestone; treat as a one-off sweep.
- context: JOURNAL 2026-05-10 ("Audit close: design-md-consolidation").
- [ ] **\[todo\]** `design_schema_drift.rs` fidelity widening —
current test checks anchor *presence* anywhere in DESIGN.md;
the audit found that `[high]` schema gaps in §"Data model"
are invisible because anchors live in Decision 11 instead.
Constrain the test to scan only §"Data model" + ParamMode
block, or extract JSON-schema blocks into a machine-readable
file the test consumes.
- context: JOURNAL 2026-05-10 ("Audit close").
- [ ] **\[todo\]** Split `BadCrossModuleTypeRef` into two diagnostics —
the current single shape collapses unknown-owner and
known-owner / unknown-type-in-owner into one message. The two
cases suggest different fixes (add `(import <owner>)` vs. fix the
type name). In the known-owner branch, list the owner's available
type defs as candidates the way `bare-cross-module-type-ref`
lists candidates from imports.
- context: fieldtest 2026-05-11 — `examples/ct_3*.ail` exhibits both branches with identical-shape diagnostics.
- [ ] **\[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.
- [ ] **\[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
subdirectory has no way to resolve cross-module imports. Add either
a `--workspace-root` flag on `ail check` / `ail build` / `ail run`,
or upward-search from the entry module's directory. Alternatively
ratify the flat-workspace assumption in DESIGN.md if intentional.
- context: fieldtest 2026-05-11 — fieldtest fixtures could not be
placed under `examples/fieldtest/` because of this; predates the
canonical-type-names milestone but surfaces every time.
- [x] **\[todo\]** `check_in_workspace` per-module overlay narrowing —
`crates/ailang-check/src/lib.rs:1234` still clears+rebuilds
`env.ctor_index` per-module; ct.3.2 narrowed the analogous mono
overlay to types-only. The typecheck-side overlay's `env.ctor_index`
half serves the duplicate-detection diagnostic at workspace-build
time, not the runtime ctor lookup (which is type-driven post-ct.2.2).
Narrowing is mechanical but needs a careful read to confirm no
other consumer survives.
- context: JOURNAL 2026-05-11 ("Iteration ct.4") — milestone close
follow-up; closed by iter ctt.1 — recon confirmed the rebuild is the load-bearing consumer of in-band DuplicateCtor (pinned by `crates/ailang-check/tests/duplicate_ctor_pin.rs`); the asymmetry with the mono side is intentional.
- [x] **\[feature\]** `str_concat : (borrow Str, borrow Str) -> Str`
heap-Str concatenation primitive. Shipped 2026-05-13 as iter
str-concat (closes fieldtest-form-a friction #4). Symmetric to the
iter 24.1 `str_clone` / `int_to_str` / `bool_to_str` plumbing:
runtime C helper (`ailang_str_concat`), `ailang-check` builtin
registration, `ailang-codegen` extern + `lower_app` arm, plus a
fresh `examples/show_user_adt_with_label.ail` corpus fixture
exercising the LLM-natural Show-body shape.
- context: per-iter journal 2026-05-13-iter-str-concat.md.
- [~] **\[milestone\]** Stateful islands — bounded mutation for
streaming workloads (`Stateful a b` + `!Mut` effect + `mut`
syntactic block). Adds a sealed mutable-state layer on top of
the pure core: a `Stateful a b` first-class type whose interior
is mutable, whose exterior is a typed callable with `!Mut` in
its effect set, and whose state non-aliasing is enforced by
uniqueness inference at the block boundary. Targets the
online / streaming workload class — rolling indicators, IIR
filters, online aggregates, sensor fusion, online learning —
whose mathematics is "new sample + old state → new state +
output" per step, and which today's pure-functional state-
threading makes ergonomically expensive at scale (multi-record
explicit threading for the canonical sliding-window SMA, no
zero-allocation pipe combinator, growing tuple-state types as
pipelines lengthen).
**Progress.** Decomposed at brainstorm time (2026-05-15) into a
sequence of shippable sub-milestones. Sub-milestone 1 closed
2026-05-15: **mut-local** — sealed-by-construction `mut`/`var`/
`assign` blocks for Int/Float/Bool/Unit scalars, alloca-resident,
no escape, no `!Mut` effect leakage. Foundation in place;
fn signatures stay pure while LLM authors can write imperative
accumulators directly. Spec `docs/specs/2026-05-15-mut-local.md`;
iters mut.1/mut.2/mut.3/mut.4-tidy (commits 7b92719, b24718a,
03fb633, 20add51). Remaining sub-milestones (to be brainstormed
separately, in order): (2) effect-handler infrastructure as a
prerequisite for any non-IO/non-Diverge effect; (3) `!Mut` effect
+ `ref a` first-class type that can escape mut blocks under
uniqueness-tracking; (4) mutable-array primitive `MutArray a`;
(5) `Stateful a b` sealed callable type + `pipe` combinator with
zero-allocation composition + `runST`-equivalent escape discharge.
**Motivation.** AILang's signature-as-contract thesis is *better*
served by an explicit `Stateful a b` + `!Mut` annotation than by
the implicit-closure-mutation idiom of myc / Lua / JS factory
patterns: the signature tells the truth about time-identity
without the body needing to be read — exactly the LLM-author
correctness affordance AILang exists to deliver, extended to a
workload class it does not yet serve. Decision 10's constraint #3
forbids *shared* mutable refs (DESIGN.md:1082); it does not
forbid uniqueness-bounded mutation. Lean 4 (`ST`), Roc (`Task`),
Haskell (`ST`/`IO`), and Koka (effects) all use a layered design
of this shape to host exactly this workload. AILang's existing
`own` / `borrow` mode machinery plus its effect-slot architecture
are the foundation; this milestone supplies the layer that sits
on top.
**Scope (subject to brainstorm refinement).**
- `Stateful a b` as a first-class type — a sealed callable with a
hidden mutable env, externally a typed callable whose effect
set includes `!Mut`.
- `!Mut` effect, the first non-IO / non-Diverge effect; forces
the effect-handler infrastructure forward.
- `mut` block as the syntactic boundary in Form A — outside,
AILang's pure self; inside, `var` / `assign` / mutable arrays
legal. (Iteration is *not* part of this boundary: the "Iteration
discipline" P1 milestone supersedes the earlier "while-loops
legal" scope here — repetition is structural recursion or named
`loop`/`recur`, never a `while` over mutable state.)
- `var x = expr` + `assign x expr` AST nodes, legal only inside
`mut`.
- Mutable array primitive (`MutArray a`, O(1) index/update under
uniqueness) — co-developed because the ring-buffer use case
that motivates the whole effort has no carrier today.
- `pipe : Stateful a b → Stateful b c → Stateful a c` as a
built-in combinator with zero-allocation lowering — composition
is fusion at codegen, not closure-pair layering at runtime.
- Decision 12 (or amendment to Decision 10) that names
uniqueness-bounded mutation as the legitimate exception to
"no shared mutable refs", and the pure / mutable-island layering
as the architectural shape.
**Blockers (hard prerequisites + open design questions).**
- *Effect handlers absent.* DESIGN.md:2663 — "No effect
handlers — only the built-in IO and Diverge ops." `!Mut` is the
first non-trivial effect and forces handler infrastructure to
ship. May warrant lifting out as its own prerequisite milestone;
brainstorm decides.
- *Uniqueness inference through `var` captures.* Current
inference operates over the immutable RC graph; mutable
bindings captured by closures need a more powerful pass. Lean 4
has a known algorithm; AILang does not implement it.
- *No mutable-array primitive.* No `Array a`, no slab container
of any kind in the language today. Separate spec needed inside
this milestone (representation, `own`-only or `borrow`-able,
bounds-checking discipline).
- *`runST`-equivalent escape discharge.* Producing a `Stateful`
that legitimately escapes its `mut` block while proving the
inner state doesn't leak. Haskell's rank-2 trick
(`runST :: (forall s. ST s a) -> a`) is unavailable —
DESIGN.md:1930 confirms higher-rank polymorphism is not
supported. Need an alternative — likely sealed-by-construction
at the factory boundary, or an effect-mode tag that gates
escape.
- *Form A surface design.* Decision 1 forbids macros (source =
data, not text), so `mut` / `var` / `assign` are genuine AST
nodes with round-trip-invariant coverage. Surface needs LLM-
utility validation (does the author reach for `var` / `mut`
unprompted?) before implementation.
- *Codegen for in-place struct-field writes.* `Term::ReuseAs`
today lowers ADT in-place rewrite; no direct mutable struct-
field-write path exists. `crates/ailang-codegen/src/escape.rs`
plus the lowering passes need extension.
- *Boundary escape analysis.* "Interior state doesn't leak" is an
escape analysis specifically over `var` / `ref` values; the
existing pass covers allocations, not mutable cells.
- *Decision-10 status.* Whether to amend Decision 10 inline or
add a Decision 12 that names the mutable-island layer alongside
the pure layer. The latter is probably cleaner — Decision 10
stays load-bearing for the pure layer, Decision 12 names the
extension and its discipline.
- *Streaming bench corpus.* Current corpus (list_sum, tree_walk,
closure_chain, hof_pipeline) is all pure. Streaming-specific
benches (SMA pipeline, IIR filter, online stats) plus an
external baseline (myc, hand-C, Python/NumPy) need to exist to
validate that the layered design hits the zero-alloc
performance target that myc demonstrates.
- *LLM-utility test.* DESIGN.md §"Feature-acceptance criterion"
is the gate: the surface must produce code an LLM author
naturally writes. Fieldtest after spec, before any code
commits.
- context: 2026-05-15 chat on the `~/sma_factory.myc` analysis —
myc's stateful-closure-plus-pipe idiom delivers a streaming
workload pattern (3-line SMA factory, 2-line pipeline, zero
runtime allocation per tick) that AILang's pure-only model
cannot match without this layered extension. Pending brainstorm
will produce `docs/specs/<date>-stateful-islands.md` and decide
the effect-handler-prerequisite question.
## P3 — Ideas
- [x] **\[todo\]** `compare_primitives_smoke.ail` counterpart.
Satisfied 2026-05-13 by milestone form-a-default-authoring —
`examples/compare_primitives_smoke.ail` is the canonical
authoring form for that fixture. Form A is now the default
authoring surface across the entire corpus.
- context: closed incidentally by form-a iter form-a.1.
- [ ] **\[todo\]** Codegen `lookup_ctor_in_pattern` type-anchoring —
`crates/ailang-codegen/src/lib.rs:1790` still walks every module's
ctor_index by bare ctor name. Plumbing the scrutinee's
qualified `Type::Con` through pattern lowering would type-anchor
it symmetric to the ct.2.2 typecheck-side fix. Not load-bearing
(uniqueness is enforced at typecheck), but cleaner.
- context: JOURNAL 2026-05-11 ("Iteration ct.3" + ct.4 close).
- [ ] **\[todo\]** `compare_primitives_smoke` IR-shape assertion —
observe `compare__Int` / `compare__Bool` / `compare__Str` symbols
in the emitted IR. Blocked on `emit-ir` CLI not running mono;
resolution paths include extending the CLI to run mono, using
library APIs directly in e2e.rs, or adding `--dump-ir` to `ail
build`. The E2E stdout assertion already covers correctness; the
IR-shape test would catch refactor regressions that rename
mono symbols.
- context: JOURNAL 2026-05-11 ("Iteration ct.4") — dropped from
ct.4.4 when the BLOCKED condition surfaced.
- [ ] **\[idea\]** Latency methodology rework — switch from per-run
timing to a histogram-based approach so tail-latency regressions
are visible without re-running. Queued from 21'g.
- [ ] **\[idea\]** Parser-test backfill for 22b.4a-era duplicate-clause
sites (class × 3, class method × 2, instance × 3, instance method
× 1). No regression pin today; only worth it if a 22b.4a-era
diagnostic needs to change.
- context: JOURNAL 2026-05-10 ("Iteration 22-tidy.7")
- [ ] **\[idea\]** `write_type` `Type::Forall` arm in
`crates/ailang-prose/src/lib.rs` silently drops constraints in
inline-type rendering. Dormant — surface forms today only carry
forall at fn-signature top level. Fix only if a future feature
carries forall + constraints inline.
- context: JOURNAL 2026-05-10 ("Iteration 22-tidy.6")
- [ ] **\[idea\]** Richer integration paths between RC and
uniqueness — deferred from the 21' arc; revisit once the
uniqueness inference covers more program shapes.