Files
AILang/docs/roadmap.md
T
Brummel 9fdc4cacff iter form-a.1 (Tasks 6-12): milestone close
Second half of the form-a-default-authoring milestone-close iter
(Boss-decided strategy C, big-bang). All seven tasks DONE; cargo
test --workspace green at every per-task boundary.

T6 — Bench-driver suffix flip from .ail.json to .ail across 4
Python scripts + run.sh. compile_check.py + cross_lang.py exit 0.

T7 — Re-author e2e.rs raw-JSON-inspect tests:
- diff_detects_changed_def — derive sum.ail.json on-the-fly via
  `ail parse examples/sum.ail` into tempdir, then mutate + diff.
- borrow_own_demo_modes_are_metadata_only — same pattern.
- reuse_as_demo_under_rc_uses_inplace_rewrite — same pattern.
- render_parse_round_trip_canonical — RETIRED (subsumed by T1's
  cli_parse_then_render_then_parse_is_idempotent over whole corpus).
- ail_run_accepts_ail_source_with_same_stdout_as_ail_json —
  re-authored to derive hello.ail.json in a per-process tempdir
  from hello.ail via `ail parse`, then assert dual-form stdout.

T8 — Bulk-delete 156 non-carve-out .ail.json. Inventory:
8 .ail.json (carve-outs, alphabetical: broken_unbound + prelude
+ 3× test_22b2_* + 3× test_ct1_*) + 157 .ail. carve_out_inventory
test un-#[ignore]'d and green. Forward-pulled 20 repairs that the
T1-5 dispatch's recon missed (12 Group-B suffix + 5 Group-A
load_workspace + 3 ail_run sites). Also forward-pulled T9 Step 5
(schema_coverage corpus flip from .ail.json to .ail) to satisfy
T8's green-gate.

T9 — Retire obsolete roundtrip tests:
- print_then_parse_round_trips_every_fixture (round_trip.rs)
- every_ail_fixture_matches_its_json_counterpart (round_trip.rs)
- cli_render_then_parse_preserves_canonical_bytes_on_every_fixture
- Dead helpers: list_json_fixtures ×2, round_trip_one,
  strip_trailing_newlines.
Schema-coverage corpus already flipped in T8 (forward-pull).

T10 — DESIGN.md §"Roundtrip Invariant" (lines 2027-2109) restated
with parse-determinism + idempotency + CLI-pipeline-idempotency +
carve-out-anchor framing. Five surviving enforcement tests named.
§"Float literals" and §"Why anchored at top level" preserved.

T11 — §A4 doctrine edits: CLAUDE.md:5-6 + DESIGN.md:465-466.
Canonical form remains JSON-AST; authoring projection is .ail;
build derives JSON-AST in-process via ailang_surface::parse.

T12 — Milestone close:
- WhatsNew entry: user-facing language, lead with the change.
- Roadmap: [milestone] form-a struck [x] with closing note.
- Final inventory verified: 8 .ail.json + 157 .ail.
- Final cargo test --workspace: 557 passed, 0 failed, 3 ignored.
- bench/compile_check.py + bench/cross_lang.py: exit 0.

Test math: pre-iter 558 baseline + 3 new T1 tests = 561, − 1
(T7 retire) − 3 (T9 retire) = 557 final.

INDEX.md appended with the full iter summary covering T1-T12 (the
T1-5 commit at 77b28ad deferred the INDEX line to full-iter close).

Milestone [Form-A as the default authoring surface] structurally
closed. The compile-time-embed carve-out (prelude.ail.json) is
the subject of the queued follow-up milestone [Prelude embed:
Form-A as compile-time source]. audit-form-a runs as the next
dispatch.
2026-05-13 11:31:39 +02:00

21 KiB
Raw Blame History

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

  • [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").
  • [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> xdo 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. (Satisfied 2026-05-13 by iter form-a.0 — examples/prelude.ail rendered via ail render, 116 lines / 6386 bytes, round-trip-CI green.)

  • [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.
  • [milestone] Prelude embed: Form-A as compile-time source. Retire examples/prelude.ail.json by switching the include_str!-then-serde_json::from_str embed sites in crates/ailang-core/src/workspace.rs:417 and crates/ail/src/main.rs:474 to consume examples/prelude.ail at compile time, parsed via ailang_surface::parse. After this milestone the only surviving .ail.json carve-outs are the seven subject-matter rejection fixtures (§C4 (a) of the form-a-default-authoring spec); §C4 (b) becomes empty and is removed from the spec.

    Motivation. The form-a-default-authoring milestone (just closed) made .ail the authoring surface for every program in the corpus, with one exception: the prelude. The prelude is the language's most-read file and the canonical demonstration of every language feature stacked into one module; that it is the one file the user authors as .ail.json is a doctrine inconsistency relative to CLAUDE.md "authors write .ail; the build derives the JSON-AST in-process via ailang_surface::parse".

    Problem. ailang-core consumes the prelude at compile time via include_str!("../../../examples/prelude.ail.json") plus serde_json::from_str. Switching the embed to prelude.ail requires parsing via ailang_surface::parse, but ailang-core cannot import ailang-surface (the crate-cycle constraint documented at crates/ailang-surface/src/loader.rs:9-13ailang-surface depends on ailang-core for the AST types). The same embed exists at crates/ail/src/main.rs:474; that site can import ailang-surface but is in lockstep with the ailang-core one (both must reproduce the same Module value for prelude semantics to be uniform between CLI dispatch and workspace loading).

    Resolution options (decide at brainstorm time):

    • (β) Split the workspace loader: move the prelude-injection step from ailang-core::load_workspace to a new ailang-surface::load_workspace_with_prelude (or wrapper). ailang-core retains a prelude-free workspace primitive; ailang-surface is the entry point that injects the parsed prelude. Substantive refactor of ~3-5 sites.
    • (γ) build.rs generates examples/prelude.ail.json at build time from examples/prelude.ail via either an in-tree parser (would re-create a similar crate-graph issue) or a snapshot test that fails on drift. Avoids the loader-split but introduces a build-time artefact and a bootstrap concern (the ail binary itself is built from ailang-core).
    • (δ) Some hybrid: prelude stays canonical as .ail.json in storage but is rendered to prelude.ail at every commit by a pre-commit hook; the dual-form is preserved as a lockstep invariant rather than retired. Trades one inconsistency for another but is the smallest change.

    Carve-out today. Per spec §C4 (b) (added 2026-05-13), prelude.ail.json is the sole compile-time-embed carve-out during the form-a-default-authoring milestone. examples/prelude.ail already exists alongside it (rendered in iter form-a.0) as the authoring surface; the dual-form for prelude is the symptom this milestone exists to retire.

    • context: surfaced 2026-05-13 during iter form-a.1 recon; the crate-cycle issue was not anticipated by the spec authors and forced a brainstorm-level amendment to §C4. The fix exceeds the form-a-default-authoring milestone's scope and is sequenced as its own milestone to keep the architectural change auditable in isolation.
  • [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)
  • [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.
  • [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.
  • [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
  • [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.
  • [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.