Files
AILang/docs/specs/0069-cma-corpus-revival.md
T
Brummel 74849160b7 spec: cross-model harness corpus revival (refs #68)
The cma authoring-form harness (experiments/2026-05-12-cross-model-authoring)
is milestone-complete but its corpus went schema-dead: the implicit-cutover
made param_modes/ret_mode mandatory, so today's ail check rejects 100% of the
master examples + reference solutions and the renderer crashes on load. This
spec revives the corpus to the current language and gets a mock run green.

Scope (corpus-first, method-form-later per #68): minimal field-completion
preserving existing modes; two new author-facing examples (loop/recur, new);
spec.md section 4 rewritten to own/borrow with the borrow-over-value rule
surfaced under the parse-gate; spec_completeness allowlists the non-authorable
Term::Intrinsic out; mock fixture migrated so harness score assertions hold.
No live IONOS call. Old specs 0017/0018 left intact as closed-milestone
history.
2026-06-02 16:32:31 +02:00

262 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cross-model harness corpus revival — Design Spec
**Date:** 2026-06-02
**Status:** Draft — ready for planner
**Authors:** Brummel (orchestrator) + Claude
**Tracker:** refs #68
## Goal
The cross-model authoring-form harness in
`experiments/2026-05-12-cross-model-authoring/` (shipped by milestones
cma.1-3, ms.1-2) is milestone-complete but its corpus is **schema-dead**.
The `render` and `harness` crates still build against current
`ailang-core`/`ailang-surface`, but every `master/examples/*.ail.json`
and `master/tasks/*.reference.ail.json` was authored when `param_modes`
could be omitted. The #55 implicit-cutover (`76b21c0`) deleted
`ParamMode::Implicit` and made `param_modes`/`ret_mode` mandatory on
`Type::Fn`. Consequences, all reproduced 2026-06-02:
- `ail check master/tasks/t1_add_three.reference.ail.json`
`Error: missing field 'param_modes'`. 100% of the corpus is rejected.
- `render` crashes loading the first example
(`missing field 'param_modes' at line 19`).
- `render/tests/spec_completeness.rs` no longer compiles: it matches on
the deleted `ParamMode::Implicit`, and its `Term` walker is
non-exhaustive against four variants that landed since May — `Loop`,
`Recur`, `New`, `Intrinsic`.
This iteration brings the corpus to the language as it ships today and
gets a **mock** run green. It does **not** make a live IONOS call and
does **not** decide the recurring-method form (skill vs. tool) — both
are deferred per #68.
This spec is **new**; the closed milestone specs
`docs/specs/0017-cross-model-authoring-form-test.md` and
`0018-multi-subject-codellama.md` are **not** rewritten. They correctly
describe the then-state (`.ailx`, `docs/DESIGN.md`); editing them would
falsify the history they record (honesty-rule, `0007-honesty-rule.md`).
## What changed in the language since the corpus was written
Mapped 2026-06-02 against the current tree:
1. **`param_modes` + `ret_mode` mandatory.** Every `Type::Fn` in canonical
JSON now carries `param_modes` (a list of `own`/`borrow`, one per
parameter) and `ret_mode`. `ParamMode::Implicit` no longer exists.
2. **Three new author-facing Term variants** — surface-writable, appear in
ordinary `examples/`, classified author-facing in the ledger:
- `Loop` / `Recur` — bounded iteration (`design/contracts/0012-tail-calls.md`,
`docs/specs/0034-loop-recur.md`). Surface keywords `loop` / `recur`.
- `New` — kernel-extension type construction
(`design/models/0007-kernel-extensions.md`). Surface keyword `new`.
3. **One new Term variant that is NOT author-facing**`Intrinsic`, the
`(intrinsic)` body-marker for compiler-supplied function bodies. It is
**not** in the parser's term dispatch and the typechecker rejects it
outside a kernel-tier/prelude module
(`crates/ailang-core/src/ast.rs` ~609; `design/glossary.md`). A foreign
model neither can nor should produce it.
## Architecture
### Repair strategy — minimal field-completion, modes preserved
The mode of each parameter is a **semantic** property (does the callee
consume the argument, or borrow it?), not a free mechanical field. But
recon (2026-06-02) shows most corpus files **already carry their
`param_modes` explicitly** — `data_simple` (`own`), `data_with_match`
(`borrow`), `param_modes_all` (both). What is uniformly absent is
`ret_mode`; only a handful lack `param_modes` outright (the 0-param fns
need `[]`; `fn_calls_prelude.add`, `fn_with_lambda.make_adder`,
`match_literal_pattern`'s two fns need values). So the Spec goal — modes
author-set, not guessed — is already met for the files that set them. The
repair must **preserve** those, not overwrite them.
Strategy, per `master/examples/*.ail.json` and
`master/tasks/*.reference.ail.json`:
1. Complete the missing schema fields in place, preserving every existing
`param_modes` value verbatim:
- add `ret_mode` where absent — `own` (a function returns a fresh owned
value; `borrow`-return is forbidden by `0015`, so `own` is the only
legal choice here);
- add `param_modes` where absent — `[]` for 0-param fns; for the
value-taking fns, `own` per parameter (the natural consume default).
2. `ail check <f>.ail.json` exit 0 is the per-file oracle: a wrong mode is
a check error, so a file that checks green has a ratified mode set.
3. `example_roundtrip` (load → print `.ail` → reparse → same canonical
bytes) is the second gate; it canonicalises on compare, so field-order
in the edited JSON is not load-bearing as long as the file loads.
A full Form-A re-author is **not** used for the existing corpus (it would
risk flattening the deliberate `borrow` annotations to `own`). It *is* the
path for the two NEW examples below, where the `.ail` is authored fresh and
`ail parse <f>.ail > <f>.ail.json` produces canonical JSON. Both paths sit
on the canonical-authoring-form machinery the round-trip invariant protects
(`0009-roundtrip-invariant.md`), so neither can silently drift.
### Mode-annotations section (spec.md §4) — rewrite
`master/examples/param_modes_all.ail.json` currently defines `f_implicit`,
`f_own`, `f_borrow` and its doc-strings explain `ParamMode::Implicit` as the
"legacy default". `Implicit` is gone — **and** a second drift surfaced under
the parse-gate (2026-06-02): the typechecker now rejects `(borrow V)` over an
unboxed value type with `borrow-over-value` (`crates/ailang-check/src/lib.rs`
~813; unboxed = `Int`/`Bool`/`Float`/`Unit`, `linearity.rs:175`). The old
`f_borrow` borrows over `Int` — illegal today. `borrow` is only legal over a
**boxed** type (an ADT).
So the rewrite cannot be "two Int functions". It must show the mode set *and*
the value/boxed rule: a small boxed ADT, an `own` function over an unboxed
`Int`, and a `borrow` function over the ADT. The example is verified to
check green (see plan Task 2). spec.md §4 prose presents the exhaustive rule:
every parameter carries exactly one of `own`/`borrow`; `own` is mandatory for
unboxed value types (`borrow` over them is a `borrow-over-value` error);
`borrow` is available only for boxed types; `ret_mode` is always `own`. No
mention of a defaulted/omitted mode.
### Two new spec.md sections + examples
The mini-spec must show a foreign author the constructs the language now
has. Add two sections and two examples (AST-source `.ail.json`, regenerated
from the `.ail` below via `ail parse`):
**§ Iteration (`loop` / `recur`)** — example `loop_sum`:
```
(module loop_sum
(fn sum_to
(type (fn-type (params (own (con Int))) (ret (own (con Int)))))
(params n)
(body
(loop (acc (con Int) 0) (i (con Int) 1)
(if (app gt i n)
acc
(recur (app + acc i) (app + i 1)))))))
```
**§ Kernel-extension types (`new`)** — example `new_rawbuf`:
```
(module new_rawbuf
(fn main
(type (fn-type (params) (ret (own (con Unit))) (effects IO)))
(params)
(body
(let b (new RawBuf (con Int) 3)
(app print (app RawBuf.size b))))))
```
Both are reductions of shipped, checked `examples/` fixtures
(`loop_sum_to.ail`, `new_rawbuf_size_only.ail`), so they are known to
parse, check, build, and run under today's toolchain.
### spec_completeness.rs — the one design cut
The completeness test asserts every `Term`/`ParamMode` variant is exercised
by at least one master example. Update it:
- Remove the `ParamMode::Implicit` arm.
- Cover `Loop`, `Recur`, `New` via the two new examples.
- **Allowlist `Intrinsic` OUT**, with an in-code comment stating the reason:
`Intrinsic` is non-authorable (absent from the parser term dispatch;
typecheck enforces kernel-tier-only), so a foreign-author mini-spec must
not show it, and no master example can legally contain it. This mirrors
the harness's existing `OPTIMISATION_ONLY` allowlist precedent — a named,
justified exemption rather than a silent gap.
### Mock fixture migration (harness) — corpus-decoupled, but in scope
`harness/tests/fixtures/mock_full_run.json` is a **separate** program
store from the `master/` corpus: it holds hand-authored *simulated model
output* (string-embedded `.ail.json` programs, one per cohort×task×turn)
that the harness pipeline scores by running `ail check` + `ail build` on
each. Every embedded program is legacy-schema (`grep -c param_modes` → 0),
so under today's mandatory `param_modes` they all fail `ail check`. Two
harness tests depend on this fixture and would break:
- `mock_full_run.rs` asserts `json,t3_main_prints,false,2` (t3 in the json
cohort goes green on **turn 2**), `ail,t1_add_three,false,INF` (runs to
the turn limit, never green), exactly 8 score rows, and `RUN_STATUS=ok`.
The `,2` row breaks under schema-death: t3's turn-2 program no longer
reaches green, so `turns_to_green` flips to `INF`.
- `budget_abort.rs` asserts `RUN_STATUS=budget_exceeded` and ≥1
`budget_abort` row. This depends only on the token economy (the fixture's
`usage` fields, ~1200/turn vs. a 1500 budget), **not** on the check
result, so it is robust to the schema drift — but must be re-verified
green, and the `usage` fields must be left untouched.
**Repair:** migrate the embedded `content` programs to the current schema
(`param_modes`/`ret_mode`), **except** the deliberately content-broken ones
(e.g. t3 json turn 1 is `…"defs":[]` — an intentional first-attempt miss
that must stay broken-by-content). Hard requirements pinned by the
assertions:
- t3_main_prints / json / turn 2 → a valid current-schema program that
passes `ail check` + `ail build` + run, so the `,2` row holds.
- t1_add_three / ail → stays failing to the turn limit, so `INF` holds; its
failure should be **content-driven** (the fixture's intended miss), not a
silent schema-drift artefact — otherwise the fixture stops testing what it
documents.
Leave the `usage` token fields byte-for-byte unchanged. This keeps the mock
an honest simulation of a model authoring against *today's* language, rather
than a fixture that passes only because everything uniformly schema-fails.
`harness/tests/fixtures/check_schema_missing_field.stderr` (open question
raised in recon) is a **no-op**: it pins `missing field ``type```, not
`param_modes`, so it is independent of this drift and stays green.
## Components
- `experiments/2026-05-12-cross-model-authoring/master/examples/*.ail.json`
(13 files) — regenerated with `param_modes`/`ret_mode`.
- `.../master/tasks/*.reference.ail.json` (4 files) — regenerated.
- `.../master/examples/param_modes_all.ail.json` — rewritten (own/borrow only).
- `.../master/examples/loop_sum.ail.json`, `.../new_rawbuf.ail.json` — new.
- `.../master/spec.md` — §4 rewritten; two new sections added; their
`{example: …}` markers wired.
- `.../render/src/*` — only if the renderer needs a new section/example
hook; prefer pure data additions.
- `.../render/tests/spec_completeness.rs` — variant coverage + Intrinsic
allowlist.
- `.../rendered/json.md`, `.../rendered/ail.md` — regenerated output,
re-committed for review.
- `.../harness/tests/fixtures/mock_full_run.json` — embedded `content`
programs migrated to current schema (except the deliberately
content-broken ones); `usage` fields untouched.
## Data flow
1. Re-author each corpus `.ail.json` as `.ail`; `ail parse` → new `.ail.json`;
`ail check` green per file.
2. Add the two new examples + two spec.md sections; rewrite §4.
3. `cargo run --manifest-path .../render/Cargo.toml -- --master … --rendered …`
succeeds; inspect regenerated `rendered/`.
4. `cargo test --manifest-path .../render/Cargo.toml` green
(`example_roundtrip`, `spec_completeness`, `token_balance`, splitter).
5. Migrate `mock_full_run.json` embedded programs (per §Mock fixture
migration); leave `usage` untouched.
6. `cargo test --manifest-path .../harness/Cargo.toml` green (mock E2E
`mock_full_run` with the `false,2` + `INF` rows holding,
`verify_references` driving every reference through the real ail+clang
pipeline, strip_locations, budget_abort).
## Error handling / acceptance
- Per-file bar: `ail check <f>.ail.json` exit 0.
- Renderer bar: clean run, no load crash.
- Suite bar: both crates' `cargo test` green. The harness `verify_references`
test is the end-to-end gate — it compiles and runs every reference solution
through the actual toolchain, so a green there means the references are not
just schema-valid but executable.
- No live IONOS call. No `harness` invocation with `--model` other than `mock`.
## Out of scope
- The recurring-method form (project-local skill vs. light triggered tool).
- Any live IONOS run — gated on explicit per-session consent.
- Multi-subject / token-economy re-measurement (the old `runs/` datasets are
left as historical; they are gitignored raw data, not corpus).
- Rewriting specs 0017/0018.