feat: full-workspace C28 guard — the shell boundary is structural (#295 part 2)

Completes the shell-boundary cycle (tasks 10-13):

- c28_layering now enumerates the FULL workspace: every crate row is
  reconciled against its real production [dependencies], a completeness
  assertion pins the table to the on-disk crates/ set (a new crate can no
  longer escape the guard silently), the shell/assembly imported-by-
  nothing rule is asserted, and a shell-content check pins aura-cli to
  no-[lib] plus a closed allow-list of argv/translation/presentation
  modules. A new domain module in the shell now fails the suite.
- Ledger amendments: C28 gains the assembly position (aura-runner) and
  the corrected import-rule prose (the ratified aura-campaign ->
  aura-backtest production edge, #291/#292); phase 3 is marked done with
  the #297 process::exit residual named; a provenance note records this
  as structural-debt closure, not a demonstrated downstream blocker.
  C25 records the control-surface decision: the text artifact vocabulary
  is canonical, every control surface (CLI executor verbs, a future
  host, an MCP face, a World program) is a projection/executor over it —
  the which-projection-next ranking deliberately open on #295. C14 gets
  the executor-face amendment, C26 the binding-module relocation.
- aura_campaign::MemberRunner's doc names the shipped default
  implementor (aura-runner::DefaultMemberRunner) while the column keeps
  zero dependency on it.
- New library-only E2E fixture (aura-runner/tests/world_member_run_e2e):
  runs the canonical member recipe over a tiny synthetic archive with no
  aura-cli in the link graph, and pins C1 determinism (two independently
  constructed runners produce a byte-identical RunReport).
- campaign_run.rs module doc: intra-doc MemberRunner link re-anchored to
  aura_campaign::MemberRunner (the impl moved out with part 1).

Verification: cargo test --workspace green (1473 passed, 0 failed);
clippy --workspace --all-targets -D warnings clean; cargo doc clean of
NEW warnings (the five unresolved-link warnings in aura-std/aura-backtest
predate this cycle byte-identically at the anchor — #288-era doc drift,
left for the cycle audit).

refs #295
This commit is contained in:
2026-07-21 06:35:31 +02:00
parent 5c2ac982bc
commit 170c6c82dc
7 changed files with 360 additions and 18 deletions
+60 -17
View File
@@ -1253,6 +1253,13 @@ manifest stamps the untouched bound defaults (`defaults`, one-directionally
widened beside `params`, which keeps its "what varied" reproduce semantics) so
a raw reader of a fully-bound run no longer sees a misleading `"params": []`;
the blueprint itself carries no such duplication.
**Amendment (2026-07-20, #295 — the programmatic face is an executor).** The
member-run recipe — the definition of what a standard aura backtest is — is
library content (`aura-runner`/`aura-backtest`/`aura-measurement`, C28
assembly position), not CLI content: the binary is argv → document →
executor → presentation, and a downstream World program reaches the same
recipe with no binary involved. See C25's control-surface amendment for the
projection rule.
### C15 — Resampling-as-node; sessions/calendars
**Guarantee.** A resampler is a node (finer stream → coarser bar stream),
@@ -2508,6 +2515,15 @@ throughput lives. Realization: cycle 0106 (#189) shipped roles 5/6b their
artifacts (process/campaign documents, C18 realization note); roles 4/7/8 remain
technically present but faceless (no addressed verb families yet); role homes in
the project layout and docs-by-role are open (#192 context).
**Amendment (2026-07-20, #295 — control surfaces are projections).** The
text-first invariant fixes the control-surface layering: the text artifact
vocabulary (blueprints, process/campaign documents, registry records) is the
canonical layer, and every control surface — the one-shot CLI's executor
verbs, any future long-running host, any MCP face, a World program — is a
projection/executor over those artifacts, never a second home for intent.
Which projection is built next (document-first flag-vocabulary completion, a
typed-protocol host, an MCP face, or none until demand) is deliberately open
— recorded as an open direction fork on #295.
### C26 — Harness input binding: role names bind archive columns
**Guarantee.** A strategy blueprint declares WHICH data it consumes as the NAMES
@@ -2518,10 +2534,11 @@ 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-cli`'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 (`wrap_r`) in the **same
identity never changes. Resolution (`aura-runner`'s `binding` module,
`resolve_binding` — moved out of the shell with #295) 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 merge tie-break order,
so role i receives source i by construction. A close column is always part of
@@ -2544,7 +2561,7 @@ 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.
**Why.** The single-price weld (`wrap_r`'s hard-wired `price`←close and the six
**Why.** The single-price weld (`aura-runner::member`'s `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: topology-as-data carries its input contract),
@@ -2625,19 +2642,32 @@ outer:
Beside the ladder stands the **process column** — the research-process machinery
(the run registry C18, the process/campaign document types, campaign execution).
It consumes run *artifacts*, not market streams, and orchestrates runs of any
ladder layer; it stands beside the ladder, not on a rung. The **shell** (the
`aura` CLI) imports everything and is imported by nothing.
ladder layer; it stands beside the ladder, not on a rung. Between ladder/column
and the shell stands the **assembly** position (`aura-runner`, #295): it
composes the rungs into the canonical member-run recipe — harness assembly,
input binding (C26), the C1-load-bearing param↔config translators, and the
shipped default `MemberRunner` — importing the ladder rungs, `aura-composites`,
`aura-ingest`, and the process column, and imported only by the shell and by
downstream World programs. The **shell** (the `aura` CLI) imports everything,
is imported by nothing, exports nothing, and holds no domain logic:
argv/dispatch, argv→document translation, and presentation only (rendering
stays in the shell until the C22 web face provides its second consumer — #295
deferral).
**Import rule.** An outer ladder layer may import inner layers, never the
reverse; *measurement* and *strategy* are siblings (no import either way);
*backtest* may import *strategy*. The process column imports the engine layer
plus the run-artifact and metric interfaces, and is imported by no ladder crate;
column-internal edges are free. The shell imports all; nothing imports the
shell. **`dev-dependencies` are exempt** — tests may cross layers (the existing
`engine`/`ingest`/`registry``std` edges are dev-only and stay). The rule
binds **crate structure, not graph composition**: a blueprint may feed a
measurement statistic into a bias input — data flow inside a graph is free (C24);
layers govern imports, graphs compose freely.
plus the run-artifact and metric interfaces — realized since #291/#292 as a
production dependency on `aura-backtest` (the trading instantiation
`M = RunMetrics`) — and is imported by no ladder crate; column-internal edges
are free. The assembly position imports ladder and column alike and is
imported by neither; the shell imports all; nothing imports the shell — both
now enforced by the full-workspace `c28_layering` table plus its shell-content
check (#295). **`dev-dependencies` are exempt** — tests may cross layers (the
existing `engine`/`ingest`/`registry``std` edges are dev-only and stay).
The rule binds **crate structure, not graph composition**: a blueprint may
feed a measurement statistic into a bias input — data flow inside a graph is
free (C24); layers govern imports, graphs compose freely.
**Forbids.** An inner layer importing an outer one — most sharply the engine
layer importing backtest reductions, or market importing strategy. A ladder crate
@@ -2719,9 +2749,16 @@ cleanly by layer, so the layering is realized only partially:
`aura-backtest`, the statistics kernel to `aura-analysis`, dependency
inversion, ladder enforced by the structural test; realizes item 1 of #147);
(3) the #286
shape dispatch plus the per-run scaffold as a library (seeds an `aura-backtest`
crate, gives *measurement* its run verb, removes the measured O(cycles) run-time
retention); (4) split the `aura-std` roster along engine / market / strategy /
shape dispatch plus the per-run scaffold as a library **done** (#295: the
pure per-run scaffold pieces live in `aura-backtest::scaffold`; the composed
recipe — harness assembly, binding, translators, the default `MemberRunner`
— is the assembly crate `aura-runner`; the measurement rung is seeded as
`aura-measurement` with the IC; the shell is reduced to
argv/translation/presentation, enforced structurally. Residual: ~20 refusal
sites inside `aura-runner`'s single-run verb paths still terminate the
process; their conversion to `RunnerError` propagation is tracked as #297
the campaign path already refuses via `MemberFault`, never a process exit);
(4) split the `aura-std` roster along engine / market / strategy /
backtest — **done** (#288: four `aura-core`-only node crates, the closed roster
moved to `aura-vocabulary`, the ladder direction enforced by a structural test);
(5) split `aura-analysis` into generic statistics and backtest metrics —
@@ -2731,6 +2768,12 @@ cleanly by layer, so the layering is realized only partially:
names now realized (`aura-market`/`aura-strategy`/`aura-backtest`/
`aura-vocabulary`); full evidence on #288, #286 and the milestone.
Provenance note (#295): the shell-boundary cut closes a ratified structural
debt (this contract's own phase plan) and un-blocks the future World program;
it was not a demonstrated downstream blocker — the first downstream project's
observed friction was document-vocabulary gaps, never Rust-level recipe
reachability.
---
## Open architectural threads not yet resolved