Bind strategy input roles to sources as authorable data — retire wrap_r's single-price weld (C20/C24) #231
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Every verb path (run / sweep / walkforward / mc / generalize / campaign member) wraps the strategy blueprint in the hard-wired
wrap_rscaffolding (crates/aura-cli/src/main.rs:1256-1324). The scaffolding declares exactly one input role —g.source_role("price", F64)at main.rs:1297, a bare string literal repeated at each consumer port — and the real-data path opens exactly one column:M1FieldSource::open(…, M1Field::Close)(main.rs:545-566). A strategy therefore never sees more than a single close-price column, which structurally excludes OHLC-consuming strategies (true ATR channels, range logic) regardless of whether the node is std or project-authored.The narrowing is CLI scaffolding, not substrate:
Compositecarriesinput_roles: Vec<Role>with no arity cap (crates/aura-engine/src/blueprint.rs:174-175); C8's "at most one" applies to the output port, which itself carries 1..K fields (#40).introspect --unwiredlists every open slot (crates/aura-engine/src/construction.rs:143-151 and 370-380).ohlcv_sources()test fixture.open_ohlcopens open/high/low/close as four column sources in the C4 merge order (crates/aura-ingest/src/lib.rs:315-338, #92) — used only by tests/examples, by no CLI verb.The design ledger already ratifies the end state: C20 defines a harness as "the closed root graph: a strategy with its input roles bound to sources and its output terminated in broker/sink nodes", and C24 (topology as a serializable, World-owned data value) names the hard-wired aura-cli harnesses "pre-C24 scaffolding … retired as this lands" (docs/design/INDEX.md, C20/C24 entries). Missing is the binding as data: an authorable, closed-vocabulary declaration of which archive columns/symbols bind to which strategy input roles — in the blueprint or the campaign document's data section (which already carries instruments × windows) — instead of the literal in wrap_r.
Role framing (C25): this sits on the role-6a/6b boundary — 6a (strategy designer) declares the roles a topology consumes, 6b (campaign designer) binds them to data. The vocabulary must pass the Blockly litmus test (typed slots, no free-form holes), per the C25 acceptance criterion.
This is also #159's remaining substance: its originally named targets (HarnessKind, run_stage1_r, the *_sweep_family functions) are already deleted; wrap_r is the last fixture-era harness organ.
Sibling defects living in the same
wrap_rscaffolding, tracked separately: #232 (real-data members run at the synthetic pip while the manifest stamps the sidecar pip), #233 (reproduce re-runs members under the default stop regime), #234 (the cost graph lost its last production caller, net-R unreachable). The scaffolding retirement is complete only when each of these has its fix home in the binding vocabulary or is closed on its own path.Design reconciliation (specify)
Spec: harness-input-binding. The binding-home fork below is posed open by this issue's body ("in the blueprint or the campaign document's data section"); this records its resolution.
Fork: where the column-to-role binding lives -> name-driven default + explicit campaign override (a synthesis, not either pure option).
Basis: derived. (a) Default: a closed column vocabulary — open, high, low, close, spread, volume, with "price" as the backward-compatible alias of close — makes a root role's NAME its binding: role "high" binds the cell instrument's High column. Grounds: the roles a strategy consumes are topology (C25 6a), and column names are exactly the symbol-agnostic form C20 asks of roles (the instrument stays a campaign data axis); the working multi-role proof (crates/aura-ingest/examples/shared/breakout_real.rs, roles open/high/low/close) already follows this naming; zero format change means the entire content-id/golden pin surface (blueprint_serde.rs:332 golden, the research_docs/cli_run content-id pins) stays byte-stable. (b) Override: the campaign DataSection gains an ADDITIVE bindings block (role -> column; serde default + skip-if-empty, the existing description/risk precedent, so binding-less docs keep their content ids) that overrides the name default per campaign — preserving the 6b capability the issue's own framing names ("6b binds them to data"): rebinding a stored blueprint without editing its identity. (c) An adversarial scope-fork review (2026-07-10) refuted PURE name-driven on two grounds — the eliminated 6b rebind, and ledger-admitted roles outside any column vocabulary (C20 "symbol-agnostic where possible"; invariant 6's recorded news-bias streams, e.g. a future "sentiment" role) — the synthesis answers the first now and reserves the second as the amendment's named extension point (the binding value-space grows additively once recorded non-price sources exist at the Source seam); a role neither vocabulary-named nor explicitly bound refuses with both remedies named, the honest-refusal idiom.
Fork: what the broker and risk executor consume under multi-column strategies -> always close.
Basis: derived. The broker marks fills and the stop measures risk at the close today; nothing in the sources asks to change fill semantics, and a strategy-dependent mark price would make R depend on which columns the signal happens to read. The close source is opened once and fanned (the engine feeds one role into several ports).
Fork: multi-column strategies on synthetic data -> honest refusal.
Basis: derived. The synthetic walk generates one close series; fabricating open/high/low from it would invent data semantics no source backs. The established "not yet available on this path" refusal (the #168/#215 pattern) names the real-data remedy instead.
Fork: role-declaration/merge order -> fixed canonical column order (open, high, low, close, spread, volume), filtered to the consumed set.
Basis: derived. Root-role declaration order determines source lowering (blueprint.rs compile_with_cells) and the C4 merge tie-break; the canonical order is open_ohlc's already-vetted #92 contract, extended.
Spec auto-signed (2026-07-10): the spec for the input-role binding (docs/specs/harness-input-binding.md, a git-ignored working file) was signed autonomously — the signature is an independent fresh-context grounding-check PASS (11 load-bearing assumptions about current codebase behaviour, each ratified against a currently-green test; one earlier BLOCK on the validation-placement claim was repaired by correcting the spec to the actual two-tier validate_campaign / validate_campaign_refs split and re-checked). No human signed. The design decisions the spec encodes are the four forks recorded in the reconciliation comment of 2026-07-10 above (name-driven default + campaign override; broker/executor stay close-fed; honest refusal on synthetic multi-column; canonical column order). Planning proceeds from this spec.
Shipped on the branch, five commits:
595c98b(binding module: closed column vocabulary, ResolvedBinding, refusal prose, 12 unit pins + the generalized aura-ingest column openers),99a7f66(wrap_r declares its root roles from the resolved binding — behaviour-preserving, entire suite green unchanged as the gate),1d14ebc(all six Close-only open sites route through the resolved column set; synthetic multi-column refusal; engine OHLC e2e over inline sources pinning the merge order),43b1c7f(campaign data.bindings — the rebind seam, serde-additive with content-id stability pinned, two-tier validation, archive-gated price->open override e2e yielding different realized metrics),7748b53(the r_channel Donchian example pair + run/sweep/reproduce e2e on GER40 + ledger amendment C26 with the C20/C24 annotations).All four acceptance boxes are met on the branch: the binding vocabulary is recorded as C26 (Blockly-litmus-clean, typed slots, no free-form holes, with the recorded-non-price-source extension point reserved at the Source seam); multi-role real-data opening works on run/sweep/walkforward/mc/generalize/campaign/trace-re-run; single-role blueprints run unchanged (every golden/content-id pin byte-stable, full suite green after every task); an OHLC-consuming strategy runs end-to-end on real data. The spec was auto-signed on a grounding-check PASS (comment of 2026-07-10 above); the plan executed in five reviewed slices, each independently quality-reviewed. Closes via commit on merge.