Files
AILang/docs/journals/2026-05-19-audit-embedding-abi-m5.md
T
Brummel 28ab56af7e audit embedding-abi-m5: milestone close — RATIFY gc_rss trio (evidenced) + DRIFT (one doc-honesty tidy); P2 bench noise causally exonerated, baseline pristine for it
Architect: Invariant 1 PASS (zero data-server in any compiler
crate/runtime; ail-embed its own workspace root); M3 frozen-layout
SSOT unmoved (the lone rc.c change 7bfa11e touched only the global
stats counters); rc.c header-comment honesty accurate. Drift =
[medium] DESIGN.md:2358-2360 stale "additive M4 concern" (PRE-
EXISTING — M4 retired; correctly scoped out of every M5 iter) +
[medium] DESIGN.md §"Embedding ABI" silent-drift (it asserts "no
shared mutable runtime state … data-race-free" but the now-atomic
global RC-stats fallback IS shared mutable state under concurrency;
M5 is AILang's first concurrent consumer) + [low] rc.c:88 "two
unconditional ++" imprecise → one doc-honesty tidy iteration
(embedding-abi-m5.tidy), same honesty axis, not split.

Bencher refuted my pre-audit "gc_rss allocator-orthogonal →
environmental" hypothesis WITH EVIDENCE: the 3x *.gc_rss_kb +32%
are genuine, deterministic, M5-attributable — 7bfa11e's atomic
split a branch in ailang_rc_alloc (reached by GC programs via
str.c int->str at print), spilling %r14/%rbx; a stale interior
list pointer in the new spill slot is conservatively retained by
Boehm, pinning a ~32 MB dead spine (collection #12 freed PRE 32 MB
vs HEAD 32 B); bisects exactly on the commit, IR byte-identical,
ail sha256-identical; RC/bump RSS dead-stable. bump_s + max_us are
pre-existing tracked-P2 noise (bump RC-orthogonal; max_us 1-sample
tail-jitter, PRE often worse, +65->+104% swing between runs) — NOT
M5.

RATIFY the gc_rss trio. Language reason (not effort): Decision 9 —
Boehm is explicitly transitional; AILang's committed model is RC +
uniqueness. The measurable cost of the correct, necessary,
user-approved swarm-safety fix is exclusively a Boehm
conservative-stack-scan false-retention artifact on the
transitional allocator (codegen-layout-fragile; no memory actually
leaked; RC/bump RSS dead-stable). Chasing it with an out-of-scope
codegen change invests in the path being migrated away from; the
committed RC path is unaffected. Selective baseline re-anchor:
bench/baseline.json lines 14/44/64 ONLY -> 137448/137768/137636
(bencher-confirmed steady-state); every other metric byte-pristine
(a wholesale --update-baseline would absorb the P2 noise and break
the established-envelope discipline). Re-run confirms the 3 now ok
at +-0.3%.

bump_s x2 + max_us x2: NO-ratify, causally exonerated, baseline
left pristine — identical disposition to the M2/M3/prelude-decouple
milestone closes.

Friction-harvest recorded (P2 flat-array-decision input):
host-per-tick-FFI ~= ~206 ns/tick at real EURUSD volume.

Ratify discipline: updated baseline JSON + this paired JOURNAL
ratify entry committed together (bench/baseline.json + the audit
journal + INDEX line). M5 closes after the doc-honesty tidy lands
and is Boss-verified.
2026-05-19 02:56:43 +02:00

8.8 KiB
Raw Blame History

audit embedding-abi-m5 — milestone close

Date: 2026-05-19 Scope: c75fb80..HEAD (M3-close → m5.3 0900f3f), focus the M5 arc Outcome: DRIFT (one doc-honesty tidy) + RATIFY (gc_rss trio, evidenced) + causally-exonerated pre-existing P2 bench noise

Architect (drift review) — drift_found

Gates that PASS:

  • Invariant 1 — PASS (hard M5 spec Acceptance gate, spec:328-330). cargo metadata --no-deps on the AILang root manifest = zero data-server; ail-embed/ is its own [workspace] root, not an AILang member. No compiler crate / runtime/ gains a data_server/finance symbol. The only "finance" hits in compiler crates are the M3 ABI export symbol name backtest_step_tick in tests/docs — ABI knowledge, not data-server knowledge (correct).
  • M3 frozen-layout SSOT unmoved. The one runtime/rc.c change in range (7bfa11e) touches only the two GLOBAL g_rc_* statistics counters; box offsets, p-8 header, ctor tag, host-ailang_rc_dec rule byte-unchanged; DESIGN.md §"Frozen value layout" correctly not touched.
  • rc.c header-comment honesty accurate. The bugfix-rc-global-stats-race GREEN rewrite (rc.c ~:42-58 / ~:85-101) matches the actual _Atomic decls + atomic_fetch_add_explicit/ atomic_load_explicit sites + plain per-ctx ++; the rc.c:~270 drop-worklist "single-threaded" note is correctly still true (per-call worklist).

Drift list (prioritised):

  • [medium] docs/DESIGN.md:2358-2360 — "an additive M4 concern" for a recursive typed-free of boxed-field records is stale: M4 was RETIRED 2026-05-18 (never speced), will never ship. PRE-EXISTING (M4-retirement era), correctly scoped out by the M5 spec §"Out of scope" and the M4-retirement journal forward note; NOT an M5 regression. Direction: doc-honesty tidy (drop the M4 reference; the boxed-field record is simply export-gate-rejected).
  • [medium] docs/DESIGN.md §"Embedding ABI" — DESIGN.md already asserts "no shared mutable runtime state … data-race-free, sanitiser-verified". After 7bfa11e the global RC-stats fallback is shared mutable runtime state under concurrency (atomic-relaxed), and M5 (the embedding swarm) is AILang's first concurrent runtime consumer. The doc asserts a concurrency property whose mechanism it no longer fully/accurately describes — real silence-drift the contract line reaches. Direction: additive present-tense note (the global RC-stats fallback is atomic-relaxed; per-ctx counters + the per-object refcount header stay non-atomic by design because a box never crosses a thread and each ctx is single-thread-per-ctx) — a current-state mirror, not a history retelling.
  • [low] runtime/rc.c:88 — "two unconditional ++" is now imprecise (the global path is a relaxed atomic add, conditional on _ctx == NULL). Pre-existing, self-flagged known debt in 2026-05-19-iter-bugfix-rc-global-stats-race.md; a performance aside, not an atomicity claim. Fold into the same tidy if rc.c is reopened.

Architect recommendation (adopted): ONE doc-honesty tidy iteration reconciling the stale-M4 phrasing and the atomic-global-stats / first-concurrent-consumer note (carrying rc.c:88 if rc.c is reopened) — same surface, same honesty axis; do not split.

Bencher (bench-regression localisation) — exit 1, hypothesis-driven

bench/check.py exit 1: 6 regressed / 1 improved / 56 stable on the first audit run. Bencher built ail at HEAD (0900f3f) and pre-atomic (427b687) in an isolated worktree; ail binary sha256-identical; emitted IR for the gc_rss fixtures byte-identical both sides (rc.o links into bench binaries, not the compiler).

  • 3× *.gc_rss_kb +32% (list_sum, hof_pipeline, list_sum_explicit) — GENUINE, DETERMINISTIC, M5-ATTRIBUTABLE. My pre-audit causal read (allocator-orthogonal → environmental) was refuted by evidence. Mechanism: 7bfa11e's atomic split a branch in ailang_rc_alloc (called by str.c int→str at print, reached by GC-mode programs too), spilling %r14/%rbx; a stale interior list pointer in the new spill footprint is conservatively retained by Boehm, pinning a ~32 MB dead list spine across a collection (Boehm internal: collection #12 freed PRE 32,000,032 B vs HEAD 32 B). Bisects exactly on the commit with IR byte-identical — causation established, not inferred. RC/bump RSS dead-stable.
  • bench_list_sum.bump_s / bench_list_sum_explicit.bump_s ~+12% — pre-existing tracked-P2 stale-0.046/0.048s-anchor; bump path is g_rc_*-orthogonal; HEAD-vs-PRE delta ~2%. NOT M5.
  • latency.{explicit,implicit}_at_rc.max_us — 1-sample worst-case tail; median/p99/p99.9 flat both sides; PRE frequently worse; tracked-P2 -n5 tail-jitter. Re-run minutes later swung +65→+104% / +33→+71% — dispositively noise, NOT M5.

Adjudication (orchestrator)

RATIFY — the gc_rss trio (7bfa11e, evidenced)

Item: throughput.bench_list_sum.gc_rss_kb, bench_hof_pipeline.gc_rss_kb, bench_list_sum_explicit.gc_rss_kb, each ~+32% (103980→137448, 103788→137768, 103772→137636).

Iter that moved the metric: 7bfa11e (fix(rc): GREEN — atomic global g_rc_* stats fallback counters), the user-approved Option-A swarm-safety fix in the bugfix-rc-global-stats-race chain — necessary and load-bearing for M5's deterministic leak-proof (reverting it reintroduces the non-deterministic swarm-leak race).

Language reason (not effort): AILang's committed memory model is RC + uniqueness inference; Boehm is explicitly transitional (Decision 9). The measurable cost of the correct, necessary atomic fix is exclusively a Boehm conservative-stack-scan false-retention artifact on the transitional GC allocator — the bencher is explicit it is "a Boehm conservatism artefact, not an RC-semantics cost"; RC and bump RSS are dead-stable; no memory is actually leaked by AILang (Boehm conservatively retains a dead stack slot). The mechanism is "codegen-layout-fragile — a future unrelated rc.c edit could equally flip it back." Spending an out-of-scope codegen change (clear the dead root before print) to placate the conservative scanner of the allocator the language is migrating away from is investing in the path being removed; the committed RC path — the one that matters — is unaffected. Therefore ratify the new deterministic steady-state rather than chase it.

Action taken: selective baseline re-anchor — bench/baseline.json lines 14/44/64 only (the 3 evidenced M5-caused gc_rss_kb), set to the bencher-confirmed stable HEAD values (137448 / 137768 / 137636); every other metric byte-pristine (a wholesale --update-baseline would have absorbed the unrelated P2 noise and broken the established-envelope discipline). Re-run confirms the 3 are now ok (±0.3% vs the new anchor, ≪ 5% tol).

NO-RATIFY / causally exonerated — the pre-existing P2 noise

bench_list_sum.bump_s, bench_list_sum_explicit.bump_s, latency.explicit_at_rc.max_us, latency.implicit_at_rc.max_us remain firing (check.py still exit 1 on these). Bencher-established NOT M5 (bump path RC-orthogonal; max_us 1-sample tail-jitter, PRE often worse, +65→+104% swing between runs minutes apart). Baseline left pristine for these — the established tracked-P2 noise envelope, identical disposition to the M2 / M3 / prelude-decouple milestone closes ("check.py exit 1 causally exonerated, NO ratify, baseline pristine"). The standing P2 items (the stale bump_s anchor; the -n5 max_us tail) are not M5's and are not bundled here.

DRIFT → tidy iteration

The architect's [medium]+[medium]+[low] doc-honesty drift is resolved via one tidy iteration embedding-abi-m5.tidy (planner → implement; doc-only — DESIGN.md stale-M4 phrasing + the atomic-global-stats / first-concurrent-consumer present-tense note reconciling the "no shared mutable runtime state" claim; rc.c:88 carried along). Same surface, same honesty axis, not split.

Friction-harvest (recorded — the P2 flat-array-decision input)

m5.3 measured host-per-tick-FFI ≈ 658 ms / 3 192 562 ticks ≈ ~206 ns/tick at real EURUSD volume (one symbol-window per thread, own ctx, the M3 ABI per tick). This is the observation the P2 "flat-array/batch-crossing" roadmap item is now informed by: per-tick FFI at ~200 ns is the cost a future batch crossing would amortise; whether that is worth the cons-list-free flat-array path is the P2 decision, now anchored on a real number rather than the stale cons-list framing the M4 retirement obsoleted.

Close posture

Invariant 1 PASS; M3 frozen layout unmoved; bench dispositioned (gc_rss ratified evidenced, P2 noise exonerated baseline-pristine). M5 closes after the doc-honesty tidy lands and is Boss-verified. The baseline edit + this journal are committed together (ratify discipline: updated baseline JSON + paired JOURNAL ratify entry).