Verdict: the 0106 F7 wire is closed — campaign+process documents drive aura campaign run through sweep -> gate -> walk_forward over the real GER40 archive, realization recorded as a thin family-linking CampaignRunRecord, bit-identical on re-run and across file/id addressing (C1). 0106 F1/F5 on-ramp gaps confirmed repaired; the honest-refusal surface (v1 boundary, empty gate exit 0 + truncation, plateau/deflate composition, stale wf vocabulary, bad targets, persist_taps loud deferral) exercised and clean. 0 bugs. Findings triaged: F5 -> #202 (op-script vs envelope shape on the on-ramp verbs), F6 -> #194 comment (content: prefix trap re-confirmed, now spans graph register), F7 -> #203 (open-param coverage at validate; raw-vs-wrapped path prose), F8 -> RED-first fix follows this commit (SpanTooShort Debug leak), F9 -> #197 comment (rankable/per-member roster split undiscoverable), F10 -> ratified store-before-gate on #198 (derived: consistent with campaign register's intrinsic-only gate; content-addressed store is an idempotent intent archive). Orthogonal: #204 (aura new nested-.git gitlink hazard). refs #198
13 KiB
Fieldtest — cycle 0107 — campaign executor + blueprint on-ramp — 2026-07-03
Role: downstream consumer (methodology / campaign designer), public interface
only (ledger C16/C18/C20/C24/C25, glossary, CLI --help + introspection verbs,
the 0106 fieldtest corpus as shape hints). No crates/ source read. Binary
built from HEAD (cargo build --workspace, target/debug/aura, commit
428f83c). Real data: scaffolded demo_project/ (aura new), cargo build
for the dylib, ran the executor over the local GER40 2024-09 Pepperstone
archive.
Fixtures (this directory)
c0107_1_smacross.json— SMA-cross op-script, lengths bound (content id573262…)c0107_1_smacross_open.json/_open_built.json— lengths unbound (sweepable), op-script + itsgraph buildenvelope (content id597d71…)c0107_2_process.json— sweep(sqn,argmax,deflate) → gate(expectancy_r>0) → walk_forward(rolling, ms lengths) (content id5a88…)c0107_2_campaign.json— campaign missing abias.scaleaxis (validates, then fails run)c0107_2_campaign_full.json— the runnable campaign (all 3 open params covered), GER40, 2024-09c0107_2_realization_campaign_runs.jsonl— the recorded realization (3 identical runs + 1 gate-all)c0107_3_process_{mc,gateall,badmetric}.json+c0107_3_campaign_*.json— refusal probesc0107_4_process_{plateau_wf,deflate_plateau,wf_toobig}.json+c0107_4_campaign_*.json+c0107_4_campaign_prefixref.json— trip-up probesdemo_project/—aura newscaffold + all docs (itsruns/store is project-gitignored; the realization is copied out to the corpus root as evidence)
Findings
F1 [working] The full loop closes — 0106 F7 is answered
aura campaign run c0107_2_campaign_full.json (file = register-then-run) and
aura campaign run 533aed18… (id) both execute the cell
GER40 × [2024-09-01,10-01] through std::sweep → std::gate → std::walk_forward over real data (exit 0). The gate expectancy_r > 0 kept
survivors [0,1,2] (dropped the losing fast=4/slow=16 member,
expectancy_r=-0.022); the sweep argmax winner was ordinal 0 (sqn=1.054);
walk-forward rolled 2 OOS folds re-sweeping only the survivor points and
stamped manifest.selection on the OOS winners. Emit honored: family_table +
selection_report lines, always the campaign_run record. The stored
CampaignRunRecord (runs/campaign_runs.jsonl) links
{campaign, process, run, seed, cells[{stages: family_id / survivor_ordinals / selection}]} over untouched families.jsonl records, run-counted per campaign
id (3 successful runs → run 0/1/2). Intent → execution → recorded realization
is one continuous, reproducible path. → carry-on.
F2 [working] Determinism is bit-identical across re-run and addressing mode
Two file runs and one id-addressed run of the same campaign are
byte-identical after normalizing only the per-campaign run counter in
family ids (diff clean on both pairs). Deflation nulls seed from the doc
seed=42 (overfit_probability, deflated_score reproduce). Realization is a
pure function of doc + stores + data, as C1 promises. → carry-on.
F3 [working] 0106 F1 closed — the document envelope is now introspectable
aura campaign introspect --unwired <bare {}> enumerates the whole envelope,
including the previously-invisible seed:
open slot: format_version (required, must be 1)
open slot: kind (required, must be "campaign")
open slot: name / data / strategies / process.ref / seed / presentation …
process introspect --unwired {} likewise names
format_version/kind/name/pipeline. The field names (data, strategies,
process.ref, presentation) that 0106 F1 could not map are now spelled out.
→ carry-on.
F4 [working] The honest-refusal surface is loud, precise, and correctly disposed
All probed refusals were clean, actionable prose with the right exit code:
- v1 boundary:
process stage 1 (std::monte_carlo) is not executable in v1; executable pipeline shape: std::sweep [std::gate]* [std::walk_forward](exit 1).std::generalizeis symmetric. - gate-everything-out (
expectancy_r > 100): exit 0,family_table+ sweepselection_reportstill emitted, walk-forward truncated, loud stderrcell …: gate at stage 1 left no survivors; cell realization truncated. plateau:meaninside walk_forward:walk_forward cannot use a plateau select (a gated survivor subset has no parameter lattice)(exit 1).deflate:true+ non-argmax:sweep deflate: true composes only with select "argmax"(exit 1).- old 0106 wf vocab (
folds):block std::walk_forward: unknown slot "folds"(exit 1) — the 0107 machinery-true vocabulary correction is enforced. - bogus file / unknown 64-hex id / uppercase-hex / outside-project: each exit 1
with a precise message (
neither a readable .json file nor a 64-hex content id/no campaign <id> in the project store/campaign run needs a project: … no Aura.toml found up from <cwd>). persist_tapsdeferred LOUDLY on stderr before execution (persist_taps not yet honored (N tap(s) ignored)), the 0106 F7 lesson honored. → carry-on.
F5 [friction] The on-ramp rejects the op-script; the graph build bridge is undocumented
The corpus's canonical strategy form is an op-script (c0106_2_strategy.json,
c0107_1_*). aura graph introspect --content-id FILE accepts it
(shape-discriminated) — but aura graph register FILE and aura graph introspect --params FILE reject it with a raw serde message:
aura: blueprint document is not valid JSON: invalid type: map, expected u32 at line 2 column 2
Only the envelope emitted by aura graph build < op-script > envelope.json is
accepted. Nothing in --help, the glossary blueprint entry, or the error
names this graph build step, and the three on-ramp verbs disagree on which
shapes they accept. (Once bridged, the content id is shape-invariant —
op-script and envelope both hash to 573262…, so register and a ref computed
from the op-script agree; this part is correct.)
Repro: aura graph register c0107_1_smacross_open.json.
→ plan: shape-discriminate the op-list in register/--params (as
--content-id already does), or make the error name the graph build bridge.
F6 [friction] The content: prefix trap persists and the on-ramp emits the wrong-to-paste form
graph|process|campaign register and introspect --content-id all print
content:<hash>, but every ref wants the bare hash. A campaign whose
process.ref carries the copied content:5a88… form passes intrinsic
validation and then fails referential:
aura: campaign references do not resolve:
process content:5a88…6ea not found in the project store
The error now echoes the offending prefixed ref (an improvement over 0106 F3)
but never says "strip the content: prefix", and register still prints
exactly the form that breaks. This directly taxes the on-ramp the cycle set out
to smooth.
Repro: aura campaign validate c0107_4_campaign_prefixref.json.
→ plan: one bare display form across verbs, or accept/strip the content:
prefix in refs (carried from 0106 F3, still open: #194).
F7 [friction] campaign validate passes but campaign run aborts when an open param is uncovered
Referential validation reports all references resolve, axes are in the param space (exit 0), but it only checks that each axis name is in param_space
— not that every open param is covered by an axis. The open blueprint has
three open params (fast.length, slow.length, bias.scale); a campaign
sweeping only the two lengths validates, then aborts at member-bind:
aura: a member failed to bind: open param "graph.bias.scale" of strategy 597d71… is bound by no campaign axis
The executor's "every open knob required, no default" rule (documented for
sweep/walkforward in the glossary) is not mirrored in the validator, so
"valid" ≠ "runnable". (Secondary: the bind error uses the wrapped
graph.bias.scale path while --params and the axis use the raw bias.scale
— two namespaces for one knob.)
Repro: aura campaign validate c0107_2_campaign.json (passes) then
aura campaign run c0107_2_campaign.json (aborts).
→ plan: extend the referential tier to require full open-param coverage,
surfacing the missing knob at validate time; reconcile the raw-vs-wrapped param
path in the bind message.
F8 [friction] The walk-forward window-fit refusal leaks a raw Rust Debug struct
A walk_forward whose in_sample_ms exceeds the campaign window refuses
correctly, but in internal {:?} form, unlike its clean sibling refusals:
aura: process stage 1: walk_forward windows do not fit the campaign window:
SpanTooShort { span: (Timestamp(1725148800000), Timestamp(1727740800000)), need: 315964799999 }
The SpanTooShort {…} struct, the Timestamp() wrapper, and the
unit-unlabeled need (ms) break the "Debug-free house seam" the cycle
otherwise maintains, and a user cannot easily see that need is
in_sample_ms + out_of_sample_ms in ms against the window span.
Repro: aura campaign run c0107_4_campaign_wf_toobig.json.
→ debug (RED-pin the prose form).
F9 [doc-gap] The rankable / gate metric roster is enumerable by no verb, nor by the refusal
Block hints say metric name (see metric_vocabulary), but no verb lists it
(--vocabulary is the block list; --block metric_vocabulary → unknown block). process validate accepts any string (profit_factor passes
intrinsic), and the run-time refusal names no roster:
aura: process stage 0: metric "profit_factor" is not rankable (winner selection needs one of the registry's rankable metrics)
profit_factor is itself emitted in every member's metrics block, so the
exists-but-not-rankable split is invisible. A campaign author must guess the
sweep/gate/wf metric names (0106 F6, still open: #197).
→ tighten docs: expose the rankable + gate metric rosters via process introspect (or enumerate them in the refusal).
F10 [spec_gap] Register-then-run stores the campaign before the gate — unrunnable docs accumulate
Every campaign run <file> issued left a runs/campaigns/<id>.json (9
stored), including campaigns that then failed the referential gate
(prefixref) or preflight (mc/badmetric/plateauwf/deflateplateau/
wf_toobig). The store thus accumulates never-runnable and
referentially-broken documents, with no cleanup. The ledger is silent on
whether the file sugar should gate before storing, and whether the
content-addressed store should hold referentially-broken docs.
→ ratify (accept intrinsic-keyed store as-is) or tighten (gate before storing).
Verdict
Yes — the campaign+process pair now delivers the full loop the 0106 verdict said was one wire short. F7 (0106) is closed end to end: a campaign document
- its referenced process document drive
aura campaign runthroughsweep → gate → walk_forwardover the real GER40 archive, and the run's realization is recorded as a thin, family-linkingCampaignRunRecordthat is content-addressed, run-counted, and bit-identical on re-run — persisted intent finally has a consumer, and every id, axis, gate, and selection earns its reason to exist. The authoring on-ramp that 0106 flagged is substantially repaired too:graph register+introspect --params(file and store-id) +--content-idfile mode give a blueprint ref a resolvable path (F5-0106), and--unwired {}makes the whole envelope,seedincluded, derivable (F1-0106). The honest-refusal surface is a genuine strength — v1 boundary, empty gate (exit 0 + truncation), plateau/deflate composition, stale wf vocabulary, and every bad target are refused loudly, precisely, and with the correct disposition.
What remains is friction concentrated at the first-author edge, not the
loop: the on-ramp verbs disagree on op-script vs envelope shape and hide the
graph build bridge (F5); the content: prefix that every
register/--content-id prints is the exact form that silently breaks a ref
(F6); campaign validate blesses a campaign the executor then rejects for an
uncovered open param (F7); one preflight fault leaks a raw Debug struct
(F8); and the metric roster a process must name is still discoverable nowhere
(F9). None block the loop; all tax the newcomer. One spec silence to settle:
whether register-then-run should store before or after the gate (F10).
Counts: working 4 (F1–F4), friction 4 (F5–F8), doc-gap 1 (F9), spec-gap 1 (F10), bugs 0.
Orthogonal concern (out of 0107 scope)
aura new (cycle 0103) scaffolds a nested .git repo inside the project
directory — a gitlink/submodule hazard when the scaffold is embedded in the
tracked fieldtests/ corpus. demo_project/.git (and the large
demo_project/target/) were removed so the corpus commits cleanly. Worth a
scaffold-side note (--no-git or skip when nested under a repo).