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.
12 KiB
iter cma.2 — Harness binary + four tasks + reference solutions + three integration tests
Date: 2026-05-12
Started from: 21c2d2cfaa
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. Firstcargo buildsucceeded 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 checkenforces filename stem == module name, sopipeline::run_pipelineextracts the top-level"name"field from the JSON via a newmodule_name_from_jsonhelper and renames the working file to<module_name>.ail.jsonbefore invokingail check. Without this, every reference would have failed at theail checkstep 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 theIonosClientpath (Auth, Usage,last_errassignment) 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.jsonfiles authored fresh against the cma.1 templates (hello.ail.json shape for t3; gc_stress.ail.json's polymorphicList ashape for t2 and t4; canonical chained+for t1's three-arg sum; canonicaleqinvocation pattern + branchediffor t4's count_zeros). All four references compile clean and produce the expected stdout. Reference solutions intentionally omitparam_modesbecause 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 inmaster/examples/param_modes_all.ail.json. See Concerns. Addedtempfile = "3"to[dev-dependencies]for theverify_references.rsintegration test. The test runs every reference throughpipeline::run_pipeline(Cohort::Json, ...)end-to-end (ail parse-check-build-execute); passes whenAIL_BINis 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) becausemain.rs::run_oneuses 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.jsonas thecontentfield for the green-path entries, plusail renderfor 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 inper_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 carriedfinal_status = budget_abortbecause the innerrun_oneonly marks BudgetAbort if the budget is exhausted during the turn loop. The plan's test assertion expects at least one row withbudget_abort. Repair: after the outer loop breaks withrun_status="budget_exceeded", fill in the not-yet-run (cohort, task) pairs with syntheticBudgetAbortrows. 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.mdverbatim from the plan. Fullcargo testsweep: 5 (lib unit, strip_locations) + 5 (integ, strip_locations) + 1 (verify_references) + 1 (mock_full_run) + 1 (budget_abort) = 13/13 green.git statusshows 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 checkenforces filename stem == module name; the plan'srun_pipelinewrites the model's program toprog.{ext}, which would have been rejected at everyail checkinvocation. The repair adds a 9-line helpermodule_name_from_json(reads JSON, returns top-levelname) plus 14 lines inrun_pipelinethat renameprog.ail.json-><module_name>.ail.jsonbetween parse and check. Behaviour outside that rename is unchanged. The structural alternative (telling the model to name its moduleprog) 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_modesomitted 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 omitparam_modes, and theparam_modes_all.ail.jsonmaster example's own doc string documents "Implicit mode is the legacy default —param_modesis 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 assertionexpected at least one budget_abort row. Repair is gated onrun_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_BINenv required forverify_references.rsandmock_full_run.rsto run from a fresh shell. These tests shell out toail; withoutailon 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::posthas no test coverage in cma.2 (mock mode bypasses it entirely). - Three compile warnings in
ionos.rs(Authand oneUsageare read in tests/other code;last_errsecond 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.tomlexperiments/2026-05-12-cross-model-authoring/harness/.gitignoreexperiments/2026-05-12-cross-model-authoring/harness/src/lib.rsexperiments/2026-05-12-cross-model-authoring/harness/src/main.rsexperiments/2026-05-12-cross-model-authoring/harness/src/strip_locations.rsexperiments/2026-05-12-cross-model-authoring/harness/src/pipeline.rsexperiments/2026-05-12-cross-model-authoring/harness/src/ionos.rsexperiments/2026-05-12-cross-model-authoring/harness/src/mock.rsexperiments/2026-05-12-cross-model-authoring/harness/src/scoring.rsexperiments/2026-05-12-cross-model-authoring/harness/src/tasks.rsexperiments/2026-05-12-cross-model-authoring/harness/tests/strip_locations.rsexperiments/2026-05-12-cross-model-authoring/harness/tests/verify_references.rsexperiments/2026-05-12-cross-model-authoring/harness/tests/mock_full_run.rsexperiments/2026-05-12-cross-model-authoring/harness/tests/budget_abort.rsexperiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_unbound_var.stderrexperiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_type_mismatch.stderrexperiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_bare_xmod.stderrexperiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/check_schema_missing_field.stderrexperiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/parse_unclosed.stderrexperiments/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.jsonexperiments/2026-05-12-cross-model-authoring/master/tasks/t1_add_three.reference.ail.jsonexperiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.task.jsonexperiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.reference.ail.jsonexperiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.task.jsonexperiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.reference.ail.jsonexperiments/2026-05-12-cross-model-authoring/master/tasks/t4_count_zeros.task.jsonexperiments/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