# 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](c09-fractal-composition.md) — the strategy as a composite blueprint. - [C10](c10-bias-r-cost.md) — the bias output and the risk-regime axis. - [C17](c17-authoring-surface.md) — Rust authoring and the DSL prohibition. - [C19](c19-bootstrap.md) — the bootstrap that produces the harness. - [C21](c21-world.md) — the World orchestrates harnesses as objects. - [C23](c23-graph-compilation.md) — the `FlatGraph` compiled substrate. - [C24](c24-blueprint-data.md) — experiment-matrix members as topology data. - [C25](c25-role-model.md), [C18](c18-registry.md) — the campaign document / run registry. - [C26](c26-input-binding.md) — roles bind archive columns by name. > History: [c20-strategy-harness.history.md](c20-strategy-harness.history.md)