Files
AILang/docs/specs/0066-fieldtest-raw-buf-milestone.md
T
Brummel 9545257333 fieldtest: raw-buf milestone — 4 examples, 3 findings (refs #7)
Milestone-close fieldtest for the raw-buf milestone, milestone-scope
variant: four end-to-end scenarios derived top-down from the milestone
promise (construct via (new RawBuf <elem> <size>), fill/read via
set/get, size-only buffers, element variety across {Int,Float,Bool},
forbidden-element reject at check, buffer threading under own/borrow).

Result confirms the milestone-promise hypothesis — RawBuf is clean
end to end, zero bugs:
- mrb_1 Int fill/read loop: check ok, run + native build both print 70
- mrb_2 Float own->own threading + borrow read: prints 3.5
- mrb_3 size-only Bool buffer (#51 legitimate-but-unobserved case): prints 8
- mrb_4 Unit element: rejected at check with a precise
  param-not-in-restricted-set diagnostic; build stops at the same
  error, codegen never reached (the #51 fix)

Two [working] findings (carry-on) and one [spec_gap]: the
kernel-extensions whitepaper (design/models/0007) teaches bare-mode
fn-type slots that the post-#55 parser rejects. Verified and triaged
separately; the ledger fix follows in its own commit.
2026-06-02 10:46:16 +02:00

7.1 KiB

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 <elem> <size>), 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 Unitis not in the restricted set for type-variableaofraw_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)