54f0ced148
Strict application of the "Future-Use, not Verlauf" criterion to the
two remaining journal artefacts:
- `docs/journals/` (110 files): the per-iter and audit journals from
2026-05-11 onward. Pure history; no live reader after the previous
sweep. Entire directory removed.
- `docs/journals/2026-05-19-design-decision-records.md`: the one file
that had live readers (`docs_honesty_pin.rs:108`, `parse.rs:80`,
`duplicate_ctor_pin.rs:8`, three roadmap.md mentions) and was framed
as a "relitigation guard". On re-examination its three asserted
pinned phrases ("Regions were considered and rejected", the
"demands annotations *because*" rationale, the prose-render
placeholder statement) are rationale-prose, not load-bearing
invariants — the test pinned itself, not a system property. Any
Decision that still holds lives in the code, `design/contracts/`,
or `design/models/`. Removed with the rest.
- `docs/journal-archive.md` (pre-2026-05-11 history): content-frozen
long-tail history with no live reader. Removed; if anyone ever
needs the pre-cutoff rationale they can `git log --before=2026-05-11
--grep=<keyword>`.
Live-file consequences:
- `docs_honesty_pin.rs` `design_md_present_tense_anchors_present`:
the three `records.*` assertions and the `read("docs/journals/…")`
are dropped; the contract/model anchor pins remain.
- `duplicate_ctor_pin.rs`: the "why-two-overlays rationale lives in
docs/journals/…" comment is replaced with the rationale inline.
- `parse.rs`: the "form-refinement rationale lives in docs/journals/…"
comment is dropped (the rule body already states the refinement).
- `roadmap.md`: the decision-records mention in the DESIGN.md →
design/ entry's body is dropped; the journal-archive.md `context:`
pointers across ~12 closed entries are either rephrased to point
at the relevant iter/audit (no path), or simplified to a one-line
comment when the iter name alone carries the story.
- `CLAUDE.md` Roles section: the `docs/journal-archive.md` slot is
removed; vocabulary note rephrased.
- `design/INDEX.md`: the Docs bullet drops `journal-archive.md`.
- `skills/README.md` bootstrap-rationale pointer rephrased.
`docs/specs/` and `docs/plans/` (per-milestone specs and per-iteration
plans) are unaffected — they remain the structured-design artefacts
they were before this sweep.
Workspace builds, full test suite green.
1059 lines
61 KiB
Markdown
1059 lines
61 KiB
Markdown
# 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 or commits are landing); `- [x]`
|
||
is done. A finished entry may stay briefly for context, then is
|
||
removed; the durable record stays in `git log`.
|
||
- 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 rationale: a spec file
|
||
under `docs/specs/`, a commit hash (`git show <hash>`), or a
|
||
`git log --grep="<keyword>"` pointer for older entries. The
|
||
roadmap is intentionally terse; rationale stays in those sources.
|
||
- 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
|
||
|
||
- [x] **\[milestone\]** Remove `mut` / `var` / `assign` — CLOSED
|
||
2026-05-18. The local-mutable-state construct removed entirely
|
||
and atomically (`Term::Mut`/`Term::Assign`/`MutVar`, the 3 Form-A
|
||
keywords, the 4 `mut` `CheckError` variants, `mut_scope_stack`,
|
||
every exhaustive `Term` arm across 17 src files) in lockstep with
|
||
DESIGN.md + fixtures + drift trio + carve-out + roadmap; `loop`/
|
||
`recur` + `let`/`if` are the surviving forms. Justified by the
|
||
feature-acceptance criterion applied inverted (clause 2 redundant,
|
||
clause 3 IS the iterated-mutable-state bug class). Approach A: no
|
||
deprecation window, JSON tags fail closed; shared codegen alloca
|
||
machinery kept (loop reuses it, renamed `binder_allocas`) + the
|
||
`Term::Lam` escape-guard loop half byte-equivalent. spec/plan/
|
||
iter (`d1ad50e`/`f355899`/`07f0802`), milestone-close `audit`
|
||
clean (architect `[high]` form_a.md fixed in `.tidy` `4837871`;
|
||
bench causally exonerated — HEAD vs pre-milestone bench binaries
|
||
`cmp`-byte-identical, no ratify), `fieldtest` clean (4 fresh
|
||
programs, all worked first try without mutable locals — the
|
||
removal thesis empirically confirmed). Decoupled from
|
||
Stateful-islands. Stays here briefly for context; remove once
|
||
stale (full record in `git log`).
|
||
- context: `docs/specs/2026-05-18-remove-mut-var-assign.md`;
|
||
`docs/specs/2026-05-18-fieldtest-remove-mut-var-assign.md`
|
||
|
||
- [x] **\[milestone\]** Prose `loop` binders — projection redesign —
|
||
CLOSED 2026-05-18. Form-B prose now renders loop binders as a
|
||
parenthesised init-list on the keyword (`loop(acc = 0, i = 1)
|
||
{ … }`, positionally isomorphic to `recur(...)`) instead of bare
|
||
`name = init;` statements inside the body block (which misread as
|
||
C/Rust re-init-every-iteration). Single-iteration, projection-only:
|
||
one `ailang-prose` `write_term` `Term::Loop` arm + 2 committed
|
||
byte-equality `.prose.txt` snapshots/tests; loop/recur AST, Form A,
|
||
JSON-AST, typecheck, codegen, and the Form-A↔JSON round-trip
|
||
invariant all byte-unchanged. brainstorm→plan→implement
|
||
(`c657e74`/`6533134`/`c9355d7`); milestone-close `audit` clean
|
||
(architect zero drift; bench carry-on — sole firing is the tracked
|
||
P2 `*.bump_s` environmental staleness, latency-tail proven sampling
|
||
noise by re-run); no fieldtest (projection-only, no authoring-
|
||
surface change). Stays here briefly for context; remove once stale.
|
||
- context: `docs/specs/2026-05-18-prose-loop-binders.md`; surfaced
|
||
from the loop/recur prose review (2026-05-18 chat).
|
||
|
||
- [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 `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 `git log`).
|
||
- 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.
|
||
|
||
- [x] **\[milestone\]** DESIGN.md / docs honesty lint — CLOSED
|
||
2026-05-18. `docs/DESIGN.md` + `docs/PROSE_ROUNDTRIP.md` now mirror
|
||
ONLY the current state, present-tense: 14 DESIGN.md corrections
|
||
(Wunschdenken stated-as-fact + non-citation post-mortem/doc-
|
||
archaeology stripped) + the PROSE tool-use/MCP paragraph; genuine
|
||
forward intent (LLM tool-use/MCP/LSP) relocated to P3. The
|
||
two-pronged tense+modality discriminator is codified present-tense
|
||
as a DESIGN.md `### What this document is …` meta-subsection
|
||
(Approach A) the architect cites. Anti-regrowth two ways: a
|
||
wrap-robust enumerated absent/present pin
|
||
`crates/ailang-core/tests/docs_honesty_pin.rs` (a `norm()`
|
||
whitespace-collapse helper structurally discharges the recurring
|
||
grep/contains line-wrap failure family — strictly better than the
|
||
effect-doc-honesty "keep-on-one-line" precedent) + wrap-robust
|
||
advisory Sweep 5 in `architect_sweeps.sh` (contiguous-fragment
|
||
regex) with full sweep-count lockstep + a new `ailang-architect.md`
|
||
"DESIGN.md honesty drift" bullet. Procedural enumeration vindicated:
|
||
the Task-4 Step-14 catch-all caught a genuine soft-wrapped extra FIX
|
||
(Form-B prose-projection bullet) a frozen list + the spec's
|
||
illustrative regex would both have missed. Single iter
|
||
(`aff25cd`/`de66eb7`/`7580d43`); milestone-close `audit` CLEAN
|
||
(architect clean, one `[medium]` advisory Sweep-5 self-match wart →
|
||
P3 below; bench causally exonerated — HEAD vs `5bb7211`/`de66eb7`
|
||
bench binaries `cmp`-byte-identical, NO ratify); no fieldtest (zero
|
||
authoring-surface change). Stays here briefly for context; remove
|
||
once stale (full record in `git log`).
|
||
- context: `docs/specs/2026-05-18-docs-honesty-lint.md`
|
||
(grounding-check PASS 10/10).
|
||
|
||
## P1 — Next
|
||
|
||
- [x] **\[milestone\]** Embedding ABI — M1: linkable artifact +
|
||
scalar C entrypoint + `main`-free lifecycle — CLOSED 2026-05-18.
|
||
First of the five-milestone arc (M1–M5) making a compiled AILang
|
||
kernel callable in-process from a concurrent Rust host. Shipped:
|
||
additive `FnDef.export: Option<String>` (hash-stable, ~85-site
|
||
compile-driven thread); Form-A `(export "<sym>")` modifier
|
||
(round-trip-gated); check-side scalar-only+effect-free export gate
|
||
(`export-non-scalar-signature` / `export-has-effects` — the
|
||
feature-acceptance clause-3 discriminator *in code*); codegen
|
||
`Target::StaticLib` (suppresses `@main`+`MissingEntryMain`, emits
|
||
one external `@<sym>` forwarder per export, decoupled from
|
||
`ail_<module>_<fn>` mangling per Decision 2); CLI `ail build
|
||
--emit=staticlib` (`lib<entry>.a` + separate `libailang_rt.a`).
|
||
Coherent stop PROVEN: C host links both archives, calls the
|
||
scalar kernel, typed return holds. spec `51160e9` (grounding-check
|
||
PASS) → plan `b0bd7ef` (Repaired) → iter `818177d` (partial 1–3 +
|
||
Boss plan Repair: a module≠stem fixture defect, resolved Option 2)
|
||
+ `e406d07` (DONE 4–7). Milestone-close `audit` `425c4eb` CLEAN
|
||
(architect clean — Invariant 1 holds semantically, lockstep
|
||
intact; sole `[low]` rustfmt-divergence evidence-adjudicated
|
||
carry-on, 1137 pre-existing tree-wide divergences prove rustfmt
|
||
is not a project convention; bench `check.py` exit 1 decisively
|
||
causally exonerated — 21/21 bench binaries `cmp`-byte-identical
|
||
vs pre-milestone, the two tracked-P2 noise families, NO ratify).
|
||
`fieldtest` `6a4e866` substantiated the thesis (0 bugs; Int+Float
|
||
positive E2E first-try clean = clause-1; both clause-3 rejections
|
||
precise+self-correcting; 3 working) — 3 non-blocking findings
|
||
routed to the two follow-up items directly below. Stays here
|
||
briefly for context; remove once stale (full record in
|
||
`git log`).
|
||
- context: `docs/specs/2026-05-18-embedding-abi-m1.md`;
|
||
`docs/specs/2026-05-18-fieldtest-embedding-abi-m1.md`
|
||
|
||
- [x] **\[todo\]** form_a.md scalar-parameter mode carve-out — docs-
|
||
honesty tidy (M1 fieldtest friction + spec_gap#1, shared root) —
|
||
CLOSED 2026-05-18. `crates/ailang-core/specs/form_a.md` stated an
|
||
*unconditional* "every `(fn …)` param MUST carry an `(own/borrow)`
|
||
mode" rule in **four** places that contradicts shipped checker
|
||
behaviour (scalars take **and require** bare `(con Int)`; a mode on
|
||
a scalar trips body-pointing `use-after-consume` /
|
||
`consume-while-borrowed`). All four sites rewritten symmetric to
|
||
the pre-existing return-type carve-out; the already-correct
|
||
few-shot annotation left verbatim by design; DESIGN.md §"Embedding
|
||
ABI (M1)" gained the bare-scalar export-param rule + a
|
||
corpus-grounded `step` Form-A snippet; RED-first anti-regrowth pin
|
||
via `ailang_core::FORM_A_SPEC` + `norm()` (4 ABSENT + 4 PRESENT
|
||
form_a + 1 PRESENT DESIGN). Zero language/checker/codegen change;
|
||
Boss-verified docs_honesty_pin 5/0, spec_drift 8/8 (not a lockstep
|
||
partner), ailang-core 112/0, workspace 622→623 (+1 = the pin), zero
|
||
`crates/**/src/**` diff. Pure docs+pin tidy — no audit/fieldtest
|
||
gate (the pin IS the regression coverage). plan
|
||
`docs/plans/form-a-scalar-param-mode-carveout.md` (`4c266a6`) → iter
|
||
`7d7f04e`. Stays here briefly for context; remove once stale (full
|
||
record in `git log`).
|
||
- context: `docs/specs/2026-05-18-fieldtest-embedding-abi-m1.md`
|
||
findings [friction] + [spec_gap]#1.
|
||
|
||
- [x] **\[feature\]** `ail emit-ir --emit=staticlib` — restore the
|
||
Decision-5 IR-readability affordance for kernels (M1 fieldtest
|
||
spec_gap#2) — CLOSED 2026-05-18. `ail emit-ir` had no
|
||
`--emit=staticlib` (only `ail build` did) and on a `main`-free
|
||
kernel hit the executable-path `MissingEntryMain` rejection — an
|
||
author could not read the generated `@<sym>` forwarder for an
|
||
exported kernel. Resolution **added** (DESIGN.md *widened*, never
|
||
narrowed): a one-line symmetric codegen convenience
|
||
`lower_workspace_staticlib(ws)` routing through the M1-audited
|
||
unchanged `Target::StaticLib` path, an `emit` clap field on
|
||
`Cmd::EmitIr` symmetric with `Cmd::Build`'s, a zero-export guard
|
||
byte-identical to `build_staticlib`'s; DESIGN.md §"Embedding ABI
|
||
(M1)" affordance sentence + a CLI-synopsis correction also
|
||
discharging a pre-existing M1 `ail build --emit=staticlib`
|
||
omission. No new doc pin (E2E pins behaviour; architect's
|
||
milestone-close DESIGN.md read catches stale prose). Boss-verified:
|
||
3 new E2E 3/0 (incl. functional CLI spot-check — `@backtest_step`
|
||
external forwarder readable, no `@main`), `embed_staticlib_cli`
|
||
2/0 (build-side untouched), `docs_honesty_pin` 5/0, `ail` 186/0,
|
||
workspace 623→626 (+3), zero out-of-scope diff, no fixture minted.
|
||
Scoped feature — no audit/fieldtest gate (E2E is the coverage;
|
||
reuses the M1-audited codegen path, no invariant/surface change).
|
||
plan `docs/plans/emit-ir-staticlib.md` (`03493c9`) → iter
|
||
`bcfe554`. Stays here briefly for context; remove once stale (full
|
||
record in `git log`).
|
||
- context: `docs/specs/2026-05-18-fieldtest-embedding-abi-m1.md`
|
||
finding [spec_gap]#2.
|
||
|
||
- [x] **\[milestone\]** Embedding ABI — M2: per-thread runtime
|
||
context + concurrency safety — CLOSED 2026-05-18. A compiled
|
||
scalar AILang kernel is now callable concurrently from a host
|
||
thread swarm without an RC-runtime data race. `ailang_ctx_t*`
|
||
is a mandatory leading parameter of the (M3-frozen-shape) C
|
||
`@<sym>(ctx, scalars…)` ABI; the M1 forwarder publishes it via
|
||
a `__thread` slot around the **byte-unchanged** internal
|
||
`@ail_<mod>_<fn>` call (internal convention + `_adapter`/`_clos`
|
||
untouched — M1 decision held; TLS sound because the kernel call
|
||
is synchronous, ctx never held across a suspension). `runtime/rc.c`
|
||
de-globalised: the two RC-counter sites became `if (ctx)
|
||
ctx->{alloc,free}_count++ else g_rc_*++`, the `g_rc_*` statics +
|
||
`atexit` retained verbatim as the null-ctx single-threaded
|
||
executable fallback (executable-path leak readback byte-preserved).
|
||
ctx near-empty by discipline (`{alloc,free}_count` only — no
|
||
arena/lock/atomic/IO; the build-ahead trap named out-of-scope).
|
||
Staticlib is RC-only enforced (`--emit=staticlib` rejects
|
||
`--alloc=gc|bump`; Boehm never linked into a swarm artefact;
|
||
standing P2 Boehm-retirement untouched). No authoring-surface /
|
||
schema / `ailang-check` change — the `.ail` is byte-identical to
|
||
M1; the deliverable is a swarm-safe generated ABI + de-globalised
|
||
runtime, **sanitiser-verified** (per-thread-ctx scalar swarm
|
||
tsan-clean == oracle; direct rc-accounting per-ctx clean;
|
||
shared-ctx negative control a genuine `ThreadSanitizer` race at
|
||
the de-globalised counters, exit 66 — teeth non-vacuous). Process
|
||
note: the first implement dispatch correctly BLOCKED on a genuine
|
||
spec defect (a negative control on the non-allocating scalar
|
||
swarm is structurally impossible — the spec's own honesty point);
|
||
Boss adjudicated a spec-consistency repair (teeth relocated to the
|
||
rc-accounting harness) rather than a brainstorm bounce. spec
|
||
`1c58055` (grounding-check PASS 9/9) → plan `b3388c8`
|
||
(Boss-corrected) → iter `c9a84b3` (PARTIAL 4/9 + Boss repair) +
|
||
`fbeeade` (DONE 5–9); milestone-close `audit` `ad88dec` (architect
|
||
one `[medium]`+`[low]` doc-honesty drift; bench `check.py` exit 1
|
||
decisively causally exonerated — bencher H0-refuted, all 3 firings
|
||
the two tracked-P2 noise families, rc.c hunk branchless+free, NO
|
||
ratify) → tidy `a80d495` (the `## Embedding ABI` lockstep rename,
|
||
drift resolved). No fieldtest (zero authoring-surface change).
|
||
Stays here briefly for context; remove once stale (full record in
|
||
`git log`).
|
||
- context: `docs/specs/2026-05-18-embedding-abi-m2.md`.
|
||
|
||
- [x] **\[milestone\]** Embedding ABI — M3: frozen value layout +
|
||
single ADT/record crossing + RC ownership contract — CLOSED
|
||
2026-05-18. A single-constructor record of `Int`/`Float` fields
|
||
now crosses the embedding C ABI in **and** out; ownership follows
|
||
the declared `own`/`borrow` mode (mechanically inherited — the M2
|
||
forwarder body is byte-unchanged, all mode-driven RC stays in the
|
||
byte-unchanged internal `@ail_<mod>_<fn>`); the record memory
|
||
layout (header@p-8 / i64 tag@0 / fields i64-strided@8+i·8 /
|
||
size=8+n·8) is **frozen** as a one-way commitment — a new
|
||
DESIGN.md §"Embedding ABI" `### Frozen value layout` SSOT +
|
||
`// FROZEN ABI` lockstep pointers at the three encoders + an
|
||
enforceable `@ailang_rc_alloc` heap-box byte-pin (RED-on-offset-
|
||
perturbation proven); host construction via `ailang_rc_alloc`,
|
||
host-free via `ailang_rc_dec` (leak-free *because* M3 fields are
|
||
scalar — no boxed children; non-scalar recursive-free named M4-
|
||
additive). No authoring-surface / schema / Form-A / `CheckError`
|
||
change (M2-style — the `.ail` is the minimal evolution of the
|
||
M1/M2 scalar kernel into a record `State`). The `(State,Float)->
|
||
State` fold round-trip is proven **globally leak-free** for both
|
||
`own` and `borrow`. Process: spec `1fbb9c4` (grounding-check PASS
|
||
8/8) → plan `15ee3c5` → iter `d5c565d` (PARTIAL 5/7 + a Boss
|
||
spec-consistency repair: the orchestrator correctly BLOCKED on a
|
||
genuine spec defect — the single-ctx-readback proof model is
|
||
unsatisfiable for `borrow` by M2's TLS-ctx design; Boss
|
||
M2.1-precedent adjudication → a stronger global-leak-freedom proof
|
||
model) + `4ea8bc5` (DONE 6-7: the freeze). Milestone-close `audit`
|
||
`b8a60b1` (architect: Invariant 1 clean, M1/M2 byte-invariant
|
||
held, frozen-SSOT consistent — DRIFT only `[medium]`+`[low]`
|
||
doc-honesty → tidy; bench `check.py` exit 1 **decisively causally
|
||
exonerated** — byte-identical generated IR HEAD vs pre-M3
|
||
`9a609ae` for every firing bench, the two tracked-P2 noise
|
||
families, NO ratify) → tidy `63d7d60` (pin-safe; drift resolved).
|
||
No fieldtest (zero authoring-surface change). Stays here briefly
|
||
for context; remove once stale (full record in
|
||
`git log`).
|
||
- depends on: Embedding ABI — M2.
|
||
- context: `docs/specs/2026-05-18-embedding-abi-m3.md`.
|
||
|
||
- [x] **\[milestone\]** Embedding ABI — M5: `ail-embed` adapter +
|
||
`data-server` wiring + thread-swarm backtest — **DONE 2026-05-19**,
|
||
audited + ratified + doc-honest. Terminal milestone of the M1–M5
|
||
Embedding ABI arc: a real backtest runs over **real Pepperstone
|
||
tick data** through the real external `data-server` crate into the
|
||
shipped M3-frozen AILang kernel, across a thread swarm of
|
||
independent per-thread embedding contexts. `ail-embed` is a lean
|
||
reusable embedding module (`extern "C"` to the M3-frozen ABI +
|
||
frozen-layout box helpers, zero finance knowledge) **plus** the
|
||
real E2E on top — in-repo but its own cargo workspace root
|
||
(`[workspace]`-excluded), the sole `data-server`↔AILang meeting
|
||
point (Invariant 1, architect-confirmed PASS at close). The
|
||
adapter (not the kernel) owns chunk iteration, unrolling each
|
||
chunk host-side into per-tick `(State, Tick) -> State` calls (M4's
|
||
cons-list crossing was retired as speculative infra). Symbol-fan
|
||
swarm = the headline existence proof (per-thread bit-exact vs an
|
||
independent host reference); time-shard swarm = the **first actual
|
||
proof** of the chunk/window-boundary invisibility the M4
|
||
retirement rests on (per-shard bit-exact). Globally leak-free,
|
||
deterministic. Zero language/compiler change; one runtime change
|
||
(`7bfa11e`: the global RC-stats fallback counters made
|
||
atomic-relaxed — the swarm exposed that AILang's first concurrent
|
||
consumer needed it; user-adjudicated bounce-back → RED-first fix,
|
||
then a build-dependency staleness root-caused + fixed). Audit:
|
||
Invariant 1 PASS, M3 frozen-layout SSOT unmoved, bench `gc_rss`
|
||
trio evidenced-RATIFY (a Boehm conservative-scan artefact of the
|
||
necessary atomic fix; Decision-9 Boehm-transitional), P2 noise
|
||
causally exonerated baseline-pristine; doc-honesty tidy clean.
|
||
**Friction-harvest deliverable: host-per-tick-FFI ≈ ~206 ns/tick
|
||
at real EURUSD volume** — the P2 flat-array decision input.
|
||
Stays one cycle for context, then prune.
|
||
- depends on: Embedding ABI — M3 (shipped); Tick-coverage on M3
|
||
(shipped `170464f`).
|
||
- context: `docs/specs/2026-05-19-embedding-abi-m5.md`.
|
||
|
||
- [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`.
|
||
|
||
## P2 — Medium-term
|
||
|
||
- [x] **\[milestone\]** DESIGN.md → `design/` — CLOSED 2026-05-19.
|
||
The 3020-line `docs/DESIGN.md` is **deleted** (clean cut, no
|
||
stub) and replaced by the `design/` ledger: `design/INDEX.md`
|
||
(sole addressable spine, typed Contracts+Models tables with
|
||
polymorphic links — prose file OR authoritative source `//!`),
|
||
14 `design/contracts/*.md` test-linked invariants + 3
|
||
source-link-only contracts (mangling/env-construction/
|
||
qualified-xref — code is SoT), and 5 `design/models/*.md`
|
||
whitepapers. RED-first
|
||
`design_index_pin.rs` 4-clause anti-regrowth spine; clause-3 is
|
||
a hand-rolled **faithful Sweep-1 superset** so the in-code hard
|
||
gate enforces the honesty spirit (clause-3 GREEN ⟹ Sweep-1
|
||
clean in contracts/). Build-atomic by task ordering (the only
|
||
compile-time consumer's `include_str!` retargeted before the
|
||
deletion). Every live `DESIGN.md` reference — 4 executables, 2
|
||
diagnostics + 2 E2Es, ~12 agent reading lists, 5 SKILL bodies,
|
||
CLAUDE.md, README, ~25 code/C/.ail/spec comment xrefs —
|
||
retargeted in lockstep; the honesty rule rewritten to name the
|
||
new rationale home. Process: spec `a64b2cc`/`314e5e4`
|
||
(grounding-check PASS ×2) → plan `deeffb1` → iter .1 `176821c`
|
||
(DONE 9/9) → `audit` `2ba5e16` (architect drift_found
|
||
`[medium]`+`[low]`, relocation byte-faithful; bencher
|
||
causally-exonerated DECISIVE on byte-identical IR+binaries,
|
||
baseline pristine — M2/M3/M5 disposition) → tidy `f2cdd67`/
|
||
`f683f1a` (DONE 7/7; the audit Resolution mechanism+scope
|
||
corrected on planning-time evidence after a correctly-BLOCKED
|
||
plan defect — the "two+ defects ⇒ fix the upstream artifact"
|
||
discipline, not a third patch). No fieldtest (zero
|
||
authoring-surface change — reasoned exclusion). Stays briefly
|
||
for context; remove once stale (full record in
|
||
`git log`). Original decided-shape rationale
|
||
retained below for context until pruned.
|
||
|
||
**Motivation.** The just-closed `docs-honesty-lint` milestone is
|
||
the proof this is not a cleanup problem: it ran a full docs pass
|
||
and DESIGN.md stayed 2905 lines, because honesty-lint operates
|
||
*within* a job (tense / Wunschdenken / citation) and its own
|
||
audit-trail invariant *mandates retaining* the bulky
|
||
decision-record prose (DESIGN.md:242 "records the *why* … for
|
||
the audit trail"; the architect Sweep-5 discriminator explicitly
|
||
keeps "present-tense design rationale"). You cannot clean your
|
||
way out of conflation — the conflation itself is the structural
|
||
defect.
|
||
|
||
The three conflated jobs and their distinct consumers:
|
||
- **Contract layer** — prescriptive, test-linked invariants
|
||
(e.g. Decision 6's seven hard constraints, the Roundtrip
|
||
Invariant, the borrow/own binding rules). Consumer:
|
||
`architect` (mandatory full read at *every* milestone close),
|
||
`grounding-check` (every spec), the bench drift lints. Wants:
|
||
structured, addressable, test-linked, small, hot. This is the
|
||
only job that belongs on the per-milestone hot path.
|
||
- **Model exposition** — whitepaper-shaped model narratives
|
||
(RC + Uniqueness, typeclasses, effects). Consumer: onboarding
|
||
(fresh-context LLM, `fieldtester`). Irreducible — the code
|
||
cannot speak the gestalt; one whitepaper per model.
|
||
- **Decision-record / relitigating-guard** — why X was chosen,
|
||
why Y/Z were rejected (region inference, tracing GC, …).
|
||
Consumer: a future `brainstorm`, so it does not re-propose a
|
||
settled-and-rejected idea. (This tier was migrated out of
|
||
DESIGN.md at split time and later retired entirely; the
|
||
project's history lives in `git log`.)
|
||
|
||
Underlying principle: the code is authoritative for *what it
|
||
does* — delete every description of behaviour, the code speaks;
|
||
the spec's irreducible job is *what is promised* plus a pointer
|
||
to the green test that ratifies it (code answers *what*; spec
|
||
answers *what is promised*; test answers *does the promise still
|
||
hold*).
|
||
|
||
**Decided shape.** `design/` split on the consumer/lifetime
|
||
axis — **not** by Decision-number (that is cosmetic; the
|
||
architect would still read everything). `design/contracts/` (the
|
||
hot, test-linked invariant set), `design/models/` (one
|
||
whitepaper per model). `design/INDEX.md` is the typed ledger
|
||
spine: per entry a one-liner + metadata
|
||
`{kind, ratifying-test, consumer/lifetime}` + a content link
|
||
whose target is polymorphic — a `design/contracts/…` file, a
|
||
single-owner `//!` source header (mangling, env, roundtrip,
|
||
float, tail-calls, data-model), or a `design/models/…`
|
||
whitepaper. INDEX.md is the sole addressable entry point.
|
||
Decision-records move out of DESIGN.md; the honesty-lint
|
||
audit-trail invariant is repointed in the same milestone.
|
||
|
||
**Rejected — do not relitigate.** Quarto / `.qmd` was evaluated
|
||
and rejected: every distinctive Quarto feature (rendered
|
||
HTML/site, figures, citations, cross-ref resolution, executable
|
||
chunks) is render-time value for a *human reader who does not
|
||
exist here* — the `design/` consumer is an LLM reading raw
|
||
files. Executable chunks would reintroduce a second "is the
|
||
invariant green?" truth that can diverge from `cargo test` (the
|
||
duplicate-source-of-truth failure mode); Quarto `@ref`
|
||
cross-refs degrade the raw-file reader (unresolved tokens vs.
|
||
resolving relative-path links); plus a non-Rust toolchain
|
||
dependency for zero in-loop value. Plain Markdown + a typed
|
||
INDEX.md is strictly better for this consumer. A plain shard by
|
||
Decision-number is likewise rejected (changes nothing for the
|
||
architect hot path).
|
||
|
||
**Sequencing / blast radius.** Deliberately scheduled *after*
|
||
the in-flight Embedding ABI arc — a substantive sequencing
|
||
reason, not effort. `DESIGN.md` is referenced by name by at
|
||
least `bench/check.py` (history-anchor / `design_schema_drift`),
|
||
`bench/architect_sweeps.sh` (Sweep 5; exit 2 = "could not find
|
||
DESIGN.md"), the reading lists of `ailang-architect` /
|
||
`ailang-grounding-check` / `ailang-fieldtester`, the CLAUDE.md
|
||
"Roles of docs/…" section, and the honesty-lint invariant's own
|
||
scope — load-bearing invariants and agent contracts, not just
|
||
paths. Every Embedding-ABI milestone close runs `audit`
|
||
(architect + bench) against DESIGN.md; restructuring it mid-arc
|
||
would break the architect's reading list and the drift scripts
|
||
*under* an in-flight milestone. Hence no overlap with active
|
||
implementation that closes audits against the current
|
||
DESIGN.md. Brainstorm hard-gate before any plan/code — it
|
||
rewrites enforced invariants and agent contracts.
|
||
- context: spec `docs/specs/2026-05-19-design-md-rolesplit.md`
|
||
(incl. the Boss-adjudicated relocation Appendix; grounding-check
|
||
PASS ×2); origin 2026-05-18 chat (DESIGN.md-management
|
||
dialogue — six-turn convergence). CLOSED — audited +
|
||
drift-resolved; hard gate enforces the honesty spirit.
|
||
|
||
- [x] **\[milestone\]** Formal cross-links in the `design/` ledger
|
||
(browsable wiki) — CLOSED 2026-05-19. The positive completion
|
||
of DESIGN.md → `design/`: informal prose cross-references between
|
||
contracts/models are now **formal file-relative Markdown links**
|
||
resolved relative to the containing file, gated by RED-first
|
||
`design_index_pin.rs` **clause-5** that fails the build on any
|
||
body link that does not resolve into the durable tier (`design/`
|
||
+ `crates/**` + `runtime/**` only — never `docs/`, never a
|
||
`#fragment`). clause-5 ∘ clause-3 = complete invariant: every
|
||
contract cross-reference is a resolving durable file-link or
|
||
clause-3-forbidden history prose. INDEX spine stays
|
||
repo-root-relative (registry tier, clause-1 byte-unchanged) — the
|
||
move-fragility rationale that drives body prose to file-relative
|
||
is absent for the file that never moves. Shipped in one iter
|
||
(5/5 tasks): clause-5 with `strip_fences` (toggle on ```/~~~ so a
|
||
`](` inside a fence is not treated as a link) + RED-first via
|
||
identity-stubbed `strip_fences` synthetic vectors; 7 prose
|
||
conversions (8 link tokens — float-semantics:69/100,
|
||
embedding-abi:45, memory-model:44/105, scope-boundaries:48/88
|
||
with mixed-referent split into a source link + a Pipeline
|
||
cross-file link); clause-6 disposition (b) on the 2
|
||
PROSE_ROUNDTRIP homeless pointers (pointer removed, behavioural
|
||
`ail merge-prose` prose preserved); pin-safe positive-half
|
||
paragraph in `honesty-rule.md`.
|
||
- context: spec `docs/specs/2026-05-19-design-ledger-formal-links.md`
|
||
(grounding-check PASS ×3 across two corpus-grounded amendments —
|
||
clause-6 + cross-ref definition; clause-5 fence-skip + closed
|
||
convert-set enumeration); plan
|
||
`docs/plans/design-ledger-formal-links.1.md`; origin this
|
||
session's six-turn design conversation following the split close.
|
||
CLOSED — audit clean (zero drift, bench trio 0/0/0); no tidy,
|
||
no fieldtest (zero authoring-surface change — reasoned exclusion).
|
||
|
||
- [ ] **\[milestone\]** Flat array/slice primitive — performance
|
||
follow-up to the Embedding ABI arc, *not* a capability gap.
|
||
AILang's only sequence is the recursive `List` ADT (one RC cell
|
||
per element). The ABI arc resolved chunk-crossing by having the
|
||
M5 adapter unroll each `data-server` chunk host-side into per-tick
|
||
`(State, Tick) -> State` calls (M4's cons-list crossing was
|
||
retired as speculative infra) — so the residual perf question is
|
||
no longer "1024-cell cons-chain per chunk" but **host-per-tick-FFI
|
||
vs. an eventual batch/flat-array crossing**. M5's friction-harvest
|
||
measured the per-tick FFI at **≈ ~206 ns/tick at real EURUSD
|
||
volume** (≈ 658 ms / 3.19 M ticks). Open decision: whether a
|
||
contiguous array/slice primitive (to amortise that per-tick cost
|
||
via a batch crossing) is worth a *language* change — it interacts
|
||
with uniqueness inference, RC, and the
|
||
structurally-decreasing-recursion totality story, and is out of
|
||
the (now-closed) ABI arc's scope. Now decidable on the measured
|
||
number, not speculation.
|
||
- depends on: Embedding ABI — M5 (shipped; measured ~206 ns/tick
|
||
— the justification input now exists).
|
||
- context: 2026-05-18 chat (user deferred this to keep the ABI
|
||
arc clean); the friction-harvest number recorded in the M5
|
||
audit commit; M4 retirement decision recorded in the M5 closing
|
||
commits.
|
||
|
||
- [ ] **\[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); the branching-builder
|
||
counter-example that surfaced the gap landed in the it.3 iter
|
||
commit.
|
||
|
||
- [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: shipped in the rpe.1 iter commit.
|
||
|
||
- [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: `git log --before=2026-05-11 --grep=...` for the
|
||
pre-archive-cutoff rationale.
|
||
- 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: 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: shipped in the cli-diag-human iter commit.
|
||
- [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: 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: flagged by the 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: `git log --before=2026-05-11 --grep=...` for the
|
||
pre-archive-cutoff rationale.
|
||
- [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: pre-milestone-22 "Boehm transitional" decision —
|
||
see commits from that window.
|
||
- [ ] **\[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.
|
||
|
||
- [x] **\[todo\]** DESIGN.md effect-prose is fiction — standalone
|
||
documentation-honesty tidy. Shipped 2026-05-16 as iter
|
||
effect-doc-honesty (a29700c): the three false effect-system claims
|
||
(row-polymorphic `![IO | r]`; `IO`+`Diverge` both wired up;
|
||
`Term::Do` "effect-handler table at link time") reconciled to the
|
||
real flat-closed-set / IO-only / `IndexMap`+codegen-`match`
|
||
mechanism, satellite lockstep done, guarded by a 4-test
|
||
doc-presence pin. No language/checker/codegen change.
|
||
- context: shipped in the effect-doc-honesty iter commit.
|
||
- [ ] **\[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: surfaced in the design-md-consolidation audit close
|
||
(2026-05-10).
|
||
- [ ] **\[todo\]** `ailang-plan-recon` site-undercount countermeasure
|
||
(P2, **escalated** — now a structural recon-contract defect, no
|
||
longer a single-milestone curiosity) — the recon agent hand-lists
|
||
the change blast radius and has under-counted it **four times across
|
||
two milestones, through a different site type each time**:
|
||
loop-recur.1 walker arms, loop-recur.2 cross-module `synth` callers,
|
||
loop-recur.tidy implicit, and now remove-mut-var-assign.1 (in-source
|
||
`mod tests` fns + a drift-pin fn + 5 orphaned `.ail.json` carve-outs
|
||
+ a non-enumerated `codegen_import_map_fallback_pin.rs` E0599) **plus
|
||
a spec-named doc the recon was told did not exist**
|
||
(`crates/ailang-core/specs/form_a.md` — the spec listed it for
|
||
deletion; a Boss plan-time grep checked only `docs/form_a.md`,
|
||
concluded "no such file", and propagated that into the recon brief;
|
||
caught only at milestone-close audit). Two distinct gaps: (a) the
|
||
compile-driven sweep catches exhaustive-`match`/caller misses but
|
||
**not** non-compile-checked sites (docs, fixtures, drift pins) —
|
||
those need a separate "every spec-named path is verified to exist
|
||
and is handled" cross-check; (b) a recon brief must never inherit an
|
||
unverified "X does not exist" — existence claims feeding a recon are
|
||
themselves load-bearing and must be tree-wide, not dir-scoped.
|
||
Tighten `skills/planner/agents/ailang-plan-recon.md`: for any
|
||
signature-change / enum-variant / removal scope the recon REPORTS
|
||
the compile-driven enumeration as the authoritative code-site set
|
||
(hand-list advisory) AND emits a spec-named-path existence table
|
||
(every path the spec names, `ls`-verified, with its handling task).
|
||
No language change; an agent-definition discipline fix.
|
||
- context: loop/recur close audit (architect `[low]`, 2026-05-18) +
|
||
remove-mut-var-assign close audit (architect `[high]` form_a.md +
|
||
`[medium]` process-drift, 2026-05-18); pairs with planner Step-5
|
||
items 7+8 (those scrub the *plan*; this scrubs the *recon* and the
|
||
*recon brief*).
|
||
- [ ] **\[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: surfaced in the 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) 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
|
||
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.
|
||
- [ ] **\[todo\]** `*.bump_s` throughput baseline is stale vs current
|
||
hardware — `bench/check.py`'s `throughput.*.bump_s` family (the
|
||
fastest, most jitter-prone metrics) reads ~+5–13% over
|
||
`bench/baseline.json` on the current machine. Localised at the
|
||
iteration-discipline-revert audit (2026-05-16): an interleaved
|
||
3×60-run measurement of `bench_list_sum` bump_s built from the
|
||
byte-oracle commit `1ff7e81` shows the *same* ~+11% elevation as
|
||
HEAD, and the two bump binaries are `cmp`-identical — so this is
|
||
environmental drift relative to the 2026-05-09 baseline-capture
|
||
machine state, **not** a codegen regression. Re-capture the full
|
||
`*.bump_s` set on current hardware from a known-clean commit and
|
||
recalibrate the `bump_s` baseline+tolerance pair (or widen the
|
||
tolerance) so the noise floor stops tripping `check.py` exit 1.
|
||
Pure bench-harness recalibration; no language change.
|
||
- [ ] **\[todo\]** `check.py` RC-latency `*.max_us` is a structural
|
||
false-positive at `-n 5` — **distinct** from the `*.bump_s`
|
||
staleness above (different metric family, different root cause).
|
||
`latency.{explicit,implicit}_at_rc.max_us` is the single worst of
|
||
~5000 samples over only 5 runs, dominated by OS scheduling / THP /
|
||
IRQ jitter on a shared host, not allocator behaviour. It has fired
|
||
a false `REGRESSION` on **three consecutive no-runtime-change
|
||
milestones** (iteration-discipline-revert, prose-loop-binders,
|
||
remove-mut-var-assign) and vanished on identical-code re-run every
|
||
time (remove-mut-var-assign close: HEAD vs `48e7774` bench binaries
|
||
`cmp`-byte-identical, `explicit_at_rc.max_us` collapsed
|
||
+108%→-2.30% ok by rerun3) — a ~3-for-3 false-positive rate on null
|
||
changes, while the median/p99/p99.9 of the same benchmarks held
|
||
across all re-runs. Pick one mitigation: drop `*.max_us` from the
|
||
gating set (keep median/p99/p99.9, which are the trustworthy
|
||
signal), or raise `-n` substantially for the tail metrics, or pin
|
||
the latency arm to an isolated cpuset. Pure bench-harness change;
|
||
no language change.
|
||
- context: remove-mut-var-assign close audit, bencher localisation
|
||
(2026-05-18) — byte-identical-binary causal exoneration + the
|
||
3-milestone false-positive history.
|
||
- context: the bencher localisation evidence in the
|
||
iteration-discipline-revert audit commit.
|
||
- [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: 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: shipped in the str-concat iter commit.
|
||
|
||
- [~] **\[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).
|
||
|
||
**Sub-milestone sequencing is UNDER RE-THINK with the user
|
||
(2026-05-16) — no further sub-milestone is planned or brainstormed
|
||
until that conversation happens.** Why: the 2026-05-15 decomposition
|
||
named "(2) effect-handler infrastructure as a prerequisite for any
|
||
non-IO/non-Diverge effect" as the next step. A read-only recon of
|
||
the live effect subsystem (2026-05-16) showed that premise is false:
|
||
the effect raise / declared-set / `UndeclaredEffect`-subset / call-
|
||
propagation machinery is already fully generic over an arbitrary
|
||
effect string; `!Mut` needs **no** effect-handler machinery. The
|
||
only real code prerequisite the recon found is one hard-coded
|
||
assumption (`linearity.rs` walks every `Term::Do` arg as `Borrow`;
|
||
`EffectOpSig` has no per-arg mode field) — a single struct field,
|
||
not a milestone. A brainstorm spec that tried to make sub-ms-2 a
|
||
standalone deliverable bundled that speculative one-field capability
|
||
with an unrelated DESIGN.md honesty fix; the user correctly rejected
|
||
the bundle as incoherent and the build-ahead-of-consumer half as the
|
||
iteration-discipline trap repeated. **Resolution:** (a) the DESIGN.md
|
||
effect-honesty correction is split out and runs now as a standalone
|
||
documentation tidy (see the P2 `[todo]` "DESIGN.md effect-prose is
|
||
fiction" below); (b) the `arg_modes` finding is recorded as recon
|
||
context for the future `!Mut` design, NOT built ahead — it is
|
||
first-iteration material of whatever the `!Mut` milestone turns out
|
||
to be, validated there against a real consuming op, never a
|
||
synthetic test op; (c) the old "(2)…(5)" sequence (effect-handler
|
||
infra → `!Mut`+`ref a` → `MutArray a` → `Stateful a b`+`pipe`) is
|
||
no longer treated as settled — the whole ordering and granularity
|
||
is what the pending user conversation re-decides.
|
||
|
||
**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: AILang has no
|
||
`while`/`for` and this milestone does not introduce one —
|
||
repetition stays recursion, exactly as the language has it today
|
||
(structural / tail recursion); a `mut` block is a sealed
|
||
expression, never a loop 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: surfaced in iter ct.3 + ct.4 close (2026-05-11).
|
||
- [ ] **\[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: 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: surfaced in iter 22-tidy.7 (2026-05-10).
|
||
- [ ] **\[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: surfaced in iter 22-tidy.6 (2026-05-10).
|
||
- [ ] **\[idea\]** Richer integration paths between RC and
|
||
uniqueness — deferred from the 21' arc; revisit once the
|
||
uniqueness inference covers more program shapes.
|
||
- [ ] **\[idea\]** LLM tool-use schema / MCP server / LSP front-end
|
||
over the prose-roundtrip cycle — additive layers on top of the
|
||
static-prompt `ail merge-prose | client | ail parse | ail check`
|
||
path (LLM calls back into `ail parse`/`ail check` mid-turn; an
|
||
MCP-compatible client discovers AILang's resources/tools/prompts).
|
||
Relocated here from DESIGN.md §"prose roundtrip" + PROSE_ROUNDTRIP.md
|
||
by the docs-honesty-lint milestone (forward intent does not live in
|
||
the canonical docs).
|
||
- context: `docs/specs/2026-05-18-docs-honesty-lint.md`
|
||
- [ ] **\[todo\]** Sweep 5 self-match anchor-exclusion — `bench/architect_sweeps.sh`
|
||
Sweep 5's regex matches the DESIGN.md discriminator subsection's own
|
||
forbidden-phrasing catalogue (L62 `"planned / will back / on the
|
||
path to / if-when X arrives"`) forever, so the script inherits a
|
||
guaranteed self-referential EXIT 1 the architect must re-adjudicate
|
||
by hand each run. Not a new failure mode (Sweeps 1-4 already retain
|
||
legitimate date-anchors; non-zero is advisory-by-design per the
|
||
script header) — minor signal-erosion only. Optional fix: a
|
||
negative-lookbehind / line-range exclusion so the rule's own example
|
||
list is not a hit. P3 — may be cut; the architect adjudicates the
|
||
single known self-match trivially today.
|
||
- context: docs-honesty-lint audit commit (architect `[medium]`
|
||
advisory wart, carry-on).
|