# Fieldtest — raw-buf milestone (milestone-7) — 2026-06-02 **Status:** Draft — awaiting orchestrator triage **Author:** fieldtester (dispatched by fieldtest skill, milestone-scope variant) ## Scope The raw-buf milestone (#7) ships `RawBuf T` — AILang's first kernel-tier *base* extension: a content-addressed, mode-tracked, fixed-size flat buffer of primitive elements. The milestone promises that an LLM author can write realistic RawBuf-centric programs and hit no surprises: construct via `(new RawBuf )`, fill and read via `RawBuf.set` / `RawBuf.get`, use size-only buffers via `RawBuf.size`, vary the element type across `{Int, Float, Bool}`, have a forbidden element type (e.g. `Unit`) rejected at `check` rather than crash at codegen (the #51 fix), and thread an owned buffer through functions under the uniqueness/mode discipline. This is the milestone-close fieldtest. Four end-to-end scenarios were derived top-down from the milestone promise — each a realistic downstream task — rather than re-running the per-cycle axes the earlier per-iteration fieldtests (specs 0058, 0059) already covered. Binary exercised: `cargo run -q -p ail` against the current working tree (dev profile, freshly built from HEAD). Full native build of scenario 1 additionally went through `ail build -o … + clang`. ## Examples ### examples/fieldtest/mrb_1_histogram.ail — Int fill/read loop with computed result - Allocates a 5-slot `Int` RawBuf, fills it via an `own → own` recursive `fill` helper (slots 0,7,14,21,28), then sums it back through a `borrow` reader and prints the total. - Fits the "buffer fill/read loop" + "threading through a function under uniqueness/mode" axes: `fill` consumes and returns the owned buffer per `RawBuf.set`; `sum` reads it through a borrow. - Outcome: `check` ok (exit 0); `run` printed `70` (expected 70); full `build` to native binary printed `70`. Matched. ### examples/fieldtest/mrb_2_float_smooth.ail — Float element variety + threading - Allocates a 2-slot `Float` RawBuf, stores 3.0 and 4.0 via an `own → own` `store_pair` helper, reads both slots through a `borrow` `average` helper, prints the mean. - Fits the "element-type variety (Float)" + "threading a buffer through a function" axes; the buffer crosses two function boundaries under own/borrow. - Outcome: `check` ok; `run` printed `3.5` (expected 3.5). Matched. ### examples/fieldtest/mrb_3_bool_capacity.ail — size-only Bool buffer (#51 legitimate case) - Allocates an 8-slot `Bool` RawBuf and prints its capacity via `RawBuf.size` only — never `get`/`set`. - Fits the "size-only buffer" + "element-type variety (Bool)" axes: an explicit `Bool` element type is named at construction but only `.size` is ever observed — the #51 legitimate-but-unobserved case. - Outcome: `check` ok; `run` printed `8` (expected 8). Matched. ### examples/fieldtest/mrb_4_unit_element_reject.ail — forbidden element type (Unit) - `main` names `Unit` as the RawBuf element type, then only sizes the buffer and prints the size. - Fits the "forbidden element type — must be rejected at check, not crash at build" axis (the #51 fix). - Outcome: `check` rejected at exit 1 with `param-not-in-restricted-set` naming `Unit`; `build` also stopped at the same check error, codegen never reached, no crash. Matched the promise exactly. ## Findings ### [working] RawBuf end-to-end is clean across all three permitted element types - Examples: mrb_1 (Int), mrb_2 (Float), mrb_3 (Bool), plus the build-to-native path on mrb_1. - What happened: construct / set / get / size all behaved correctly; computed results were exact (`70`, `3.5`, `8`); `check` reported `ok (N symbols across 3 modules)`; the native binary produced the same output as `run`. The `own → own` threading of a buffer through `fill` / `store_pair` and the `borrow` reads in `sum` / `average` / `capacity` all checked clean with no spurious use-after-consume or clone friction — the author never had to reach for a `clone` or an awkward re-bind beyond the natural `(let buf (app … buf …) …)` linear-threading idiom shown in the example corpus. - Why working: the new surface was reached for naturally and produced correct results on the first try once the fixtures were in canonical form. This is the central milestone-promise win. - Recommended action: carry-on. ### [working] Forbidden element type is rejected at check with a precise diagnostic (the #51 fix) - Example: mrb_4. - What happened, verbatim: `error: [param-not-in-restricted-set] main: type-arg `Unit` is not in the restricted set for type-variable `a` of `raw_buf.RawBuf` (allowed: one of {Bool, Float, Int})` The `--json` form carries a structured `ctx` (`{"type":"raw_buf.RawBuf","var":"a","found":"Unit","allowed":["Bool","Float","Int"]}`). `ail build` stops at the same error — codegen is never reached, no crash, no `RawBuf_new__Unit`-style unregistered-intercept failure. - Why working: this is precisely the #51 promise — reject at `check`, not crash at codegen. The diagnostic names the offending type, the type-variable, the type, and the allowed set; it is actionable. - Recommended action: carry-on. ### [spec_gap] The kernel-extensions whitepaper shows bare-mode value params that the parser now rejects - Surfaced while drafting mrb_1 / mrb_2 (first run before fix). - What happened: I wrote scalar value params without a mode — `(params (own (con RawBuf (con Int))) (con Int) (con Int) (con Int))` — copying the shape shown in the public whitepaper `design/models/0007-kernel-extensions.md` (the `sum_window_step` / `emit_if_full` / `Series.at` signatures at lines 60-69, 84-86 use bare `(con Int)` / `(con Float)` slots). The parser rejected it: `error: [surface-parse-error] parse error in fn-type-slot: fn-type slot requires a mode: write (own T) or (borrow T) at byte 310`. Adding `(own …)` to every value slot fixed it and the examples then checked and ran clean. - Why spec_gap: the parser is internally consistent — *every* fn-type slot requires a mode, and that is a defensible contract (it is the `bare_slot_reject` behaviour). But the whitepaper, which is part of the public interface an LLM author reads to learn the surface, still displays the bare-mode form throughout its worked examples. An author who pattern-matches the whitepaper writes programs that fail to parse. Two readings were plausible (value params get an implied mode vs. every slot must be explicit); the shipped reading is "every slot explicit", but the whitepaper teaches the other. - Recommended action: tighten the design ledger — update the `design/models/0007-kernel-extensions.md` worked examples to put explicit `(own …)` / `(borrow …)` on every value param slot so the public surface matches the shipped parser contract. (Whitepaper drift, not a code bug.) ## Recommendation summary | Finding | Class | Action | |---|---|---| | RawBuf end-to-end clean across Int/Float/Bool | working | carry-on | | Unit element rejected at check (#51 fix) | working | carry-on | | Whitepaper shows bare-mode value params parser rejects | spec_gap | tighten the design ledger (models/0007) |