6 tasks, scoped to crates/ailang-core/src/workspace.rs only. Surface
unchanged in pd.1 (load_workspace_with survives as a thin shim
composed of the new fns + prelude inject). pd.2 will rewire surface
to call the new API directly and retire the shim. Plan-recon flagged
two ambiguities in the spec's iter-sketch vs. Components wording;
resolved here in favour of the shim approach so all existing tests
stay green without migration. ~12 in-tree validate_canonical_type_names
callers add &["prelude"] as the new second arg.
Adds the six IR-shape tests in crates/ail/tests/e2e.rs to Task 5's
file list and documents the test-harness patch (replace home-rolled
desugar+lift loop with monomorphise_workspace). The patch is the
small structural fix the rpe.1 BLOCKED orchestrator's Cat B
findings pointed to. With the two upstream codegen bugs now fixed
(commits 1fb225e + feb9413), the rpe.1 corpus migration plus this
Cat B harness patch should make the full retry green.
Single-iter plan implementing the rpe.1 spec. Ten tasks:
RED test, 92 .ail corpus migration, 6 .prose.txt regen,
four-site compiler deletion, five test-body migrations,
six doc-comment touch-ups, seven DESIGN.md sites, three E2E
comment touch-ups, bench ratification (per spec §C4 (a)),
and the close-out (roadmap strike, journal, stats).
Plan absorbs four spec-vs-code mismatches surfaced by recon
(one install-test instead of three, 92/6 fixture split rather
than 98, synth.rs:215 parallel registry, incidental test-body +
doc-comment sites) as an "Errata vs. spec" block at the top of
the plan; the spec itself is unchanged because none of the
mismatches affect load-bearing invariants.
Seven-task plan for adding str_concat : (borrow Str, borrow Str) -> Str
as a runtime + checker + codegen primitive in the four-site-lockstep
pattern established by str_clone / int_to_str / bool_to_str (iter
24.1).
T1: RED — new fixture examples/show_user_adt_with_label.ail
(LLM-natural Show body using str_concat) + E2E test
crates/ail/tests/str_concat_e2e.rs.
T2: runtime/str.c — append ailang_str_concat C helper after the
existing ailang_str_clone implementation (slab-alloc, memcpy
twice, terminate).
T3: ailang-check/src/builtins.rs — install + list() + new unit test
install_str_concat_signature; type is Fn { 2x Str borrow -> Str own,
effects empty }.
T4: ailang-codegen/src/lib.rs — extern declare ptr @ailang_str_concat
(ptr, ptr); lower_app arm after str_clone arm; extend
is_builtin_callable match list; append IR-pin unit test
str_concat_emits_call_to_ailang_str_concat.
T5: lockstep collision repair — rename str_concat -> format_label in
examples/bug_unbound_in_instance_method.ail and the corresponding
pin test (the fixture currently uses str_concat as the unbound
symbol because that was the literal fieldtester repro; after
shipping the builtin it would silently become a builtin call and
the pin would fail; format_label preserves the pin's intent under
an LLM-author-realistic name that will never become a builtin).
T6: DESIGN.md new §"Heap-Str primitives" subsection between the
milestone-24 amend block and the Primitive output paragraph,
cataloguing all 5 primitives (int_to_str, bool_to_str, float_to_str,
str_clone, str_concat) with one-line signatures + descriptions and
cross-references to runtime/str.c.
T7: verification + roadmap [x] strike entry.
Open questions resolved Boss-side ahead of dispatch:
- DESIGN.md anchor: new §"Heap-Str primitives" subsection (option (b)
in recon report) — the existing milestone-24 Show-backer enumeration
stays unchanged; the new section gives the heap-Str-primitive
category an explicit home.
- bug-fixture collision: rename to format_label (option (a) in recon
report) — LLM-author-realistic, preserves regression coverage.
Expected net delta: +3 tests from baseline (559 -> 562 green).
Post-fieldtest documentary tidy. Six tasks: T1-3 add three new
sections to crates/ailang-core/specs/form_a.md (Class, Instance,
Constraints on polymorphic fns) anchored against the live corpus
(test_22c_user_class_e2e for class-decl, show_user_adt +
mq3_class_eq_vs_fn_eq_classmod for instance, cmp_max_smoke for
constraints), each with EBNF + canonical-form-rule paragraph +
verified example. T4 fixes 5 contradictory "seven carve-outs"
sites in docs/specs/2026-05-13-form-a-default-authoring.md (the
audit-form-a journal flagged 3 sections; recon expanded to 5
total). T5 deletes the empty mod tests {} placeholder + its
6-line explanatory comment in crates/ailang-core/src/hash.rs
(form-a.1 T5 relocated the unit tests; placeholder serves no
purpose). T6 rewrites round_trip.rs module + inner test
docstrings to use the post-T10 four-property framing
(parse-determinism + idempotency + CLI-pipeline-idempotency +
carve-out-anchor) instead of retired Direction-1/Direction-2
language.
Drift item B2 (plan-file "seven carve-outs") dropped from this
iter per recon: the docs/plans/2026-05-13-iter-form-a.1.md file
contains zero defective sites; all four "seven" mentions are
internally scoped to §C4(a), arithmetic, or future-state. The
audit-form-a journal "two sites" claim does not match what is
in the plan file at HEAD.
All tasks are pure docs/comment changes. cargo test --workspace
stays at 559 green at every task boundary.
T1-5 dispatch journal surfaced 4 additional e2e.rs tests beyond
the originally-named diff_detects_changed_def that raw-read
.ail.json bytes and will break at T8 deletion:
- borrow_own_demo_modes_are_metadata_only (substring assertions)
- reuse_as_demo_under_rc_uses_inplace_rewrite (substring assertions)
- render_parse_round_trip_canonical (whole-corpus property already
pinned by T1's cli_parse_then_render_then_parse_is_idempotent —
retire as redundant)
- ail_run_accepts_ail_source_with_same_stdout_as_ail_json (dual-form
smoke — re-author to derive the .ail.json on-the-fly via ail parse
on a working fixture like hello.ail)
T7 grows from 4 steps to 7 steps; net test-count change −1 (retire
render_parse_round_trip_canonical). T7 close: 559 tests green.
Iter form-a.1 of the form-a-default-authoring milestone, Boss-decided
strategy C. End-of-milestone iter — all 156 non-carve-out .ail.json
files render+delete in this iter, plus all consumer migrations, plus
the roundtrip-CI restatement, plus the §A4 doctrine edits, plus
WhatsNew + roadmap strike.
Sequence: additive-then-subtractive.
- T1-2: new T1/T4 tests (additive) + render 99 missing .ail.
- T3-5: consumer migration (Group A load_workspace + Group B
subprocess + Group C production-code unit-test relocation).
- T6: bench-driver suffix updates.
- T7: e2e.rs synthesised-diff re-author to derive sum.ail.json
on-the-fly via ail parse.
- T8: bulk delete 156 .ail.json (carve-outs preserved).
- T9: retire obsolete roundtrip tests + flip schema_coverage corpus.
- T10: §C3 DESIGN.md roundtrip invariant restatement (4 properties
named, 5 enforcement tests pinned).
- T11: §A4 doctrine edits — CLAUDE.md + DESIGN.md "What this
Decision does not do" sentence.
- T12: WhatsNew + roadmap strike + final cargo test + bench check
+ 8-file inventory verification.
cargo test --workspace expected green at every task boundary. The
carve_out_inventory test is added #[ignore]'d in T1 and un-ignored
in T8 (RED-pinned until deletion happens).
Boss will dispatch implement in two task-ranges: [1,5] then [6,12]
to keep orchestrator-agent context bounded.
Iter 0 of the form-a-default-authoring milestone. One file
rendered (examples/prelude.ail.json -> examples/prelude.ail via
`ail render`), zero source-code edits, zero test-infra changes.
The migration mechanism is validated on the hardest single file
in the corpus (prelude exercises every language feature: classes,
superclasses, polymorphic free fns, IO effects, ctor patterns,
loader auto-injection). The two auto-discovering roundtrip tests
in round_trip.rs pick up the new .ail via read_dir without test
edits; both must pass for the iter to close.
Task 1: render + verify both roundtrip tests green + workspace
sanity check (5 steps).
Task 2: per-iter journal + INDEX append (2 steps).
prelude.ail.json retained per spec §A2 — this is the singular
dual-form iter; iter 1 deletes it with the bulk test-infra
refactor.
Tasks: T1 DESIGN.md new subsection §Cross-module references in
synthesised bodies documenting three iter-24.3 strengthenings
(canonical-form type_args, post-mono cross-module-ref import_map-
bypass, FreeFnCall constraint-residual push). T2 codegen import_map-
fallback pin (integration-style in crates/ail/tests/). T3 bare-name
poly-fn dot-qualified-branch invariant pin (asserts NoInstance fires
at typecheck not codegen). T4 replace unwrap_or_default() at
check/lib.rs:2852-2858 with expect(class_methods registry coherence).
T5 extract apply_subst_and_normalize helper from duplicated mono.rs
sites at :685-714 + :1284-1303. T6 integration + bench verification.
Boss decisions in Pre-flight section: T1 placement = new subsection
between DESIGN.md:1693 and :1695 (anchorable block beats append-in-
place); T2+T3 = integration-style tests (canonical xmod-test pattern,
not in-crate codegen); T5 helper extracts common concrete-resolved
arm only, returns Option<Type> so each call site keeps its rigid-
var / unit-default policy explicit.
Plan for iter 24.3, the second + final half of milestone 24.
Task 1: append fn print to prelude.ail.json with explicit-let body
\\x -> let s = show x in do io/print_str s. Task 2: mono symbol
IR-shape pin (AST-level Term-pattern match on post-mono
print__Int.body — protects the load-bearing let-binder for the
heap-Str RC discipline per eob.1 Str carve-out). Tasks 3-5:
three E2E fixtures + tests (positive 4-prim smoke, user-ADT
data IntBox + instance prelude.Show IntBox, negative print f
where f : Int -> Int firing NoInstance). Task 5 also extends
the Float-aware NoInstance arm at lib.rs:770-779 with a parallel
Show-aware branch. Task 6: DESIGN.md amendments (§Prelude classes
flip print to shipped, §Float semantics gains Show-Float NaN
paragraph). Task 7: roadmap P1 Show+print flips to [x] + new
P2 entry for io/print_int|bool|float retirement (insert at top of P2).
Task 8: integration + bench.
Boss decisions in plan Pre-flight section: ship explicit-let
(no auto-desugarer); AST-level IR-shape pin (Term pattern match);
NoInstance Show diagnostic wording with literal DESIGN.md section
cross-reference; P2 top-insertion for the retirement entry;
let-bound concrete fn-type for the negative fixture.
Plan for iter 24.2. Tasks 1-2: preemptive 22b-Show → TShow/tshow
rename across 21 fixtures + 2 Rust test files (the recon-corrected
count vs spec's '~14' undercount). Task 3: insert 5 new top-level
defs (class Show + 4 primitive instances Int/Bool/Str/Float) at
prelude.ail.json:223 before fn ne. Task 4: mono-synthesis existence
test for show__T. Task 5: dispatch-singleton pin (show vs tshow,
both Step-2 singletons post-migration). Task 6: hash-stability pin
extension. Task 7: DESIGN.md §Prelude(built-in) classes amendment.
Task 8: integration + bench verification.
Recon-Boss reconciliation captured in Pre-flight notes: 21 fixtures
(not 14), prelude line 223 insertion point, str_clone-reachability
verified by Task 3 round-trip.
Rigid-var refinement type-unification leg in refine_multi_candidate_residual
(high-1); same-module bare-class qualifier shape accepted (high-2);
class-method-shadowed-by-fn warning tightened to require an instance
in workspace_registry (medium-1); DESIGN.md Data Model schema fragments
cross-reference the canonical-form rule (medium-2).
Q1 revised from carrier "fix both sites" to "fix only discharge-time"
after code inspection: synth-time resolve_method_dispatch is invoked
with concrete_arg_type: None and the residual's metavar is constructed
AFTER the dispatch call, so the rigid-var leg has no residual type to
unify against — class-only filter is semantically correct there.
Q2 conservative tightening: filter via "any candidate class has any
registry instance" instead of deferring to App-arm via pending-warning
list (full spec rule).
Installs the dispatch infrastructure without retiring
MethodNameCollision yet:
- 2 new CheckError variants (AmbiguousMethodResolution, UnknownClass)
- NoInstance gains optional candidate_classes field
- ResidualConstraint gains optional candidates: BTreeSet<String>
- Env gains workspace-flat method_to_candidate_classes index
- New resolve_method_dispatch helper implementing the 5-step rule
- Synth Var-arm rewritten to route through the helper
- Constraint-discharge refines multi-candidate residuals
(concrete-type path → registry filter; rigid-var path →
declared-constraint filter)
- Mono's residual-to-target mapping mirrors the discharge refinement
End-to-end multi-class fixtures still gated by MethodNameCollision
(iter mq.3); new path exercised in this iter only by 6 unit-test
cases on resolve_method_dispatch + 3 each on refine and mono
resolver.
Boss decisions on recon open questions: residual extension via
Option<BTreeSet<String>> field; index lives workspace-flat on Env;
NoInstance.candidate_classes wired through to_diagnostic this iter;
qualify_class_ref_in_check consolidation deferred per mq.1
known-debt.
Iter mq.1 plan: move InstanceDef.class / Constraint.class /
SuperclassRef.class from bare-only to canonical form per ct.1's
existing rule, extend validate_canonical_type_names with three new
field walks, narrow check_class_name_fields to ClassDef.name-only,
qualify build_registry's class_def_module + class_by_name + entries
+ four lookup sites, qualify ClassMethodEntry.class_name +
class_superclasses, update affected test-assertion strings.
Pre-flight recon flagged that the spec's '14 fixtures migrated' cost
claim is over-stated: all existing fixture class refs are intra-module
under the canonical-form rule. Plan does not add migration work that
does not need doing; Task 7 Step 3 verifies zero existing-fixture
diff.
Six tasks: (1) runtime C functions in runtime/str.c, (2) builtin
install lockstep across builtins.rs + synth.rs + list() row + 2
unit tests, (3) codegen IR wiring (2 declares + 2 lower_app arms +
is_static_callee whitelist), (4) 2 IR-shape unit pins, (5) regen
5 IR snapshots, (6) 4 fixtures + 5 E2E tests + acceptance gate.
Mechanical mirror of hs.4's int_to_str/float_to_str pattern. Plan
captures every Edit verbatim (old_string + new_string) per planner
Iron Law. Pre-existing drift fix included inline: int_to_str row
missing from builtins.rs::list() since hs.4 — adding it adjacent
to the two new rows. Three open questions from plan-recon resolved
by Boss judgement (C uses bool from <stdbool.h>; tests live in
crates/ail/tests/e2e.rs as the existing real path; int_to_str
list() row drift fixed inline).
Single iter closes the milestone: language rule at three check-time
sites (uniqueness.rs + linearity.rs + linearity.rs doc-comment),
four lockstep ret_mode-Own edits (builtins.rs + synth.rs for
int_to_str and float_to_str), one Str carve-out in
drop_symbol_for_binder's App arm, RED→GREEN verification of the
two existing pinning tests, two new positive tests (Int-arg
primitive non-leak, repeated Str-arg borrow), DESIGN.md anchor
under Decision 10 covering BOTH arg-position rules (Ctor=Consume,
Do=Borrow), WhatsNew entry, roadmap close of the heap-Str ABI P1
milestone.
Plan-recon DONE: lint-side-effect surface (over-strict-mode on
(own T) params used solely via effect-ops) is empty for the
current corpus, so no follow-up sweep iter needed. IR-snapshot
regen confirmed no-op. Two inline IR-shape tests at
codegen/src/lib.rs:4127-4205 stay green (ret_mode flip does not
change their emitted-call assertions).
Wires the hs.3 runtime symbols (ailang_int_to_str, ailang_float_to_str)
into live AILang builtins. Four layers, one iter: checker install +
synth.rs lockstep partner; IR-header declares + Emitter::lower_app
arms (float arm replaces CodegenError::Internal, new int arm); rc.c
hoisted to unconditional link (weak attr on str.c's extern becomes
permanent no-op, retained intentionally); two new IR-shape pins + four
new E2E (two stdout-smoke, two RC-stats discipline) + drop.rs comment
refresh. Spec line refs were stale (hs.1+hs.2+hs.3 commits shifted
them); plan anchors to recon-verified positions lib.rs:1890-1903 and
:486-535. Edge cases (0, -1, i64::MAX, ±Inf, NaN) deferred to
follow-up tidy iter if regression sweep does not surface need.
Pure runtime/str.c addition: str_alloc helper + ailang_int_to_str +
ailang_float_to_str. No codegen, checker, or build-pipeline changes
in this iter — the IR-side wiring (declare lines, lowering, checker
install, rc.c always-linked) all batch into hs.4. Acceptance gate:
runtime continues to build and link with the new symbols present but
unreferenced (clang -O2 dead-strips them until hs.4 wires a caller).
Two tasks: (1) append three functions + includes + extern decl to
str.c, verify via clang -c + nm; (2) full workspace + cross_lang +
compile_check + check sweep stays green.
Supersedes the defunct hs.2 plan at 8ca602d (runtime-sentinel +
Str-in-ADT drop safety). After the spec amendment at 2a72a4a dropped
the sentinel-rc-header story, the iter scope is now a surgical retrofit
of hs.1's emission: drop the leading i64 sentinel slot from static-Str
globals, land the IR-Str pointer on the (now first) len-field via
GEP (i32 0, i32 0). The four +8 consumer GEPs are invariant across the
switch. Three tasks: (1) update the two IR-shape tests to the new
layout (RED); (2) flip three format-string sites + two doc-comments
in lib.rs (GREEN); (3) regenerate hello.ll snapshot + workspace +
cross_lang + compile_check + check.
Second iter of the heap-Str ABI milestone. Adds the UINT64_MAX sentinel-header short-circuit to ailang_rc_inc / ailang_rc_dec so the hs.1 packed-struct globals (whose first slot is i64 -1) flow safely through generic RC paths. Tasks: (1) author rc_str_field_in_adt_static.ail.json + confirm builds; (2) TDD cycle for the safety test + runtime guards; (3) full regression sweep.
First iter of the heap-Str ABI milestone (docs/specs/2026-05-12-heap-str-abi.md). Carrier reversed by Boss judgement: original carrier said runtime+checker first, but the spec's no-intermediate-state clause forbids installing int_to_str without its codegen arm, and the runtime/str.c additions can't be tested in isolation. Layout migration is the natural prerequisite that everything else builds on.
Tasks: (1) intern_str_literal table + function + emission loop + Literal::Str callsite switch; (2-4) +8 GEPs at io/print_str, eq__Str, compare__Str callsites; (5) snapshot regen + cross_lang.py regression check. Acceptance: every existing program byte-identical.
Boss-direct plan (no parent spec — this is a P2-roadmap-todo, not a
milestone iter). Option 1 picked from the roadmap fork: auto-parse
.ail internally so `ail check foo.ail` works the same as
`ail check foo.ail.json` (modulo the surface-parse step). Option 2
(hint-only) was a workaround that would have forced LLM-authors to
prepend `ail parse` before every check/build/run.
Architecture: extension dispatch lives in ailang-surface (which
already depends on core); core gains a `load_workspace_with` injection
point so the dispatch reaches transitive imports; surface parse
errors get a new WorkspaceLoadError::SurfaceParse variant so
`workspace_error_to_diagnostic` can route them in --json mode.
Tasks:
1. core::load_workspace_with + WorkspaceLoadError::SurfaceParse
+ .ail-first import resolution
2. surface::load_module + surface::load_workspace
3. crates/ail/src/main.rs rewire (~17 callsites) + 2 e2e tests
4. workspace_error_to_diagnostic SurfaceParse arm + DESIGN.md
§Decision-6 CLI addendum + ct1_check_cli diagnostic test
Walks the implementer from a fresh harness/ Cargo project to a green
test sweep across strip_locations (5 unit + 5 integration on real
captured stderr), verify_references (4 reference solutions run
through the live ail+clang pipeline locally), mock_full_run (eight
canned-cohort-task pairs end-to-end), and budget_abort (tiny budget
exhaustion). Six modules: strip_locations, pipeline, ionos
(reqwest blocking + retry per spec §Error handling), mock, scoring,
tasks. Captures five real stderr samples (recon-collected) as
fixtures; strip_locations regex calibrated to the actual diagnostic
shape (handles `at byte N`, anyhow `Caused by:` chains).
Workspace-table and .gitignore included in Task 1 from the outset
(learned from cma.1 surfaced concerns; not repeated as
post-hoc journal items). Out-of-workspace pattern matches render/
verbatim. AIL_BIN env var with PATH fallback for `ail` resolution;
clang preflight check added since `ail build` shells to clang.
Two explicit deferrals to implementer named in the plan:
ParamMode names (lifted from cma.1) and prelude function symbols
(from crates/ailang-check/src/builtins.rs); both stable from cma.1
authoring, no re-derivation needed.
Walks the implementer from a fresh directory to a green test sweep
(splitter_unit, spec_completeness, example_roundtrip, token_balance)
plus two checked-in rendered/*.md files. Visitor borrowed verbatim
from schema_coverage; master/examples authored fresh as a curated 13-
fixture set (one per AST variant cluster); master/spec.md authored in
13 sections with form-only blocks balanced by the ±5% gate.
Open authoring deferrals to the implementer (named at the planner
level, intentional): exact ParamMode enum names and the canonical
prelude function names — both retrieved from current source by
reading ailang-core/src/ast.rs and the prelude module at authoring
time, rather than pinned in the plan against a possibly-stale recall.
Plan for the corrected iter 23.4: restructure mono.rs into a single
specialiser for every Type::Forall-quantified Def::Fn (class methods
+ polymorphic free fns) in one fixpoint pass, and remove the codegen-
time specialiser (lower_polymorphic_call + module_polymorphic_fns +
mono_queue) entirely.
11 tasks, 4-step TDD template per task (write-failing-test →
verify-fail → write-impl → verify-pass), no per-task commit step
under the new disc.1 discipline.
Parent spec: docs/specs/2026-05-11-23-eq-ord-prelude.md (re-grounded
2026-05-11 against main HEAD 51da9fa, PASS, 10 load-bearing
assumptions verified).
Open commitments resolved:
- N-ary mono-symbol naming: spec default <name>__<t1>__<t2>__...
- Target-collector: extension of existing collect_mono_targets
(lockstep with collect_residuals_ordered)
- Original poly Def::Fn retention: kept parallel to Def::Class/
Def::Instance
Float-NoInstance diagnostic wording remains deferred to iter 23.5.
The 23.4 mono-unification plan (ea0285b) assumed prep1 + prep2 +
prep3 were on main. They never were — they sit only on iter/23.4.
Spec component table was factually wrong. Plan Task 10 (revert prep
commits) was impossible on main. Discarding the plan; the spec is
next revised against the actual main state.