iter embedding-abi-m1.1 (DONE 7/7): Embedding-ABI M1 — scalar AILang fn callable from C/Rust

Tasks 4-7 on the Boss-Repaired plan, completing the M1 iteration:

- Task 4: check-side export-signature gate — CheckError variants
  ExportNonScalarSignature / ExportHasEffects (codes
  export-non-scalar-signature / export-has-effects), code() arms,
  check_fn gate (params->ret->effects, first-violation-wins,
  unconditional on f.export.is_some()). The clause-3 discriminator
  in code: effectful/non-scalar exports fail to typecheck. 6/6.
- Task 5: codegen Target::StaticLib — Target enum, threaded param,
  @main/MissingEntryMain gated behind Target::Executable, one
  external @<sym> forwarder per export to @ail_<module>_<fn>
  (fn_scalar_sig/llvm_scalar). In-source missing_entry_main_is_error
  byte-unchanged. Lowering pin 2/2.
- Task 6: CLI ail build --emit=staticlib — clap field, Cmd::Build
  branch, build_staticlib (verbatim build_to prefix + has_export
  guard + two-archive clang -c/ar rcs tail) + run_cmd. CLI pin 2/2.
- Task 7: C-host E2E coherent-stop proof (cc links
  libembed_backtest_step.a + libailang_rt.a, backtest_step(0,3)=9 /
  (9,4)=25, s==25, exit 0) + DESIGN.md §"Embedding ABI (M1)"
  (scalar C ABI, provisional until M3). E2E pin 1/1.

Independent Boss verification: E2E 1/1, gate 6/6, full workspace
0 failures, roundtrip_cli + design_schema_drift + spec_drift green,
Invariant 1 holds (no new dep in core/codegen/runtime). 3 behaviour-
neutral plan pseudo-vs-reality concerns in the journal. Default-exe
codegen/runtime path byte-unchanged (check.py firing = tracked-P2
known-noise, audit-adjudicated). INDEX line added.

Completes the M1 iteration (impl). Milestone-close audit + fieldtest
(surface-touching) are the next pipeline steps.
This commit is contained in:
2026-05-18 14:50:27 +02:00
parent 818177d835
commit e406d07d81
17 changed files with 804 additions and 81 deletions
+1
View File
@@ -93,3 +93,4 @@
- 2026-05-18 — fieldtest remove-mut-var-assign (milestone CLOSE, clean — removal thesis empirically confirmed): post-audit downstream-LLM-author field test of the post-removal Form-A surface. The fieldtester (DESIGN.md + form_a.md + public examples only, never compiler source) wrote 4 FRESH real-world programs an imperative-trained author would instinctively reach for a mutable local for — running sum-of-squares accumulator (`loop`/`recur`, 2 binders → `385`), grade-classification cascade (let-threaded flag through 4 `if``4`/`2`/`0`), Horner polynomial straight-line build-up (5 `let acc` shadows → `73`), multi-state bracket-balance scanner threading `(rest,depth,ok)` by tail recursion → `true`/`false`) plus an out-of-tree `mut`-keyword rejection probe. **0 bugs, 0 friction, 0 spec_gap, 4 working**: every task expressed cleanly and correctly on the FIRST try with only `let`/`if`/`loop`/`recur`; all 4 `ail parse|render|parse` byte-identical. The closest-to-friction case (the multi-state machine must restate the full state tuple at every tail-call even when a branch changes one component) was carefully classified `working` not `spec_gap` — that explicit-dataflow cost IS the local-reasoning pillar working as designed, and `mut`'s implicit cross-iteration persistence of un-restated state is precisely the clause-3 failure the removal eliminates; the fieldtester explicitly noted the only conceivable sugar (a record to bundle state) is an orthogonal additive future question, NOT evidence against the removal (refused the scope-creep). The `mut` rejection is fail-closed with an actively-guiding diagnostic that enumerates the surviving term heads incl. `loop`/`recur`/`let` (no tombstone, no-nostalgia, as spec'd). Independent Boss verification: all 4 fixtures re-run → `385`/`4 2 0`/`73`/`true false`, zero `mut`/`var`/`assign` construct tokens in any fixture (a doc-faithful author did not produce the removed construct — the affirmative clause-1 evidence). **Verdict: the removal thesis is confirmed**`mut` was redundant with `let`/`if`/`loop`/`recur` in 100% of the fresh real tasks; no expressivity lost. **The remove-mut-var-assign milestone is fully ratified and CLOSED**: spec+plan+iter, audit clean (architect `[high]` form_a.md fixed in `.tidy`, bench causally exonerated), fieldtest clean on the removal axis. Fixtures `examples/fieldtest/remove-mut_*.ail` + spec `docs/specs/2026-05-18-fieldtest-remove-mut-var-assign.md`; roadmap P0 flipped `[~]``[x]`, WhatsNew.md user-facing entry appended; next queue item (DESIGN.md/docs honesty-lint) is a NEW milestone = a /boss new-milestone bounce-back (not auto-started) → 2026-05-18-fieldtest-remove-mut-var-assign.md
- 2026-05-18 — iter docs-honesty-lint.1 (single-iteration milestone, DONE): canonical docs made present-tense-honest. Stripped Wunschdenken (forward intent stated as fact) + non-citation post-mortem/doc-archaeology from `docs/DESIGN.md` (14 corrections: L484 iter-anchor, L503/L1318/L1504/L2027/L2065/L2135 post-mortem, L650 malformed-Wunschdenken, L655 dated-heading, L1513/L1837/L2046/L2519 Wunschdenken, + the Task-4-Step-14 procedural-catch-all FIX at the Form-B prose-projection bullet "was deferred from milestone 22 entirely … A future iter ships" — a soft-wrapped site the spec's illustrative regex would have missed, the procedural enumeration working exactly as designed) + `docs/PROSE_ROUNDTRIP.md` (tool-use/MCP paragraph); genuine forward intent (LLM tool-use/MCP/LSP) relocated to roadmap P3. Codified the two-pronged tense+modality discriminator as a present-tense `### What this document is — and the honesty rule it holds itself to` meta-subsection in DESIGN.md §"Project ecosystem". Anti-regrowth two ways: 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; resolves spec Testing-item 0 + planner self-review item 6 at once) + wrap-robust advisory Sweep 5 in `bench/architect_sweeps.sh` (contiguous-fragment regex, since DESIGN.md is hard-wrapped ~70col) with full sweep-count lockstep (header + "All four"→"All five" tail + `ailang-architect.md` "four"→"five sweeps") + a new `ailang-architect.md` "DESIGN.md honesty drift" bullet citing the meta-subsection. Protected-exception KEEPs preserved + pinned-present (Diverge-reserved, regions-considered-and-rejected, the self-labelled "tiebreaker, not a rationale"); `form_a.md` correctly left untouched/unpinned. One plan-internal DONE_WITH_CONCERNS (Task-1 pin string omitted a `**…**` bold the Task-3 body added; resolved by dropping the non-load-bearing emphasis, pin is the contract) — planner self-review-item-3 calibration note. Independent Boss verification: pin 4/4 GREEN, full `cargo test --workspace` 0 failures (delta = +4 pin tests only), sentinel trio (`design_schema_drift`/`spec_drift`/`schema_coverage`) green, `cargo build --workspace` clean, scope = ONLY THE PIN in `crates/` (zero language/checker/codegen/runtime change, `ail check`/`run`/`build` byte-unchanged by construction). Post-iter sweep residue = only the 3 documented deliberate KEEPs (Sweep-1 L50 doc-roles-pointer date + L2060 true `(iter str-concat)` provenance; Sweep-5 L62 self-referential discriminator-catalogue), EXIT=1 architect-adjudicated-expected. Milestone-close `audit` is the next pipeline step (no fieldtest — zero authoring-surface change). spec `docs/specs/2026-05-18-docs-honesty-lint.md` (grounding-check PASS 10/10), plan `docs/plans/docs-honesty-lint.1.md` → 2026-05-18-iter-docs-honesty-lint.1.md
- 2026-05-18 — audit docs-honesty-lint (milestone close, CLEAN — carry-on, NO ratify): scope `928e1c0..e50b400`. Architect `clean`: scope claim exact (zero `crates/**/src/**` + `runtime/`; sole crates/ change the additive RED-first-now-GREEN `docs_honesty_pin.rs`; lockstep pairs untouched), all 14 DESIGN.md corrections genuinely present-tense with the 4 protected KEEPs (`Diverge`-reserved/`Regions`-rejected/`tiebreaker`/`No deriving`) un-stripped, agent-def "four→five" lockstep complete, sweep residue exactly the 3 documented deliberate KEEPs. One `[medium]` standing advisory wart — Sweep 5's regex self-matches the discriminator subsection's own forbidden-phrasing catalogue (DESIGN.md L62) forever (architect-flagged; orchestrator-adjudicated → filed as a P3 todo, NOT a fix iteration — not a new failure mode since the script is advisory-with-residue by design). Bench: `compile_check.py` 0/24 + `cross_lang.py` 0/25 EXIT 0 (cross_lang independently reads the same `bench_list_sum` bump path +5.95% within tol); `check.py` EXIT 1, 3 firings (`bench_list_sum.bump_s` +13.42%, `latency.implicit_at_rc.{p99_9,max}_us` +37.65%/+134.70%). Bencher causal exoneration **decisive**: HEAD vs `5bb7211` vs `de66eb7` — all 5 firing bench binaries `cmp`-byte-identical, `ail` sha256 identical at all three commits ⇒ zero causal mechanism; `*.max_us` = tracked-P2 `-n 5` tail-jitter (4th occurrence on a zero-runtime-change milestone, median/p99 rock-steady), `*.bump_s` = tracked-P2 environmental-anchor-staleness (persistent ~+15%, stale 0.046s anchor). **EXONERATED, NO baseline ratify** (Iron Law forbids ratifying noise; spec forecloses a zero-codegen baseline bump). Two pre-existing standing P2 items kept orchestrator-owned, explicitly NOT bundled into this close (bencher recommendation honoured). Milestone audit **CLEAN**; no fieldtest (zero authoring-surface change — the wrap-robust pin + Sweep 5 ARE the regression coverage). **docs-honesty-lint fully ratified and CLOSED**: spec (grounding-check PASS 10/10) + plan + single iter + audit clean; roadmap P0 flipped `[~]``[x]`, WhatsNew.md user-facing entry appended → 2026-05-18-audit-docs-honesty-lint.md
- 2026-05-18 — iter embedding-abi-m1.1 (Embedding-ABI M1, DONE across 2 dispatches + a Boss plan Repair): a compiled AILang scalar `fn` is now callable in-process from a C/Rust host. Five pipeline layers shipped: additive `FnDef.export: Option<String>` (serde `default`+`skip_serializing_if`, modelled on `FnDef.doc`, ~85-site compile-driven thread incl. all in-source `#[cfg(test)]`/drift/hash literals, hash-stable golden `b4662aa70839f60b`); Form-A `(export "<sym>")` modifier (parse/print/`form_a.md` lockstep, byte-identical round-trip); check-side scalar-only+effect-free export gate — two new `CheckError` Error variants `export-non-scalar-signature`/`export-has-effects`, params→ret→effects first-violation-wins, unconditional on `f.export.is_some()` (the feature-acceptance clause-3 discriminator *in code*); codegen `Target::StaticLib` (suppresses `@main`+`MissingEntryMain`, emits one external `@<sym>` forwarder per export to `@ail_<module>_<fn>`); CLI `ail build --emit=staticlib` (`build_staticlib`: `libembed_backtest_step.a` program-only + separate `libailang_rt.a`). Coherent stop PROVEN: `embed_e2e.rs` `cc`-links both archives, `backtest_step(0,3)=9`/`(9,4)=25`, `s==25`, exit 0. Task 1 (fixed-first CLI `MissingEntryMain` baseline pin) green before any staticlib code. Process: Task 4 BLOCKED on the first dispatch — a Boss plan-defect (gate/headline fixtures declared `module ≠ file-stem`; the loader hard-enforces `module==stem` at `workspace.rs:438`, so `ail check` panicked on load before the gate). Boss verified, chose **Option 2 over the orchestrator-recommended Option 1** (keep descriptive `embed_*` filenames, rename fixture *modules* to their stems — the C symbol `backtest_step` stays via `(export …)`, *demonstrating* spec Decision 2's mangling-decoupling rather than violating it; archive→`libembed_backtest_step.a`, internal→`@ail_embed_backtest_step_step`, `host.c` unchanged), Repaired the plan (Design-decision 6 + Task-4 module==stem + Task-5/6/7 dependent strings + folded the no-`*.`-Float-head and `content_hash_fn``def_hash` concerns), committed Tasks 13 as a verified known-good partial (`818177d`), re-dispatched [4,7]. Independent Boss verification post-completion: E2E 1/1, gate 6/6, lowering 2/2, CLI 2/2, full `cargo test --workspace` 0 failures, `roundtrip_cli`+`design_schema_drift`+`spec_drift` green, Invariant 1 holds (no new dep in `ailang-core`/`ailang-codegen`/`runtime`). 3 behaviour-neutral plan pseudo-vs-reality concerns recorded (`emit_ir` routes via `lower_workspace`; dead `Path` import dropped; `##` vs `###` heading). Bench: `compile_check.py`/`cross_lang.py` exit 0; `check.py` exit 1 = tracked-P2 `*.bump_s`/`*.max_us` known-noise (default-exe codegen/runtime byte-unchanged — audit-adjudicated, not a plan gate). spec `docs/specs/2026-05-18-embedding-abi-m1.md` (grounding-check PASS) → plan `docs/plans/embedding-abi-m1.1.md` (Repaired) → commits `818177d` (partial 13) + this completion. Milestone-close `audit` then `fieldtest` (surface-touching — `(export …)` is new authoring surface) are the next pipeline steps. → 2026-05-18-iter-embedding-abi-m1.1.md