Bind strategy input roles to sources as authorable data — retire wrap_r's single-price weld (C20/C24) #231

Closed
opened 2026-07-10 17:14:31 +02:00 by Brummel · 4 comments
Owner

Every verb path (run / sweep / walkforward / mc / generalize / campaign member) wraps the strategy blueprint in the hard-wired wrap_r scaffolding (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:

  • Composite carries input_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).
  • Op-scripts may declare N source/input roles; introspect --unwired lists every open slot (crates/aura-engine/src/construction.rs:143-151 and 370-380).
  • The engine k-way-merges N sources per harness (crates/aura-engine/src/harness.rs:365-416; invariant 3), with an ohlcv_sources() test fixture.
  • open_ohlc opens 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.

  • binding vocabulary designed and recorded as a ledger amendment (Blockly-litmus-clean)
  • multi-role real-data source opening on the verb/campaign path
  • single-role blueprints keep working unchanged (default binding price ← close)
  • an OHLC-consuming strategy runs end-to-end on real data
Every verb path (run / sweep / walkforward / mc / generalize / campaign member) wraps the strategy blueprint in the hard-wired `wrap_r` scaffolding (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: - `Composite` carries `input_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). - Op-scripts may declare N source/input roles; `introspect --unwired` lists every open slot (crates/aura-engine/src/construction.rs:143-151 and 370-380). - The engine k-way-merges N sources per harness (crates/aura-engine/src/harness.rs:365-416; invariant 3), with an `ohlcv_sources()` test fixture. - `open_ohlc` opens 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. - [ ] binding vocabulary designed and recorded as a ledger amendment (Blockly-litmus-clean) - [ ] multi-role real-data source opening on the verb/campaign path - [ ] single-role blueprints keep working unchanged (default binding price ← close) - [ ] an OHLC-consuming strategy runs end-to-end on real data
Brummel added this to the Real-project readiness — role-complete research loop (no freeze) milestone 2026-07-10 17:14:31 +02:00
Brummel added the feature label 2026-07-10 17:14:31 +02:00
Author
Owner

Sibling defects living in the same wrap_r scaffolding, 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.

Sibling defects living in the same `wrap_r` scaffolding, 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.
Author
Owner

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.

## 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.
Author
Owner

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.

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.
Author
Owner

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.

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.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#231