The single-file ledger had grown to 2968 lines / ~42k tokens, mixing current design law with accreted history: 59 cycle-stamped realization blocks, 18 [HISTORY] passages, 22 supersession markers, and the C10 / C22 / C24 reframe sagas layered several supersessions deep. A code-grounding audit (31 agents, adversarially verified) confirmed 11 defects stated as current truth: stale crate homes from the C28 #288 roster split (cost nodes, PositionManagement, PositionEvent, Session), the renamed InputSpec->PortSpec, the pre-#241 project model in C16 and the open-threads section, a stale HarnessKind retirement deferral in C24, and three C28-internal inconsistencies. New shape, per the ailang precedent: - INDEX.md stays the sole addressable entry point: foundation, external components, a C-id-keyed contract map (one line per contract), and only the genuinely open architectural threads. - contracts/cNN-<slug>.md carries each contract's current truth only: Guarantee / Forbids / Why with ratified refinements integrated, plus a code-anchored Current state. All confirmed defects are fixed here; crate anchors were re-verified against the tree. - contracts/cNN-<slug>.history.md (18 sidecars) and INDEX.history.md preserve every superseded block verbatim, stamps and issue refs intact, under a frozen-record banner. Nothing was deleted: superseded design intent remains an addressable working-tree artifact, off the per-cycle audit walk. - Ledger discipline is now stated in INDEX.md: live files are edited in place at cycle close, superseded text moves verbatim to the sidecar, and a supersession marker in a live file is itself an audit finding. Every contract file was verified against its old text by an independent zero-loss pass (statement-by-statement) plus a code-accuracy spot check; C-ids and contract titles are unchanged, so existing C-id citations in code, tests, and issues resolve as before.
6.3 KiB
C20 — Strategy ↔ harness; the harness is the root sim graph
Guarantee. A strategy is a reusable composite-node blueprint (C9): broker-,
data-, and viz-independent, with inputs declared as named roles (symbol-agnostic
where possible) and the bias stream (C10) as output. A harness (the
experimental setup) is the root sim graph — sources bound to the strategy's
input roles + the strategy + attached broker node(s) + sinks — and is itself
produced by the bootstrap (C19). A harness instance is C1's disjoint unit (the
root scope). The harness has two kinds of parameterization: structural axes
(which strategy, which instrument(s), which broker(s), which window, which risk
regime) whose variation selects different instances — the experiment matrix
(the risk regime is the fourth axis, #210, carried as CampaignDoc.risk;
kept-separate, compared-not-selected, see C10) — and tuning params (the
strategy's numeric params) swept within a fixed structure (Fork A). The same
strategy blueprint is reused across backtest, sweep, visual workspaces, and the
frozen live bot — each a different harness. Both strategy and harness/experiment
are authored in Rust via builder APIs (C17); the experiment matrix is ordinary
Rust control flow (loops/conditionals), not a config schema. Ontologically, a node
(incl. a strategy composite) is an open fragment — free input roles + ≤1 output
(C8/C9) — that does not run alone; a harness is the closed root graph: a
strategy with its input roles bound to sources and its output terminated in
broker/sink nodes, under a clock. A harness is therefore not a node (no free
inputs, no output; it does not fit eval) — it is the closure that runs, C1's
disjoint unit / the root scope. Harnesses do not nest as nodes; the World (C21)
orchestrates them as objects.
Forbids. Embedding data sources / brokers / sinks inside a strategy; a declarative experiment mini-DSL (logic is Rust — C17); modelling the harness as a node (it is the closed root scope, not an open composable node). The mini-DSL prohibition is scoped by the #188 role-model pass: it forbids an open, logic-bearing experiment language (the RustAst trap), not the closed-vocabulary campaign document (C25/C18) that carries persisted experiment intent — data windows × strategy ids × param axes × process ref, under the total, declarative P1 construct tier (bounded axes, gates, ladders; no variables, no general recursion, no unbounded iteration — invariant 5's no-free-feedback move applied one level up; #188/#189). A generator that enumerates structural variants may still be plain Rust; what it yields, and what a campaign declares, is data. Genuinely new logic (metrics, analysis blocks) escalates to a new Rust block, never to a freetext hole in the artifact.
Why. Reusability needs the strategy to be a context-free blueprint that many harnesses embed. Modelling the harness as a root graph keeps it within the one Node/graph abstraction (C9) and makes "10 strategies in one environment" and "one strategy × N instruments" plain nested loops over the structural axes. Rust authoring (not config) preserves full programmatic power — conditional/adaptive matrices, generated axes, custom wiring — and avoids re-introducing the DSL trap C17 rejects.
Current state
The shippable form is the Composite blueprint. A hand-wired FlatGraph is
not a shippable strategy — it carries no param_space(), so the World families
(sweep / walk_forward / compare, C21) cannot consume it without a hand re-author.
The canonical shippable form is the Composite blueprint (the authoring source
level, C9/C19); the FlatGraph is only its compiled substrate (C23). This is
concrete in the GER40 session-breakout example (ger40_breakout_blueprint,
crates/aura-ingest/examples/shared/breakout_real.rs), whose param_space() is
exactly its tuning knobs and whose bar period is a construction argument binding
Resample + Session together — a structural matrix axis (C12: a different period is a
different strategy, not a sweep point), never a param_space entry, so a sweep
cannot desync the two clocks. Structural constants (e.g. a delay.lag, C8) are
bound out of the space.
Experiment-matrix members are topology-data. The "ordinary Rust control flow" clause holds for the generator — the loop / conditional that enumerates structural variants may stay Rust (C17). But each member it yields is a topology-data value (C24), not a hand-coded blueprint nor engine-baked source: a structural axis selects among data blueprints the World constructs, mutates, and serializes, which is what makes structural variation first-class and searchable rather than a hand-enumerated menu. Persisted experiment intent is the campaign document (C25/C18).
Pre-C24 scaffolding retired. The pre-C24 scaffolding — HarnessKind and the
per-strategy *_sweep_family functions — is gone. The family builders are now
generic and blueprint-driven: blueprint_sweep_family, blueprint_walkforward_family,
blueprint_mc_family (and blueprint_sweep_over) in
crates/aura-runner/src/family.rs. The R-evaluator scaffold wrap_r
(defined in crates/aura-runner/src/member.rs; imported and called from
runner.rs) survives; its last hard data weld — the
hard-wired price←close role and the M1Field::Close-only open sites — is retired
(a strategy's input roles now bind archive columns by name, C26), and wrap_r's
remaining R-scaffolding retirement is tracked at #159.
See also
- C9 — the strategy as a composite blueprint.
- C10 — the bias output and the risk-regime axis.
- C17 — Rust authoring and the DSL prohibition.
- C19 — the bootstrap that produces the harness.
- C21 — the World orchestrates harnesses as objects.
- C23 — the
FlatGraphcompiled substrate. - C24 — experiment-matrix members as topology data.
- C25, C18 — the campaign document / run registry.
- C26 — roles bind archive columns by name.
History: c20-strategy-harness.history.md