roadmap: retire Embedding ABI — M4 (never speced) — speculative infra by feature-acceptance clause 2

M4 (sequence crossing via the List ADT) was struck during its own
/boss brainstorm, in the Step-2/3 Q&A, before any spec — the
"problem mis-framed -> don't ratify a known-unneeded shape into the
roadmap" brainstorm path. No spec, no grounding-check, no planner
handoff.

Why: held against DESIGN.md feature-acceptance criterion clause 2,
M4 removes no redundancy. The shipped M3 export gate is_c_abi_type
(crates/ailang-check/src/lib.rs:1934-1953) is a per-parameter loop
accepting a C scalar OR a single-ctor all-scalar record
independently per param; the forwarder's llvm_scalar maps every
non-scalar Type::Con to ptr (M3-frozen). So (State, Tick) -> State
with both as single-ctor all-scalar records is already
gate-accepted and forwarder-supported today. The minimal
data-server binding is M3 (shipped) + a host-side per-tick loop;
cons-list crossing would add a 2N+1-box-per-chunk host builder and
the deferred flat-array perf debt, with no named consumer (M5's
adapter unrolls each chunk host-side; whole-chunk in-kernel
visibility is semantically void — State threads across calls
regardless of chunk boundaries).

Outcome: M4 retired in docs/roadmap.md (struck entry kept one
cycle for context, never [x] — never shipped); M5 reconciled
(depends on M4 -> M3 + the Tick-coverage todo; adapter unrolls
host-side; friction feeds the host-per-tick-FFI vs. batch-crossing
P2 perf decision); residual recorded as a new [todo] Tick-coverage
on M3 — an E2E + fixture pinning the two-record-param per-tick
(State, Tick) -> State shape (test backfill, no brainstorm).

Rationale + the honest mid-Q&A correction (I had wrongly asserted
M5 needs M4) in
docs/journals/2026-05-18-brainstorm-embedding-abi-m4-retired.md;
INDEX.md mirrors it.
This commit is contained in:
2026-05-18 23:22:39 +02:00
parent c75fb805a6
commit 38150ae923
3 changed files with 161 additions and 19 deletions
@@ -0,0 +1,122 @@
# 2026-05-18 — brainstorm embedding-abi-m4 → RETIRED (never speced)
`/boss` picked the top P0 item "Embedding ABI — M4: sequence
crossing via the existing `List` ADT" (a new milestone, no spec).
User green-lit a continue-here brainstorm. The brainstorm
terminated **without a spec** at the Step-2/3 Q&A: the milestone's
premise collapsed under its own feature-acceptance gate. This file
records why, so the retired entry is not silently erased.
## What was explored before the collapse
Plan-recon (`ailang-plan-recon`) returned a full fact sheet. Two
user forks were resolved in Q&A and are recorded here as context
(now moot, but they shaped the analysis):
- **own-only** for the `List` parameter — kernel consumes each
chunk; its existing internal recursive drop frees it; no
host-side recursive-free symbol (M3 explicitly deferred that as
the build-ahead trap it refused). Keeps M4 "no language change".
- **`List Record` only** — element an M3-shaped single-ctor
all-scalar record crossing as ptr-to-M3-box, one uniform
cons-cell layout; sidesteps the poly/mono inline-scalar-element
divergence the recon flagged (open question #3).
Approach **A (structural recognition)** — extend `is_c_abi_type`
with a recursive list-shaped arm (2 ctors: one nullary + one
`(C-ABI, recursive-self)`), no name match, consistent with M3's
structural single-ctor rule and AILang's content-addressing
thesis — was the recommendation. B (name-anchored) rejected on
language grounds (name fragility; no canonical `List` — 8
divergent decls, no `std/`); C (land a `std_list` SSOT first)
rejected on scope grounds (a separate milestone; breaks the
established self-contained embed-fixture discipline).
## Why M4 was struck (the substantive reason — feature-acceptance clause 2)
The user asked "what is M4 even *for*?" then "but the host has to
build the list first, no?" then narrowed: a *minimal* data-server
binding would be satisfied by the host pushing each tick
individually — no list at all.
Held against `docs/DESIGN.md` §"Feature-acceptance criterion":
- **Clause 2 (improves correctness / removes redundancy) — FAILS.**
The shipped M3 export gate `is_c_abi_type`
(`crates/ailang-check/src/lib.rs:1934-1953`) runs as a **per-
parameter loop** (`for p in &param_tys { if !is_c_abi_type(p,
&env) … }`) accepting, *independently per param*, a C scalar OR
a single-ctor all-scalar record. The forwarder's `llvm_scalar`
maps every non-scalar `Type::Con``ptr` and passes it
straight through (M3-frozen). Therefore `(State, Tick) -> State`
with both as single-ctor all-scalar records is **already
gate-accepted and forwarder-supported today** — the minimal
data-server binding is M3 (shipped) + a host-side per-tick loop
over each chunk's records. Cons-list crossing would *add* a
2N+1-box-per-chunk host builder and pull in the deferred
flat-array perf debt (the cons-list IS the P2-deferred perf
problem), removing no redundancy.
- **No named consumer.** M5's adapter (the sole data-server↔AILang
meeting point, Invariant 1) can wire `while let next_chunk` by
unrolling each chunk host-side into per-tick calls — a clean
adapter, not a layering violation; the adapter is exactly where
host data shape meets the kernel. Whole-chunk in-kernel
visibility buys nothing: `State` threads across calls regardless
of chunk boundaries, so a windowed indicator behaves identically
whether ticks arrive one-per-call or chunk-per-call; chunk
boundaries are a host artefact, semantically invisible to the
fold.
M4 (cons-list crossing) is therefore **speculative infra by
AILang's own criterion**: it builds something no named consumer
needs and adds cost rather than removing redundancy. The
brainstorm discipline at this point is *not* to ratify a known-
unneeded shape into the roadmap (Common Rationalisations:
"Approaches A,B,C — proceed with A" → surface the mis-framing),
so no spec was written, no grounding-check dispatched, no planner
handoff.
## Honest correction (lesson)
Mid-Q&A I asserted "M5 cannot wire the real data-server loop
without M4." That was wrong and I corrected it on the record: M5
*can* wire it with M3 + a host-side `for tick in chunk` loop. The
earlier "O(records) vs O(chunks) FFI calls" framing was also
misleading — the per-tick forwarder is thin; the expensive thing
in the M4 alternative is the cons-list marshalling the scalar loop
does not have at all. Re-deriving against the code (not defending
the roadmap I had written) is what surfaced the clause-2 failure;
this is the "user suggestions ≠ directives, form own judgment"
discipline working in the user's favour.
## Decision / outcome
- **M4 retired** in `docs/roadmap.md` (kept one cycle as a struck
entry for context, then delete — never `[x]`, never shipped).
- **M5 reconciled**: `depends on:` M4 → M3 (+ the Tick-coverage
todo); body now states the adapter unrolls each chunk host-side
into per-tick `(State, Tick) -> State` calls; the friction
harvest now feeds the host-per-tick-FFI vs. batch-crossing perf
decision (the P2 flat-array item) rather than an array-vs-cons
decision.
- **Residual = a `[todo]`**: Tick-coverage on M3 — an E2E +
fixture pinning the two-record-param per-tick crossing
`(State, Tick) -> State` on the shipped M3 ABI. Capability is
present today but only E2E-proven for a *single* record param
(`State`): every shipped M3 fixture
(`embed_backtest_step_record{,_borrow}.ail`,
`embed_export_record_ok.ail`) pushes a scalar `Float` sample,
none a record `Tick`. A second record param is the identical
mechanism (same gate arm, same `→ptr`, same forwarder, zero new
code path) — worth pinning by fixture, but a test backfill, not
a milestone (no brainstorm). This is the actual "minimal
data-server binding" the user asked for.
## Forward note (not edited now — avoid scope creep)
The P2 "Flat array/slice primitive — performance" item's framing
("1024 RC cons-cells per chunk on the hot path") is now partly
stale — the cons-list path is dropped, so the residual perf
question is host-side per-tick FFI vs. an eventual batch crossing.
Flagged here; reconcile that entry's wording when it is picked up,
not now.
+1
View File
@@ -104,3 +104,4 @@
- 2026-05-18 — iter embedding-abi-m3.1 (Embedding ABI — M3, DONE 7/7 across one Boss-repaired split dispatch): freeze the value layout + a single-constructor record of `Int`/`Float` fields crosses the C ABI in and out, ownership following the declared `own`/`borrow` mode. No authoring-surface / schema / Form-A / `CheckError` change (M2-style — the `.ail` is the minimal evolution of the M1/M2 scalar kernel into a record `State`, existing `data`+`(own (con T))`+`(export …)`). Shipped: the export-gate scalar predicate `is_c_scalar` widened to a two-level `is_c_abi_type` (a bare `Int`/`Float`, OR a `Type::Con` resolving via the in-scope `env.types` to a single-constructor `data` whose every field is a bare `Int`/`Float` — NOT deep: a record-typed field stays rejected = M4; multi-ctor sums / `Str` / `List` / effectful stay RED; the M1 `embed_export_adt_ret_rejected.ail` must-fail RE-POINTED from the now-M3-valid single-ctor `Pair` to a genuinely-still-rejected multi-ctor+`Str` `Reading`, the pin's meaning sharpened not silently inverted; gate suite 10/10); codegen `llvm_scalar` maps a gate-guaranteed record `Type::Con``ptr` (the M2 `Target::StaticLib` forwarder body BYTE-UNCHANGED — a record crosses as a bare `ptr`, no `sret`/`byval`; all mode-driven RC stays in the byte-unchanged internal `@ail_<mod>_<fn>`; 3/3 staticlib-lowering pins, no `fn_scalar_sig` signature change so no caller ripple); the record memory layout FROZEN as a one-way commitment — DESIGN.md §"Embedding ABI" gains a `### Frozen value layout (M3 — one-way commitment)` SSOT subsection (header@p-8 / i64 tag@0 / fields i64-strided@8+i*8 / size=8+n*8 / `ailang_rc_alloc` construction MUST / `ailang_rc_dec` host-free leak-free because scalar-field / ownership follows the declared mode), the two "provisional until M3" sentences rewritten to one-way-freeze wording, lockstep `// FROZEN ABI` pointers added at the three independent encoders (`runtime/rc.c`, `match_lower.rs` `lower_ctor`, `drop.rs`), 3 stale rustdocs fixed (`codegen/lib.rs`, `core/ast.rs`, the Concerns-carry `check/lib.rs:448-450` variant doc) + the Boss-caught stale `//` gate-comment block (`check/lib.rs:1917-1922`, "M1/scalar-only" → "M1/M2/M3 / C-ABI-permitted incl. single-ctor record"); a new `@ailang_rc_alloc` heap-box byte-pin (`embed_record_layout_pin.rs`) proven RED-on-offset-perturbation / GREEN-on-restore (the freeze made enforceable, not aspirational); the `(State,Float)->State` record fold E2E round-trip proven globally leak-free for BOTH `own` and `borrow`. Process note: the first dispatch (Tasks 14 clean) correctly BLOCKED on Task 5 having surfaced a genuine spec defect — the spec's proof instrument asserted the single `ailang_ctx_free` ctx readback shows `allocs==frees`, structurally unsatisfiable for `borrow` (and only coincidentally passing for `own`) because M2's `__ail_tls_ctx` is bound only for the synchronous forwarder call, so host-side `ailang_rc_dec`s land on `g_rc_*` not ctx. Boss adjudication (M2.1-precedent class — genuine spec defect, deliverable+invariant sound, only verification mechanics mis-specified, first BLOCKED task not 2+-in-family ⇒ consistency-repair not a brainstorm bounce): Boss independently rebuilt+ran both modes and confirmed globally leak-free + value-correct (own ctx live=0/g_rc live=0; borrow ctx live=+N/g_rc live=N, Σlive=0; exit 0 both); spec amended (dated repair note + §"Testing strategy" 3/4 + §"Coherent stop" → global-leak-freedom proof model, the M2-TLS cross-attribution documented as correct behaviour) + plan amended (harness sums ALL `ailang_rc_stats:` lines) + the mechanical code repair applied; partial committed `d5c565d` (PARTIAL 5/7 + Boss spec-defect repair); re-dispatch `task_range:[6,7]` DONE. No fresh grounding-check (the repair removes an over-strong measurement assumption, adds none about compiler behaviour). Independent Boss verification: workspace 639/0, `embed_record_e2e` 2/2 (own+borrow global model), byte-pin 1/1, gate 10/10, forwarder 3/3, `docs_honesty_pin` 5/5 pin-safe (the freeze rewrite kept the pinned "Export parameters are written **bare**…" sentence byte-verbatim+contiguous, shifted :2297→:2299, content-asserted), `design_schema_drift`/`embed_export_hash_stable`/`embed_e2e`/M2 swarm regression-green-unmodified. spec `docs/specs/2026-05-18-embedding-abi-m3.md` (user-approved, grounding-check PASS 8/8, `1fbb9c4`) → plan `docs/plans/embedding-abi-m3.1.md` (`15ee3c5`, Boss-amended) → iter `d5c565d` (PARTIAL 5/7 + Boss repair) + this commit (DONE 67). Milestone-close `audit` next (architect Invariant 1 + bench trio — spec Testing items 8/9, explicitly NOT the implement run's job). → 2026-05-18-iter-embedding-abi-m3.1.md
- 2026-05-18 — audit embedding-abi-m3 (Embedding ABI — M3 milestone close — DRIFT: one [medium]+[low] doc-honesty → one tidy; bench check.py exit 1 decisively causally exonerated, NO ratify): architect `drift_found` — Invariant 1 clean (zero Cargo/runtime dep change; core/codegen/runtime touches only the spec-scoped widen + lockstep comments + pins; no data-server/finance knowledge), the M1/M2 internal-convention byte-invariant held (M2 forwarder body + @ail_<mod>_<fn>/_adapter/_clos absent from the diff — the spec's load-bearing "transparent ptr pass-through" claim verified), the frozen-layout SSOT (DESIGN.md `### Frozen value layout` :2321-2359) present-tense + matching `match_lower.rs lower_ctor` (size=8+n*8/tag@0/fields@8+i*8) AND the enforcing byte-pin (@ailang_rc_alloc(i64 24)/tag@0/@8/@16), all three // FROZEN ABI lockstep pointers aimed at the SSOT, the Boss spec-consistency repair note + Testing 3/4 + journal Boss-adjudication/Re-dispatch mutually consistent and honest about the M2-TLS cross-attribution (correct behaviour, not a leak), no new CheckError/schema/Form-A (M2-style honoured). Two residual doc-honesty drift items, both milestone-owned: `[medium]` DESIGN.md:2299-2302 — a surviving M1-era paragraph asserts modes "apply only to heap-shaped types, which the **scalar-only rule above forbids** at an export boundary anyway", now self-contradicting the section's own freeze (`:2280-2284` was rewritten to accept a single-ctor record — there is no scalar-only rule) and the (own/borrow (con State)) mode contract at :2345-2351 → fix (docs-honesty tidy); `[low]` crates/ailang-codegen/src/lib.rs:608-610 — forwarder-body comment still says the gate "guarantees Int/Float; map Int→i64 Float→double", now false (records→ptr), classified by the architect "carried debt, not a scope miss" (the body is contractually byte-frozen so the comment was outside any M3 edit region; comment-only, no emitted-IR change) → fix, folded into the same tidy. (The Boss had already caught+fixed one sibling stale `//` gate-comment block at check/lib.rs:1917-1922 during the DONE inspect; architect found no further siblings.) Bench: check.py exit 1 (3 regressed: throughput.bench_list_sum.bump_s +12.62%/tol10; latency.implicit_at_rc.p99_9_us +28.45%/tol25; .max_us +35.18%/tol30), compile_check exit 0 (24/0), cross_lang exit 0 (25/0, every rc_over_c within tol). The 3 firings are the two standing tracked P2 noise families (the *.bump_s ~+5-13% stale-baseline + the *.max_us/p99_9 -n5 tail-jitter structural false-positive — signature: same bench median +5.11% ok / p99 +15.08% ok, only the extreme tail quantiles fire). DECISIVE causal exoneration (M2-audit method, not hand-waved): M3 changed no executable-path codegen (the llvm_scalar record→ptr arm is reachable only for an (export) single-ctor record; no bench_* program has an (export)), so built `ail` at HEAD and at pre-M3 9a609ae in an isolated worktree and cmp'd the GENERATED IR — byte-identical for bench_list_sum, bench_list_sum_explicit, bench_latency_implicit, bench_tree_walk; the IR is what is handed to deterministic `clang -O2`, so the bench binaries are byte-identical and M3 causation is logically impossible. NO baseline ratify (not M3 artefacts; the two P2 todos stay separately tracked). Resolution: `[medium]`+`[low]` → one docs-honesty tidy iter embedding-abi-m3.tidy (pin-safe re docs_honesty_pin.rs:135, planner Step-5 item-6 / M2.tidy precedent), routed planner→implement; bench → carry-on. Milestone substantively closed + sound; roadmap [~]→[x] follows the tidy landing clean. → 2026-05-18-audit-embedding-abi-m3.md
- 2026-05-18 — iter embedding-abi-m3.tidy (M3 audit [medium]+[low] doc-honesty fix, DONE 3/3, pin-safe): closed the two DRIFT items the M3 milestone-close audit routed here (M2.tidy `[medium]+[low] doc-honesty → tidy` precedent). [medium] docs/DESIGN.md §"Embedding ABI" — surgically replaced ONLY the contradicted M1-era parenthetical "(modes apply only to heap-shaped types, which the scalar-only rule above forbids at an export boundary anyway)" with the present-tense truth "(a single-constructor record export parameter, by contrast, carries `own`/`borrow` — the ownership contract the frozen value layout below specifies)"; the parenthetical shared physical line :2300 with the docs_honesty_pin.rs:135 pinned bare-scalar sentence ("Export parameters are written **bare**: a scalar type carries no `own`/`borrow` mode", norm()-whitespace-collapsed, fn form_a_scalar_param_carveout_present_and_old_rule_absent) — the edit kept every pinned word (line :2299 + the `own`/`borrow` mode` continuation untouched), the planner Step-5 item-6 presence-pin-vs-verbatim-edit collision the M2.tidy precedent ran. [low] crates/ailang-codegen/src/lib.rs:608-610 — comment-only honesty fix ("gate guarantees Int/Float; map Int→i64,Float→double" → "...Int/Float or a single-constructor record of those (M3); map Int→i64, Float→double, a record → ptr"); tree-wide-grep-confirmed no test pins the comment text; the byte-pin (embed_record_layout_pin) + forwarder-IR pin (embed_staticlib_lowering) assert generated IR not source comments → byte-identical before/after (the guard that no codegen moved). Boss-verified independently: both stale fragments grep-ABSENT; the 4 standing pins green at the exact recon baseline (docs_honesty_pin 5/0 ⇒ pin-safety held, design_schema_drift 8/0, embed_record_layout_pin 1/0, embed_staticlib_lowering 3/0); workspace 639/77 byte-unchanged from the M3-DONE baseline (docs/comment tidy, zero behaviour/test delta); diff exactly 2 files (DESIGN.md 5±, codegen/src/lib.rs 7±). No language/checker/codegen behaviour change; no audit/fieldtest gate (the 4 pins + 639/77 ARE the regression coverage, M2.tidy precedent). One non-gating planner-quality defect recorded in Concerns: Task-3 Step-1's `guarantees every param` verification grep is a substring of the plan's own Task-2 replacement text (non-discriminating) — the orchestrator correctly verified the substantive intent via discriminating fragments instead of bending code (same family as planner Step-5 item-8). bench: already carry-on / NO ratify at the M3 audit (causally exonerated by byte-identical generated IR; this tidy touches no executable path). audit source docs/journals/2026-05-18-audit-embedding-abi-m3.md (b8a60b1) → plan docs/plans/embedding-abi-m3.tidy.md (44ced51) → iter this commit. M3 milestone substantively closed + sound; roadmap [~]→[x] follows. → 2026-05-18-iter-embedding-abi-m3.tidy.md
- 2026-05-18 — brainstorm embedding-abi-m4 → RETIRED, never speced (premise collapsed under its own feature-acceptance gate during Step-2/3 Q&A; no spec, no grounding-check, no planner handoff — the "problem mis-framed → don't ratify a known-unneeded shape" brainstorm path): `/boss` picked top-P0 "Embedding ABI — M4: sequence crossing via `List`"; user green-lit a continue-here brainstorm; recon (`ailang-plan-recon`) returned a full fact sheet; two user forks resolved in Q&A (own-only `List` param; `List Record`-only element) and Approach A (structural list-shaped `is_c_abi_type` arm, no name-match; B name-anchored / C `std_list`-SSOT-first rejected on language/scope grounds) recommended — all now moot. Struck on **feature-acceptance clause 2**: the shipped M3 gate `is_c_abi_type` (`crates/ailang-check/src/lib.rs:1934-1953`) is a per-parameter loop accepting a C scalar OR a single-ctor all-scalar record *independently per param*, and the forwarder's `llvm_scalar` maps every non-scalar `Type::Con``ptr` (M3-frozen), so `(State, Tick) -> State` (both single-ctor all-scalar records) is **already gate-accepted + forwarder-supported today** — the minimal data-server binding is M3 (shipped) + a host-side per-tick loop; cons-list crossing would *add* a 2N+1-box-per-chunk host builder + the deferred flat-array perf debt and removes no redundancy, with no named consumer (M5's adapter unrolls each chunk host-side — a clean adapter, the sole data-server↔AILang meeting point per Invariant 1; whole-chunk in-kernel visibility is semantically void since `State` threads across calls regardless of chunk boundaries). Honest mid-Q&A correction recorded: I had asserted "M5 cannot wire data-server without M4" — false (M5 wires it on M3 + `for tick in chunk`); re-deriving against the code rather than defending the roadmap I wrote is what surfaced the clause-2 failure ("user suggestions ≠ directives, form own judgment"). Outcome: M4 retired in `docs/roadmap.md` (struck entry kept one cycle, never `[x]`); M5 reconciled (`depends on:` M4→M3 + Tick-coverage todo; adapter unrolls host-side; friction feeds the host-per-tick-FFI-vs-batch P2 perf decision); residual = a new `[todo]` "Tick-coverage on M3" (E2E+fixture pinning the two-record-param per-tick `(State, Tick) -> State` shape — capability present today but only E2E-proven for a single record param `State`; every shipped M3 fixture pushes a scalar `Float` sample, none a record `Tick`; test backfill, no brainstorm — the actual "minimal data-server binding"). Forward note: the P2 flat-array item's "1024 cons-cells/chunk" framing is now partly stale (cons-list path dropped) — reconcile when picked up, not now. → 2026-05-18-brainstorm-embedding-abi-m4-retired.md
+38 -19
View File
@@ -304,31 +304,50 @@ work progresses.
`docs/journals/2026-05-18-audit-embedding-abi-m3.md`;
`docs/journals/2026-05-18-iter-embedding-abi-m3.tidy.md`.
- [ ] **\[milestone\]** Embedding ABI — M4: sequence crossing via
the existing `List` ADT. Host builds a `List Record` per chunk;
`(State, List Record) -> State` callable end-to-end. Pure
ABI/marshalling, no language change — applies M3's frozen
layout repeatedly. Makes the real fold runnable over real
chunks. The array-vs-cons-list performance question (1024 RC
cons-cells per chunk on the hot path) is deliberately *not* in
scope here — capability now, performance as a separate later
non-ABI milestone (P2 below), keeping the user's "focus on the
ABI, ignore the array gap" cut. Coherent stop:
`(State, List M1) -> State` runs.
- depends on: Embedding ABI — M3.
- context: 2026-05-18 chat.
- **\[milestone — RETIRED 2026-05-18, never speced\]** Embedding ABI
— M4: sequence crossing via the `List` ADT. Struck during its own
brainstorm Q&A, before any spec: failed feature-acceptance
clause 2. The minimal data-server binding is M3 (shipped) + a
host-side per-tick loop over each chunk's records —
`(State, Tick) -> State` is *already* gate-accepted and
forwarder-supported under M3 (the gate loop accepts each record
param independently; the forwarder maps each to `ptr`). Cons-list
crossing would *add* a 2N+1-box-per-chunk host builder plus the
deferred flat-array perf debt rather than remove redundancy, and
builds something no named consumer needs (M5's adapter unrolls
the chunk host-side; whole-chunk in-kernel visibility buys nothing
`State` threads across calls regardless of chunk boundaries).
Removed (not `[x]` — never shipped); rationale in the journal.
Stays one cycle for context, then delete.
- context: `docs/journals/2026-05-18-brainstorm-embedding-abi-m4-retired.md`.
- [ ] **\[todo\]** Tick-coverage on M3 — E2E + fixture pinning the
two-record-param per-tick crossing `(State, Tick) -> State` on the
shipped M3 ABI (host builds `Tick` via `ailang_rc_alloc` per the
frozen `make_state` recipe, calls once per tick, threads `State`,
alloc==free, both `own`/`borrow`). Zero language change — capability
is present today but only E2E-proven for a *single* record param
(`State`); this pins the two-record-param shape by fixture. No
brainstorm (test backfill). The residual of retired M4 — the actual
"minimal data-server binding".
- context: `docs/journals/2026-05-18-brainstorm-embedding-abi-m4-retired.md`.
- [ ] **\[milestone\]** Embedding ABI — M5: `ail-embed` adapter +
`data-server` wiring + thread-swarm backtest (fieldtest
target). The host-side adapter crate (sole meeting point of
`data-server` and AILang, per Invariant 1) wires
`data-server`'s `while let next_chunk` loop to the AILang
kernel via the ABI; an actual small backtest runs over a
thread swarm. Natural `fieldtest` milestone: existence proof
of the whole goal + friction harvest feeding the
array-primitive decision. Coherent stop: the goal itself.
- depends on: Embedding ABI — M4.
- context: 2026-05-18 chat.
kernel via the M3 ABI by **unrolling each chunk host-side** into
per-tick `(State, Tick) -> State` calls (M4's cons-list crossing
was retired — see above; the adapter, not the kernel, owns chunk
iteration); an actual small backtest runs over a thread swarm.
Natural `fieldtest` milestone: existence proof of the whole goal
+ friction harvest feeding the host-per-tick-FFI vs. batch-
crossing perf decision (the P2 flat-array item). Coherent stop:
the goal itself.
- depends on: Embedding ABI — M3; Tick-coverage on M3 (todo above).
- context: 2026-05-18 chat; M4 retirement
`docs/journals/2026-05-18-brainstorm-embedding-abi-m4-retired.md`.
- [x] **\[milestone\]** Heap-`Str` ABI — runtime infrastructure for
malloc-backed, refcounted `Str` values alongside the existing