Files
AILang/docs/roadmap.md
T
Brummel e864c85840 spec: form-a-default-authoring milestone — roadmap entry + draft spec
Form A (.ail) becomes the sole authoring surface for every program
in the working tree. The seven negative-test JSON-AST fixtures are
the only post-milestone .ail.json files; everything else is rendered
to .ail and the JSON counterpart is deleted (single representation
per program).

Spec decides the four open design questions inline:
- A1: in-process parse via ailang_surface::parse, no build-time
  target/ artefacts (would reintroduce a second representation).
- A2: prelude.ail ships as iter-0 pilot, validated by the existing
  every_ail_fixture_matches_its_json_counterpart gate.
- A3: per-file deletion cadence; iter 0 is the singular dual-form
  window because the JSON counterpart is the witness the CI gate
  uses.
- A4: CLAUDE.md and DESIGN.md reworded — canonical vs authoring
  forms separated, JSON-AST stays canonical/hashable, .ail becomes
  the authoring entry point.

Roundtrip invariant restates from "two forms agree byte-for-byte"
to "parse is deterministic + idempotent under print"; carve-out
inventory test pins the seven JSON-only fixtures against silent
list drift.

Grounding-check PASS over 8 load-bearing assumptions.

Iteration scope: iter 0 = prelude pilot only (render
prelude.ail.json -> prelude.ail; do not delete the JSON yet; do
not touch CLAUDE.md/DESIGN.md/tests yet).
2026-05-13 09:52:04 +02:00

322 lines
18 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 — canonical-type-names milestone closed 2026-05-11; Floats
closed 2026-05-10 and was removed once it stopped being load-bearing
context. Pick the next milestone from P1.)_
## 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\]** Retire `io/print_int` / `io/print_bool` /
`io/print_float` effect-ops + migrate example corpus to `print`.
Bulk text substitution `do io/print_<T> x``do print x` across
`examples/*.ail.json` (~86 fixtures affected). Per-type effect-ops
are deleted from `crates/ailang-check/src/builtins.rs`,
`crates/ailang-codegen/src/lib.rs::lower_app` arms, and the
corresponding runtime C glue.
- context: post-milestone-24 mechanical follow-up; the architecture-
shipping milestone (24) and corpus-migration milestone (this) are
separate so migration runs against a frozen architecture. Same
call milestone 23 made for `==` / `eq`.
- [ ] **\[todo\]** Author `examples/prelude.ail` alongside
`examples/prelude.ail.json`. Today the prelude exists only as
canonical-form JSON-AST; the textual surface form has to be
reconstructed via `ail render` every time someone wants to read
it. Check in the Form-A text as the authoring source and let
`ail parse` regenerate the JSON-AST at build time (or keep both
and gate equivalence via the existing round-trip invariant).
Either way the LLM-author's natural reading surface (Form A
text) becomes the entry point rather than a derived view.
- context: surfaced 2026-05-13 while demonstrating the
Show + print milestone via `ail prose`; the prelude is the
one file most likely to be read by humans and by LLM authors
learning the language, and it's the only fixture without a
`.ail` sibling.
- [ ] **\[milestone\]** Form-A as the default authoring surface for
examples and docs. 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.
- [ ] **\[todo\]** CLI human-mode diagnostic surface for `WorkspaceLoadError`
non-JSON `ail check` routes ct.1 errors via `anyhow`/`thiserror`
Display (`Error: <message>`) instead of going through
`workspace_error_to_diagnostic`, so the bracketed `[code]` prefix
the JSON path carries is missing. Plausibly applies to every CLI
subcommand that calls `load_workspace(&path)?` directly.
- context: JOURNAL 2026-05-11 ("Iteration ct.1") — surfaced by ct.1.8 tester.
- [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\]** 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.
## P3 — Ideas
- [ ] **\[todo\]** `compare_primitives_smoke.ail` counterpart —
the canonical happy-path exhibit for canonical-type-names ships
only as JSON. Per Decision 6, Surface is the LLM-author surface;
the milestone should have a `.ail` counterpart. Two paths: author
`examples/compare_primitives_smoke.ail` to round-trip into the
existing JSON, OR retire the JSON fixture in favour of
`examples/ct_1_ordering_signum.{ail,ail.json}` as the new canonical
happy-path exhibit.
- context: fieldtest 2026-05-11 (spec_gap).
- [ ] **\[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.