iter cma.2: harness binary + 4 tasks + reference solutions + 4 integration tests

Sibling standalone Cargo crate `harness/` under
experiments/2026-05-12-cross-model-authoring/ (out-of-workspace
idiom carried forward from cma.1 verbatim). Six modules:
strip_locations (regex pass for form-asymmetric location info,
calibrated against five real `ail check`/`ail parse` stderr
captures), pipeline (subprocess wrapper for parse|check|build +
5s-timeout exec; preflight on ail+clang), ionos (blocking reqwest
client + retry policy per spec), mock (canned-response loader),
scoring (CSV + summary.md), tasks (definition struct + loader).
main.rs ties them into the per-(cohort,task) loop with budget
accounting and per-turn artefact recording.

Four MVP tasks land with reference solutions that compile, build,
and execute green through the actual ail+clang pipeline:
t1_add_three (chained `+` + io/print_int), t2_length (polymorphic
List + recursion), t3_main_prints (minimal IO module), t4_count_zeros
(prelude Eq Int + branched if). Reference solutions stay in canonical
AILang form — param_modes is omitted when every parameter is the
Implicit default, consistent with the existing examples/ corpus.

13/13 tests green: 5 lib unit (strip_locations) + 5 integration
(strip_locations against verbatim captured fixtures) + 1
verify_references (drives every reference through the real
ail+clang pipeline) + 1 mock_full_run (full eight-row sweep with
mixed green-on-turn-2 + turn-limit cycles) + 1 budget_abort
(synthetic budget exhaustion with budget_abort rows + run_status).

Two implementer-phase repairs beyond the plan, both small and
surfaced in the iter journal Concerns:
1. pipeline.rs renames the program file to `<module-name>.ail.json`
   between parse and check because `ail check` enforces filename
   stem == module name (compiler contract the plan did not anticipate).
2. main.rs fills synthetic budget_abort rows for tasks the outer
   loop never reached so scores.csv preserves the expected eight-row
   shape on budget exhaustion.

One plan/text mismatch carried over: README "Total 8 passed" reflects
the plan's four-suite count; actual sweep produces 13. Doc-fix
candidate for cma.3.

cma.3 (live IONOS run + DESIGN.md addendum + roadmap edits) remains
out of scope.
This commit is contained in:
2026-05-12 12:06:34 +02:00
parent 21c2d2cfaa
commit fe1fb6b4f0
32 changed files with 1915 additions and 0 deletions
+212
View File
@@ -0,0 +1,212 @@
# iter cma.2 — Harness binary + four tasks + reference solutions + three integration tests
**Date:** 2026-05-12
**Started from:** 21c2d2cfaa999980f727a069792e3bd073c1f1e7
**Status:** DONE
**Tasks completed:** 13 of 13
## Summary
Stands up the sibling `harness/` standalone Cargo crate under
`experiments/2026-05-12-cross-model-authoring/`, mirroring the
out-of-workspace idiom from cma.1's `render/`. Six modules under
`harness/src/`: `strip_locations` (regex pass for form-asymmetric
location info), `pipeline` (subprocess wrapper around
`ail parse|check|build` plus the model's compiled binary, with a
fail-fast preflight on `ail` + `clang`), `ionos` (blocking reqwest
client with retry policy), `mock` (canned-response loader keyed by
cohort/task/turn), `scoring` (CSV + summary.md writers), `tasks`
(task-definition struct + loader). `main.rs` ties them into the
per-(cohort, task) loop. Four task definitions (`t1_add_three`,
`t2_length`, `t3_main_prints`, `t4_count_zeros`) with reference
solutions that all reach green through the actual ail+clang
pipeline. 13/13 tests pass: 5 inline unit tests under `--lib`
(strip_locations), 5 strip_locations integration tests against
verbatim recon-captured stderr fixtures, 1 verify_references
integration test driving every reference through the real pipeline,
1 mock_full_run end-to-end with eight rows and per-cohort artefact
trees, 1 budget_abort with the harness aborting cleanly under a
1500-token cap.
## Per-task notes
- cma.2.1: Bootstrap harness Cargo project + skeleton — created
`harness/{Cargo.toml,.gitignore,src/lib.rs,src/main.rs}` plus
six stub module files. Empty `[workspace]` table at the top of
Cargo.toml keeps the crate out of the root workspace. Cargo.lock
gitignored per spec. First `cargo build` succeeded with the
expected unused-import warnings on the skeleton.
- cma.2.2: strip_locations module — written verbatim from the
plan with 5 inline unit tests (json_pointer, byte_offset, line/
column, Caused-by chain, passthrough). All 5 green on first run.
- cma.2.3: pipeline module — written verbatim from the plan plus
a small implementer-phase repair: `ail check` enforces filename
stem == module name, so `pipeline::run_pipeline` extracts the
top-level `"name"` field from the JSON via a new
`module_name_from_json` helper and renames the working file to
`<module_name>.ail.json` before invoking `ail check`. Without
this, every reference would have failed at the `ail check` step
with "module name in file does not match expected name from
path". See Concerns.
- cma.2.4: ionos module — written verbatim from the plan; added
`thiserror = "1"` to `[dependencies]`. Compile-time warnings
about three unused things on the `IonosClient` path (Auth, Usage,
`last_err` assignment) are expected and flagged in the plan.
- cma.2.5: mock module — written verbatim; compile clean.
- cma.2.6: scoring module — written verbatim; compile clean.
- cma.2.7: tasks module — written verbatim; compile clean.
- cma.2.8: Author four task definitions + reference solutions —
task.json files written verbatim from the plan text. Reference
`.ail.json` files authored fresh against the cma.1 templates
(hello.ail.json shape for t3; gc_stress.ail.json's polymorphic
`List a` shape for t2 and t4; canonical chained `+` for t1's
three-arg sum; canonical `eq` invocation pattern + branched `if`
for t4's count_zeros). All four references compile clean and
produce the expected stdout. Reference solutions intentionally
omit `param_modes` because every parameter is Implicit and the
canonical AILang form omits the field in that case — consistent
with hello.ail.json, list.ail.json, eq_primitives_smoke.ail.json,
and the explanatory doc in `master/examples/param_modes_all.ail.json`.
See Concerns. Added `tempfile = "3"` to `[dev-dependencies]` for
the `verify_references.rs` integration test. The test runs every
reference through `pipeline::run_pipeline(Cohort::Json, ...)`
end-to-end (ail parse-check-build-execute); passes when
`AIL_BIN` is set to the release binary.
- cma.2.9: Wire main.rs end-to-end — written verbatim from the
plan; added `tempfile = "3"` to `[dependencies]` (not only
dev-deps) because `main.rs::run_one` uses it for per-turn
workdir. Compile clean (modulo the three flagged warnings from
ionos).
- cma.2.10: Five stderr fixtures + strip_locations integration
test — all five fixture files written verbatim from the plan
(which itself captured them from recon's run against current HEAD).
Integration test asserts strip behaviour against each fixture;
5/5 green on first run.
- cma.2.11: mock_full_run integration test + mock_full_run.json
fixture — fixture authored by inlining each
`master/tasks/<id>.reference.ail.json` as the `content` field
for the green-path entries, plus `ail render` for the AILX-cohort
inlines (verbatim AILX serialisations of the same references).
Test runs the harness binary against the fixture; 8 rows total
(4 tasks × 2 cohorts), `(json, t3_main_prints)` reaches green on
turn 2 (matches the plan's intended cycle: broken-then-fixed),
`(ailx, t1_add_three)` runs to the 5-turn limit (`(module garbage)`
repeated). All artefacts (turn_N_program.ext,
turn_N_request.json, turn_N_response.json,
turn_N_check_stderr.txt, turn_N_run_stdout.txt) land in
`per_cohort/<cohort>/<task>/`. 1/1 green.
- cma.2.12: budget_abort integration test — test verbatim from
the plan, plus an implementer-phase repair to `main.rs`: with a
tiny budget (1500), the outer loop broke out after consuming
~2400 tokens across tasks 1+2 (both green on turn 1), but
neither row carried `final_status = budget_abort` because the
inner `run_one` only marks BudgetAbort if the budget is exhausted
*during* the turn loop. The plan's test assertion expects at
least one row with `budget_abort`. Repair: after the outer loop
breaks with `run_status="budget_exceeded"`, fill in the not-yet-run
(cohort, task) pairs with synthetic `BudgetAbort` rows. mock_full_run
remains green (the new code only runs on budget_exceeded).
1/1 green. See Concerns.
- cma.2.13: README update + final sweep — appended "Running the
harness" section to `experiments/.../README.md` verbatim from the
plan. Full `cargo test` sweep: 5 (lib unit, strip_locations) + 5
(integ, strip_locations) + 1 (verify_references) + 1
(mock_full_run) + 1 (budget_abort) = 13/13 green. `git status`
shows the README modification + the new harness/ tree + the new
master/tasks/ files, all unstaged.
## Concerns
- **Pipeline filename-matching: minimal repair landed in `pipeline.rs`,
not flagged in the plan.** `ail check` enforces filename stem ==
module name; the plan's `run_pipeline` writes the model's program
to `prog.{ext}`, which would have been rejected at every `ail
check` invocation. The repair adds a 9-line helper
`module_name_from_json` (reads JSON, returns top-level `name`) plus
14 lines in `run_pipeline` that rename `prog.ail.json` ->
`<module_name>.ail.json` between parse and check. Behaviour
outside that rename is unchanged. The structural alternative
(telling the model to name its module `prog`) would have been
semantically wrong — the task description prompts say "module
named t1_add_three" etc. Recommend a forward-fix sweep in cma.3
if the structural decision (whether the pipeline or the prompt
owns module naming) warrants spec attention.
- **`param_modes` omitted from every reference solution.** The plan's
Step 8.2 text says "mode annotations on every parameter", but the
templates the plan references (hello.ail.json, list.ail.json,
eq_primitives_smoke.ail.json, etc.) all omit `param_modes`, and
the `param_modes_all.ail.json` master example's own doc string
documents "Implicit mode is the legacy default — `param_modes` is
omitted from canonical JSON when every entry is Implicit". The
references stay in canonical form. If the plan's intent was
explicit `["implicit", "implicit", ...]` annotations on every
reference, that is a follow-on amendment.
- **Budget-abort row-filling, minimal repair landed in `main.rs`,
not flagged in the plan.** Without it, Task 12's test fails on
the assertion `expected at least one budget_abort row`. Repair
is gated on `run_status == "budget_exceeded"` so the green path
is unchanged.
- **README "Total 8 passed" text mismatches Step 13.2's "13 passed"
expectation.** The plan's README text (Step 13.1) advertises four
test suites with "Total 8 passed" — counts the four integration
suites but skips the 5 inline unit tests under `--lib`. Step 13.2
expects 13 across `--lib` + 4 integration tests. The README text
is reproduced verbatim per plan; the actual sweep produces 13.
Recommend a docfix in cma.3 ("Total 13 passed across --lib + 4
integration tests"), but the cma.2 plan locked the README copy.
- **`AIL_BIN` env required for `verify_references.rs` and
`mock_full_run.rs` to run from a fresh shell.** These tests shell
out to `ail`; without `ail` on PATH the preflight bails. The
README documents this. CI integration is out of scope.
## Known debt
- The harness has not yet been live-fired against IONOS (deferred
to cma.3 per spec). The retry/backoff path in
`ionos::IonosClient::post` has no test coverage in cma.2 (mock
mode bypasses it entirely).
- Three compile warnings in `ionos.rs` (`Auth` and one `Usage` are
read in tests/other code; `last_err` second assignment is
intentional dead-store for retry-loop symmetry). Not addressed.
## Files touched
Modified (1):
- `experiments/2026-05-12-cross-model-authoring/README.md`
New — harness crate (8 source, 4 integration tests, 6 fixtures, 1
manifest, 1 .gitignore):
- `experiments/2026-05-12-cross-model-authoring/harness/Cargo.toml`
- `experiments/2026-05-12-cross-model-authoring/harness/.gitignore`
- `experiments/2026-05-12-cross-model-authoring/harness/src/lib.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/src/main.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/src/strip_locations.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/src/pipeline.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/src/ionos.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/src/mock.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/src/scoring.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/src/tasks.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/strip_locations.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/verify_references.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/mock_full_run.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/budget_abort.rs`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_unbound_var.stderr`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_type_mismatch.stderr`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_bare_xmod.stderr`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_schema_missing_field.stderr`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/parse_unclosed.stderr`
- `experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/mock_full_run.json`
New — master tasks (4 task.json + 4 reference.ail.json):
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t1_add_three.task.json`
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t1_add_three.reference.ail.json`
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.task.json`
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.reference.ail.json`
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.task.json`
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.reference.ail.json`
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t4_count_zeros.task.json`
- `experiments/2026-05-12-cross-model-authoring/master/tasks/t4_count_zeros.reference.ail.json`
## Stats
bench/orchestrator-stats/2026-05-12-iter-cma.2.json
+1
View File
@@ -21,3 +21,4 @@
- 2026-05-12 — audit-rt: milestone close (Roundtrip Invariant) — architect drift fixed in rt.tidy (3 items: Direction-2 5th test, roadmap P1 removed, wording sync); bench all-green → 2026-05-12-audit-rt.md
- 2026-05-12 — iter boss: `/boss` skill — autonomous-mode discipline (Direction freedom, Notifications, WhatsNew procedure) extracted from CLAUDE.md into a user-invoked skill → 2026-05-12-iter-boss.md
- 2026-05-12 — iter cma.1: master mini-spec + render binary for cross-model authoring experiment (13 fixtures cover 34/34 AST variants, 4 test gates green, rendered/{json,ailx}.md checked in; out-of-workspace nested crate) → 2026-05-12-iter-cma.1.md
- 2026-05-12 — iter cma.2: harness binary + 4 tasks + reference solutions + 4 integration tests (six modules, real-pipeline preflight via verify_references, 13/13 tests green; pipeline.rs renames program file to module-name for ail check) → 2026-05-12-iter-cma.2.md