Files
Aura/docs/design/contracts/c20-strategy-harness.md
T
claude 8688a60ded docs(ledger): split the design ledger into an INDEX map, per-contract live files, and history sidecars
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.
2026-07-21 16:40:36 +02:00

6.3 KiB
Raw Blame History

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 FlatGraph compiled 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