Files
Aura/docs/design/contracts/c26-input-binding.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

74 lines
4.6 KiB
Markdown

# C26 — Harness input binding: role names bind archive columns
**Guarantee.** A strategy blueprint declares WHICH data it consumes as the NAMES
of its root input roles: a role whose name is in the **closed column vocabulary**
`open`, `high`, `low`, `close`, `spread`, `volume`, plus `price` as the
backward-compatible alias of `close` — binds by default to that column of the
campaign cell's (or run invocation's) instrument. A campaign document may
**override** the name default per role via the additive `data.bindings` block
(`role name → column name`; serde `default` + skip-if-empty, so binding-less
documents keep their content ids) — the 6b rebind seam: the blueprint's content
identity never changes.
Resolution (`aura-runner`'s `binding` module, `resolve_binding`) produces ONE
ordered plan consumed by BOTH halves of the old weld: the columns are opened
(`aura_ingest::open_columns`, the generalization of `open_ohlc`/#92) and the
wrapped root roles declared (`aura-runner::member`'s `wrap_r`) in the **same
canonical order** — `M1Field` declaration order (open, high, low, close, spread,
volume), filtered to the consumed set — which is the [C4](c04-cycle-granularity.md)
merge tie-break order, so role *i* receives source *i* by construction. A close
column is always part of the plan (shared when the strategy consumes
`close`/`price`, else opened for the broker/executor pair alone).
The vocabulary is **Blockly-litmus-clean** ([C25](c25-role-model.md)): role-name
slots draw from a closed enum, the bindings block is typed `role → column` with no
free-form holes, and validation is two-tier — values against the column vocabulary
in the intrinsic tier (`validate_campaign`, `aura-research`), keys against the
strategies' `input_roles()` in the resolver tier (`validate_campaign_refs`,
`aura-registry`).
**Forbids.** Guessing a column for an unknown role name (refuse with the vocabulary
+ the override remedy, never default); opening columns in any order other than the
canonical declaration order (the [C4](c04-cycle-granularity.md) tie-break contract);
free-form or logic-bearing binding values (the [C17](c17-authoring-surface.md)/[C25](c25-role-model.md)
line — a binding value is a typed column reference, nothing else); fabricating
synthetic OHLC when a multi-column strategy meets synthetic data (the walk
generates a close series only — refuse with the `--real` remedy).
**Why.** The single-price weld — `wrap_r`'s hard-wired `price`←close and the six
`M1Field::Close`-only open sites — made every strategy monocular regardless of what
its blueprint declared. Binding by role name keeps the blueprint the single source
of its own data needs ([C24](c24-blueprint-data.md): topology-as-data carries its
input contract), lets a campaign re-aim a strategy without touching its content id
([C18](c18-registry.md) identity), and pins opening and declaring to one
shared, canonically-ordered plan so the two halves cannot drift
([C1](c01-determinism.md)/[C4](c04-cycle-granularity.md) determinism).
## Current state
`resolve_binding` lives in `aura-runner`'s `binding` module
(`crates/aura-runner/src/binding.rs`), moved out of the shell with #295; it takes
the strategy name, its `input_roles()`, and the campaign override map and returns
one `ResolvedBinding` plan. Column opening is `aura_ingest::open_columns`
(`crates/aura-ingest`; the `M1Field` enum fixes the declaration order
open/high/low/close/spread/volume); role declaration is `wrap_r` in
`aura-runner::member`. Both validation tiers are realized: `validate_campaign`
(`crates/aura-research`) checks binding values against the column vocabulary;
`validate_campaign_refs` (`crates/aura-registry`) checks binding keys against the
strategy's declared roles.
**Extension point (deferred).** When recorded non-price sources land (the #71
Source seam), the binding VALUE-space grows additively from archive columns to
recorded-stream references — a root role like `sentiment` becomes bindable to a
recorded stream then, and until then refuses with the vocabulary-naming message.
The role-name key-space and the campaign `bindings` carrier are unchanged by that
growth.
## See also
- [C4](c04-cycle-granularity.md) — the merge tie-break order that is the canonical open/declare order
- [C24](c24-blueprint-data.md) — topology-as-data carrying its own input contract
- [C18](c18-registry.md) — the content-id identity a rebind must not touch
- [C25](c25-role-model.md) — the Blockly litmus the vocabulary satisfies; the 6b rebind seam
- [C1](c01-determinism.md) — the determinism the shared plan protects
- [C27](c27-declared-taps.md) — the output-side twin (`taps` to `input_roles`)