Files
AILang/docs/journals/2026-05-18-iter-embedding-abi-m1.1.md
T
Brummel 818177d835 iter embedding-abi-m1.1 (PARTIAL 3/7): schema + surface + baseline prerequisites; plan Repair
Tasks 1-3 of docs/plans/embedding-abi-m1.1.md, a verified known-good
subset (cargo build --workspace exit 0; full workspace green; the
roundtrip_cli all-examples gate green):

- Task 1: CLI build-path MissingEntryMain baseline pin
  (examples/embed_noentry_baseline.ail + embed_missing_main_baseline.rs;
  triple-assertion, layered on the green codegen-unit pin lib.rs:3314).
- Task 2: additive FnDef.export: Option<String> (serde default +
  skip_serializing_if, modelled on FnDef.doc); compile-driven thread
  of export: None across ~85 FnDef/AstFnDef literals / 18 files incl.
  all in-source #[cfg(test)] mod tests + drift/hash/spec-drift
  literals + the codegen lib.rs:3320 in-source pin; hash-stability
  golden pin; DESIGN.md fn-JSON "export" line. DONE_WITH_CONCERNS
  (plan symbol content_hash_fn -> def_hash per hash_pin.rs, plan-
  pseudo-vs-reality class, plan corrected).
- Task 3: Form-A (export "<sym>") modifier — parse_export +
  parse_fn thread + write_fn_def emission + form_a.md grammar;
  byte-identical round-trip.

Task 4 BLOCKED (Boss plan-defect: fixtures declared module != file
stem; AILang's loader hard-enforces module==stem at workspace.rs:438,
so ail check panicked on load before the gate). Boss resolution
(Option 2, overriding the orchestrator's Option 1, rationale in the
journal + plan Design-decision 6): 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 becomes
libembed_backtest_step.a, internal symbol @ail_embed_backtest_step_step,
host.c unchanged. Plan fixed (Design-decision 6 + Task-4 module==stem
+ Task-5/6/7 dependent strings + the no-*. Float-head + content_hash_fn
concerns folded). Headline fixture corrected; blocked-Task-4 WIP
discarded (recreated by the [4,7] re-dispatch from the fixed plan).

PARTIAL commit (not the iter's final commit): the implement Repair
path mandates committing the known-good subset so the [4,7]
re-dispatch starts from a clean tree that includes Tasks 1-3 (Tasks
4-7 structurally depend on the schema field + surface). INDEX line
added when the full iter lands post-re-dispatch.
2026-05-18 14:32:39 +02:00

16 KiB
Raw Blame History

iter embedding-abi-m1.1 — Embedding ABI M1: linkable artifact + scalar C entrypoint

Date: 2026-05-18 Started from: b0bd7efeaf Status: PARTIAL — Tasks 13 committed as a known-good subset (see "Boss resolution" below); plan defect fixed; Tasks 47 re-dispatched on the corrected plan. Tasks completed: 3 of 7 (Tasks 13 DONE + committed; Task 4 BLOCKED on a Boss plan-defect — fixture module≠stem — before any gate code was written; resolved by Boss + re-dispatched; Tasks 57 follow in the re-dispatch)

Summary

Tasks 13 of the Embedding-ABI M1 plan landed cleanly: the CLI build-path MissingEntryMain baseline pin (Task 1), the additive FnDef.export: Option<String> schema field threaded across the whole workspace via compile-driven enumeration with a hash-stability golden pin (Task 2), and the Form-A (export "<sym>") surface modifier with full parse/print/grammar lockstep and a byte-identical round-trip (Task 3). Task 4 (the check-side export-signature gate) is BLOCKED before any production code: the plan's verbatim gate-pin test (crates/ailang-check/tests/embed_export_gate.rs, using ailang_surface::load_workspace) is mutually unsatisfiable with the plan's verbatim fixtures, because load_workspace hard-enforces module-name == file-stem (crates/ailang-core/src/workspace.rs:182 ModuleNameMismatch) while the fixtures (spec-mandated module backtest in file embed_backtest_step.ail; bad/bad_io/… in embed_export_*_rejected.ail) deliberately diverge module name from filename. This is not an implementer-judgement cross-check (the six named ones were executed where reached) — it is a structural contradiction that ripples across Tasks 47 and can only be resolved by a Boss/spec-level decision. Per the Iron Law (unclear spec ambiguity → BLOCKED, no implementing on a hunch), the iter stopped. Tasks 13 sit clean and green in the working tree; only the deliberately-RED Task 4 pin fails workspace-wide.

Per-task notes

  • iter embedding-abi-m1.1.1: CLI build-path MissingEntryMain baseline pin. Created examples/embed_noentry_baseline.ail (main-free module) + crates/ail/tests/embed_missing_main_baseline.rs (triple-assertion: non-zero exit + verbatim has no \main` defDisplay substring [codegen lib.rs:129] + absent binary). GREEN on write; not-a-no-op confirmed;roundtrip_cli` green with the new fixture. DONE.
  • iter embedding-abi-m1.1.2: additive FnDef.export schema field + workspace-wide threading. Added the field to FnDef after doc (verbatim plan doc-comment, #[serde(default, skip_serializing_if = "Option::is_none")]). Compile-driven enumeration via cargo build --workspace --all-targets --keep-going over two dependency-ordered waves threaded export: None into ~85 FnDef/AstFnDef literals across 18 files incl. all named blind-spot sites (codegen lib.rs:3320 missing_entry_main_is_error, design_schema_drift.rs:278, spec_drift.rs:256, hash_pin.rs, the in-source #[cfg(test)] mod tests literals, the mono.rs AstFnDef alias, the parse.rs/print.rs literals). cargo build --workspace exits 0 (planner self-review item 7 gate met). DESIGN.md fn-JSON "export" line + drift test green (Recon Q2 confirmed: no nested-struct-key anchor forced). Hash pin green (golden b4662aa70839f60b). Full workspace test gate: 611/0. DONE_WITH_CONCERNS.
  • iter embedding-abi-m1.1.3: Form-A (export "<sym>") surface modifier. parse_export helper (faithful parse_doc mirror); parse_fn threaded (let mut export, Some("export") arm with duplicate-clause guard, unknown-attr string updated, Ok(FnDef{…}) uses parsed export); write_fn_def export emission block (doc → export → suppress → type order); form_a.md production + (export STRING)? + prose paragraph. RED verified (unknown fn attribute \export``), then GREEN: round-trip gate byte-identical, surface crate 72/0, ailang-core 111/0. DONE.
  • iter embedding-abi-m1.1.4: check-side export-signature gate. Wrote the five plan fixtures + the verbatim gate pin; ran it → RED for the WRONG reason (a ModuleNameMismatch load failure, not the absence of the gate). BLOCKED — see Blocked detail. No CheckError variant, no code() arm, no check_fn gate written.

Concerns

  • iter embedding-abi-m1.1.2: the plan's RED-pin symbol ailang_core::hash::content_hash_fn(&FnDef) does not exist. The real entry point is def_hash(&Def) -> String (the one crates/ailang-core/tests/hash_pin.rs uses). Resolved by mirroring hash_pin.rs exactly (wrap the FnDef in Def::Fn(...), call def_hash) — this is explicitly authorised by the plan's own Step-1 NOTE ("the implementer mirrors the exact hashing entry point that hash_pin.rs uses if the name differs"). Property and shape unchanged; recorded because the plan's literal symbol was wrong (planner pseudo-vs-reality class — the feedback_specs_need_concrete_code / feedback_plan_pseudo_vs_reality family).
  • iter embedding-abi-m1.1.4: the plan fixture embed_export_float_ok.ail uses (app *. a b). There is no *. Float-multiply head in AILang — Float arithmetic uses the same typeclass-generic head as Int (examples/floats.ail / examples/mut_sum_floats.ail both use (app + …); codegen lowers to fadd/fmul double by type). Per the plan NOTE ("if the Float-multiply surface differs, use the same head that example uses"), the fixture as written uses (app * a b). The fixture's role ("scalar+pure Float export passes the gate") is preserved. Recorded; not yet exercised (Task 4 BLOCKED before the gate ran).
  • INFRA (not iter-authored): docs/roadmap.md became dirty in the working tree during this run (a ~98-line P2 "DESIGN.md → design/ role-split" milestone proposal, mtime mid-session). Phase 0's git status --porcelain was empty (clean start). Nothing in this iter writes roadmap.md; it was modified by an external/concurrent writer. It is path-disjoint from every iter file. Left untouched (roadmap.md is Boss-owned per CLAUDE.md; main HEAD sacrosanct). The Boss must decide independently whether to keep or discard it; it is NOT part of this iter's commit.

Known debt

  • Tasks 5 (codegen Target::StaticLib), 6 (CLI --emit=staticlib), 7 (E2E host harness + DESIGN.md §"Embedding ABI (M1)") not reached — they depend on Task 4's gate (Task 5's forwarder loop trusts the gate; Task 6/7 build on the headline fixture). Not touched because the iter is BLOCKED upstream of them; no speculative partial work.
  • E2E coverage (Phase 3) not written — the iter did not reach completion; the Task 7 host-harness is the milestone's coherent- stop proof and is downstream of the block.

Blocked detail

Task: 4 (check-side export-signature gate) Reason: spec-ambiguous (a spec-internal contradiction that rippling-binds Tasks 47, surfaced as unclear per the per-task sub-status table → BLOCKED to Boss).

Worker says: The plan's Task-4 gate pin (crates/ailang-check/tests/embed_export_gate.rs) is verbatim and correct Rust; it calls ailang_surface::load_workspace(&path) on each fixture (the exact call shape crates/ail/src/main.rs:580581 uses). load_workspaceailang_core::workspace::load_modules_withmodule_name_from_path (workspace.rs:1566) hard-enforces in-file module name == file stem, returning WorkspaceLoadError::ModuleNameMismatch (workspace.rs:182) otherwise. The plan's verbatim fixtures deliberately violate this:

  • examples/embed_backtest_step.ail has (module backtest …) — spec-mandated, because the entry module name backtest is what produces libbacktest.a (Task 6), @ail_backtest_step and the exported C symbol backtest_step (Tasks 5/7, the spec headline host host.c declares extern int64_t backtest_step(...)). Stem is embed_backtest_stepModuleNameMismatch.
  • embed_export_str_param_rejected.ail → module bad_str_param; embed_export_io_rejected.ailbad_io; embed_export_effectful_rejected.ailbad; embed_export_adt_ret_rejected.ailbad_adt_ret; embed_export_float_ok.ailembed_float_ok. All stem-mismatched ⇒ every check_codes() call panics on load before the gate is ever consulted.

These three plan facts cannot all hold simultaneously: (a) fixture files named examples/embed_* (every Task 4/5/6/7 path cites these exact names), (b) the spec-headline module/C-symbol naming (backtestlibbacktest.a / backtest_step), (c) a load_workspace-based check-side gate pin. roundtrip_cli (Task 1/3) sidesteps this only because it drives ail parse (single-file, no workspace stem-match). The contradiction is therefore invisible until the first load_workspace-based fixture test — exactly Task 4.

This is not one of the six named implementer-judgement cross-checks (Float *. head, public-symbol confirmation, Type::Con field set, declared_effs element type, run_cmd pre-existence, llvm_scalar callee-ABI). It is a structural spec/plan defect requiring a Boss/spec decision among at least:

  1. Rename each fixture file to its module stem (examples/backtest.ail, examples/bad_str_param.ail, …) and update every Task 4/5/6/7 path citation accordingly (preserves the spec headline module/C-symbol naming; ripples ~12 plan path references + the Task-6/7 libbacktest.a math still holds since module stays backtest).
  2. Rename each fixture module to its filename stem ((module embed_backtest_step …), …) and re-derive the spec's C-symbol / lib<entry>.a story from embed_backtest_step (changes the spec headline; libembed_backtest_step.a, @ail_embed_backtest_step_step — almost certainly not what the spec wants).
  3. Change the Task-4 gate pin to a loader that does not stem-enforce (e.g. parse the single file directly), which diverges from the plan's verbatim load_workspace-based test and from how ail check actually loads (main.rs:580).

Option 1 is the most spec-faithful (it keeps the headline module backtest and only moves files), but it is a cross-task plan edit (≥12 path citations) and therefore the planner's / Boss's call, not an inline implementer substitution. I did NOT silently pick one (reverted my own speculative fixture-module renames so the Boss sees the contradiction unobscured); no gate production code was written.

Suggested next step: Boss adopts Option 1 — re-issue the plan for Tasks 47 with fixture files renamed to their module stems (examples/backtest.ail etc.) and the ~12 path citations updated; keep module backtest so the spec's libbacktest.a / backtest_step headline is preserved — then re-dispatch this iter with task_range: [4, 7] (Tasks 13 are clean and can be committed now or carried).

Boss resolution (2026-05-18)

The BLOCK was verified accurate against source: crates/ailang-core/src/workspace.rs:438 (entry_module.name != module_name_from_path(entry_path)ModuleNameMismatch, no exception) — module name MUST equal file stem. The defect is a Boss plan-write error: the plan's headline + gate fixtures declared (module backtest) / (module bad) / … in embed_*.ail files, malformed under that invariant. The orchestrator correctly stopped at the Iron Law instead of guessing. Recon maps file structure, not fixture-content invariants; the Boss source- verification focused on code-edit anchors, not cross-checking each fixture's module name against its filename against the loader — this is the feedback_specs_need_concrete_code / feedback_plan_pseudo_vs_reality family (the fixture as written would not load), now also a fixture-well-formedness lesson.

Decision: Option 2, overriding the orchestrator-recommended Option 1. (Agent recommendations are not directives — own judgement formed first.) Keep the descriptive embed_* filenames; rename the fixture modules to their file stems (embed_backtest_step, embed_export_str_param_rejected, …). Rationale from semantics, not effort:

  • The spec's load-bearing contract (Decision 2, spec lines 5259 / 167169) is that the exported C symbol is author-chosen and decoupled from ail_<module> mangling(export "backtest_step") is the contract; host.c asserts on backtest_step, which is invariant under module rename. So spec-faithfulness does not discriminate between the options; libbacktest.a / @ail_backtest_step are illustrative (build-output filename + internal mangling — the latter is exactly what the spec decouples from), not frozen-ABI contracts. The orchestrator's "Option 1 most spec-faithful" over-weighted the illustrative libbacktest.a string.
  • examples/ is a flat shared corpus auto-enrolled in whole-corpus tests (roundtrip_cli); Option 1's examples/bad.ail / backtest.ail / bad_io.ail pollute it with collision-prone, non-self-describing names. Option 2 keeps the self-describing grouped embed_* set — a real maintainability argument in a ~100-file flat dir read by future LLM authors.
  • Option 2 (module = stem, C symbol stays backtest_step via the (export …) string) is a concrete demonstration of the spec's own Decision-2 decoupling thesis — the first fixture proving module-name ≠ C-symbol — not a violation of it. The spec's (module backtest) / (module bad) blocks are shape- illustrative (planner contract: spec owns shape, not bytes).

Consequence: entry module embed_backtest_step ⇒ archive libembed_backtest_step.a, internal symbol @ail_embed_backtest_step_step; the external C symbol backtest_step and host.c are UNCHANGED (the actual coherent- stop contract). Architect-at-close note recorded in the plan (Design-decision 6): a fixture module name differing from the spec's illustrative block is expected under module==stem + Decision 2, not drift.

Actions taken: (1) plan docs/plans/embedding-abi-m1.1.md fixed — Design-decision 6 added, Task-4 fixtures → module==stem, Task-5/6/7 dependent strings updated (libembed_backtest_step.a, @ail_embed_backtest_step_step; C symbol @backtest_step unchanged), the two iter-1 concerns folded (no *. Float head → (app * a b); content_hash_fndef_hash(&Def)); (2) the already-created headline examples/embed_backtest_step.ail corrected to (module embed_backtest_step); (3) the 5 blocked- Task-4 fixtures + the RED embed_export_gate.rs discarded (untracked WIP — recreated correctly by the re-dispatch from the fixed plan); (4) Tasks 13 committed as a verified known-good partial subset (cargo build --workspace exit 0, full workspace green with zero FAILED after the WIP discard, roundtrip_cli green); (5) orchestrator re-dispatched with task_range: [4, 7] on the clean tree + corrected plan. The user's path-disjoint roadmap addition (P2 "DESIGN.md → design/ role-split") was committed separately as Boss roadmap maintenance, not bundled into this iter.

Files touched

Schema / canonical docs:

  • crates/ailang-core/src/ast.rs (FnDef.export field)
  • docs/DESIGN.md (fn-JSON "export" line)
  • crates/ailang-core/specs/form_a.md (grammar production + prose)

Workspace-wide compile-gate threading (~85 export: None, literals):

  • crates/ailang-core/src/desugar.rs, pretty.rs
  • crates/ailang-core/tests/{design_schema_drift,hash_pin,spec_drift}.rs
  • crates/ailang-check/src/{lib,lift,linearity,mono,reuse_shape,suppress_filter,uniqueness}.rs
  • crates/ailang-check/tests/workspace.rs
  • crates/ailang-codegen/src/lib.rs
  • crates/ailang-prose/src/lib.rs

Surface modifier (Task 3):

  • crates/ailang-surface/src/parse.rs (parse_export + parse_fn thread)
  • crates/ailang-surface/src/print.rs (write_fn_def export block)

New files (untracked):

  • examples/embed_noentry_baseline.ail
  • examples/embed_backtest_step.ail
  • examples/embed_export_str_param_rejected.ail
  • examples/embed_export_adt_ret_rejected.ail
  • examples/embed_export_io_rejected.ail
  • examples/embed_export_effectful_rejected.ail
  • examples/embed_export_float_ok.ail
  • crates/ail/tests/embed_missing_main_baseline.rs
  • crates/ailang-core/tests/embed_export_hash_stable.rs
  • crates/ailang-check/tests/embed_export_gate.rs (deliberately RED — blocks on the spec contradiction; do NOT commit as-is)

NOT iter-authored, present in tree, Boss-decides separately:

  • docs/roadmap.md (external mid-session modification; path-disjoint)

Stats

bench/orchestrator-stats/2026-05-18-iter-embedding-abi-m1.1.json