# 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.
