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
Cycle-close audit (architect: drift_found, design core clean). What holds,
architect-confirmed: C18 bit-identity for RunMetrics (prose derived
byte-identically, rng order preserved, registry suite unchanged); the C10
wall (monomorphic R-gates, r_based in the R vocabulary, cross-vocabulary
refusal tested both ways — no leak); C28 direction (trait in aura-analysis,
vocabulary supplied from the outer rungs, zero Cargo edge changes); C1
determinism pinned through the generic path.
Drift items, all resolved as fixes in this commit:
- design ledger: the C28 #147 disposition now records item 2 SHIPPED (the
A1 cut) with A2 still deliberately deferred; the #136 one-implementor
clause carries a supersession note (the IC is the second implementor).
- seven stale doc comments describing the pre-#147 or mid-cycle state
(analysis trait + estimator docs, registry check_r_metric C9 claim,
engine re-export note, campaign PER_MEMBER_METRICS roster note,
member-seam guard comment, research vocabulary note) updated to the
shipped state.
- R_BASED_METRICS is now oracle-pinned against RunMetricKey::r_based()
in the vocabulary test (it feeds the NonRMetric refusal prose; a
divergence would have misreported the R-gate silently).
Noted, not amended (history stays): commit 6744f67's body says 'three
syntax-only edits' where the test module actually took five.
No regression scripts are configured (the bench is report-only); the
architect review is the gate. Verification: extended vocabulary test,
campaign suites, analysis/research suites green; clippy --workspace
-D warnings clean.
refs #147
C28 phase 2 (Stratification); realizes item 1 of the deferred #147. The
engine's production surface no longer names a backtest-metric type:
- RunReport becomes generic over its metric payload M; sweep/mc/walkforward/
blueprint thread the parameter (SweepPoint<M>, SweepFamily<M>, WindowRun<M>,
WalkForwardResult<M>). RunManifest stays concrete and engine-owned (its
selection: Option<FamilySelection> embeds the foundation-grade analysis type).
- summarize and the MC assembly (McDraw/McFamily/McAggregate/RBootstrap/
r_bootstrap/monte_carlo) move to aura-backtest - McAggregate::from_draws reads
RunMetrics fields by name, so generifying it is the phase-6 metric-vocabulary
abstraction (#147 item 2), still deferred; wholesale relocation is the honest
cut. The concrete instantiation lives in aura-backtest as
`type RunReport = aura_engine::RunReport<RunMetrics>` + sibling aliases.
- the statistics kernel (MetricStats/quantile/resample_block/SplitMix64) moves
to the aura-analysis foundation; the engine re-imports it (inner->foundation,
legal) and re-exports it so existing consumers stay source-compatible.
Dependency inversion in one commit: aura-engine drops aura-backtest from
[dependencies] (back to dev-deps for its SimBroker/RunMetrics test fixtures);
aura-backtest gains aura-engine. Cycle-free for lib targets - the cycle closes
only through the engine's dev-dep edge, the pattern aura-vocabulary already
uses. aura-backtest reaches the kernel transitively through the engine
re-export, so no aura-backtest -> aura-analysis edge exists (the C28 ladder
permits backtest -> {core, engine} only). run_indexed / SplitMix64::next_f64
widened pub(crate) -> pub for cross-crate use.
Consumers (registry/campaign/cli/composites/ingest/bench) rewired by import
path only, no call-site logic changed. The c28_layering structural test extends
to the full ladder: aura-analysis (no aura-* deps), aura-engine ⊆ {core,
analysis}, aura-backtest ⊆ {core, engine}.
Behaviour-preserving: 1448/0 tests, clippy -D warnings clean, serde shapes
byte-identical (C18 - RunReport<M> keeps field order manifest,metrics; the
CLI pre-serialized-splice contract unchanged), moved code traceable via git
rename detection. Cycle-introduced broken intra-doc links fixed.
closes#292
C28 phase 5 (Stratification): the backtest reductions - RunMetrics, RMetrics,
summarize_r, r_metrics_from_rs, and the position-event table - move verbatim
into aura-backtest::metrics, beside the position_management producer whose
record layout they read (the r_col/cost_col lockstep contract is now
intra-crate; its stale "aura-std" comment corrected). aura-analysis is reduced
to the domain-free half: the multiple-comparison hurdle math (inv_norm_cdf,
expected_max_of_normals) and the selection-provenance types
(FamilySelection/SelectionMode - kept beside the statistics so
RunManifest.selection embeds a foundation-grade type), [dependencies] = serde
only.
The engine re-export surface is name-unchanged (report.rs re-imports from both
crates), so no indirect consumer needed a source edit; aura-backtest moves from
aura-engine's dev-dependencies into [dependencies] as a commented TRANSIENT
widening of the C28 violation, removed by the phase-2 edge cut on this branch.
The campaign drift guard imports its pinned types from their new source
crates. Behaviour-preserving: 1448/0 tests, clippy -D warnings clean, serde
shapes byte-identical (C18), moved code traceable via git copy detection.
closes#291
Phase 4 of the Stratification milestone. aura-std held four C28 ladder
layers in one roster; this cuts them into layer-aligned, aura-core-only
node crates so the import direction is enforced by the crate graph:
- aura-std — engine nodes only (arithmetic/logic/rolling + sinks)
- aura-market — session, resample
- aura-strategy — bias, stops, sizer, cost-model machinery
- aura-backtest — sim_broker, position_management
- aura-vocabulary — the relocated closed std_vocabulary roster
Node modules move verbatim (byte-identical renames); consumers are
rewired by import path only. A new structural test
(aura-vocabulary/tests/c28_layering.rs) asserts each node crate's
[dependencies] stay within its C28-permitted inner set, catching the
acyclic-but-outward violation the compiler misses.
Behaviour byte-identical: full workspace suite green (1448 tests), no
golden edited, clippy -D warnings clean. C28 Status block updated.
closes#288
Codify the layer model settled with the user: an inner-to-outer ladder
engine -> market -> {measurement | strategy} -> backtest -> execution,
the research-process column beside the ladder, the shell outside. States
the import rule (inner never imports outer; measurement/strategy
siblings; column imported by no ladder crate; shell imports all;
dev-deps exempt), the six seams (five already ratified contracts —
C8/C24/C27/C18/C10-C13 — only the metric interface new), and an honest
Status recording the one hard production violation (engine -> analysis
via report.rs re-exports + the R-typed mc.rs fields) and its coupling to
the deferred #147.
Also restore the "### C19" header, deleted as collateral of ebe8568
(2026-07-03 cycle-0106 tidy); its body survived orphaned under C18. C28
cites C19, so the header must resolve.
refs #286
Surfaced by the measurement-milestone fieldtest: a downstream author
reaching for session anchoring or declared taps had no public statement of
either — the semantics lived only in node source / the design ledger.
SessionFrankfurt / Session output semantics:
- glossary `session node`: corrected — it claimed three streams
(`bars_since_open`, `in_session`, `session_open_ts`), but the shipped node
emits ONE `i64` field `bars_since_open`. Now states the real contract: the
count of completed bar-periods since the local (tz-aware, DST-correct)
session open, close-instant indexed (09:15->1, 09:45->3; pre-open <=0), with
the Frankfurt 09:00 Europe/Berlin open + `period_minutes` knob + the
value-ignored `trigger`.
- authoring-guide: a "Session anchoring: the SessionFrankfurt preset"
subsection — schema (trigger / period_minutes / bars_since_open), the
close-instant multiples, the EqConst gating pattern, DST handling, and the
add-snippet. Verified against `aura graph introspect --node SessionFrankfurt`.
Declared-tap authoring (C27 / the #284 tap op):
- authoring-guide: the `tap` op-table row (seven ops -> eight) + a worked
"declaring a measurement tap" example. The example is verified: piping it
through `aura graph build` emits
`taps:[{"name":"spread","from":{"node":2,"field":0}}]`.
- design ledger op-list (INDEX.md): a `tap` bullet (seven verbs -> eight);
also corrected the `expose` "only verb that keeps `as`" claim — `tap` keeps
it too.
- README op-kind enumeration: adds `tap`.
Docs only; no code change. Both worked examples were run through the built
`aura` binary; the SessionFrankfurt schema was checked against introspect.
closes#285
The measurement milestone overloaded 'tap': beside the closed campaign
persist_taps vocabulary (equity | exposure | r_equity | net_r_equity), the
word now also names a declared tap — a Composite.taps entry a hand-authored
blueprint declares on an interior output wire (C27), the output-side twin of
an input_role, bound run-mode-aware. Record-reality glossary update
reconciling the two senses; a worked authoring example is owed at docwriter
(#285).
Records the tap/binding contract: taps are the output-side twin of input_roles
(C26), resolved and hoisted at compile, bound run-mode-aware via a caller-built
sink (engine stays aura-core-only), recorded on the single run and inert in a
sweep. Documents the deliberate DCE-deferral — build-time elision now,
chain-pruning when DCE (C23) lands — and the unbound-is-inert non-error
asymmetry vs a mandatory input role.
refs #282
Cycle #191 (identity-ref index) closes drift-resolved. Architect review
held: results stay scan-identical via verify-on-hit (the same-identity-
twin choice was order-unspecified before and stays unspecified in kind);
zero caller/write-path/engine changes; the #276 append-lock discipline
correctly extended to the fourth JSONL path. No regression scripts are
configured; the architect review is the gate.
Drift items and resolution:
- C18 lacked a realization note for the new persistent sidecar store and
its index-first/verify-on-hit/repair-walk mechanism, and the "identity
refs by store scan" line was stale -> this commit adds the #191
realization note and dates the scan wording (fix, ledger-only).
- The repair pass grew the sidecar without bound while same-identity
twins coexisted (per-entry comparison against a mid-walk-stale
snapshot) -> fixed in-cycle via RED pin 35865bb + collect-then-diff-
append fix aa94f51; suite 1347/0, clippy clean.
Cycle spec and plan (git-ignored working files) removed per lifecycle.
refs #191
The two fieldtest findings, resolved:
- friction: --parallel-instruments 0 now refuses in domain terms ("must be
a whole number of at least 1 — it bounds how many distinct instruments
are resident in parallel") via a custom clap value-parser, instead of
leaking Rust's NonZeroUsize wording; the zero-bound test pins the prose.
- spec_gap: the authoring guide's exit-code contract now states that a
gate-emptied cell (a std::gate leaving no survivors) is a successful
cell — exit 0 with an informational aura: note and a truncated
realization — and that exit 3 is reserved for cells that could not be
evaluated. Records observed behaviour; no code change.
Gates re-verified: workspace suite green, clippy -D warnings clean.
refs #277
Resolutions for the architect's four drift items (all fix/document, none
ratified away):
- [medium] execute enforced family-name uniqueness only via the CLI's
validate tier: preflight now refuses duplicate campaign instruments
itself (defense in depth for direct callers), RED-first
(execute_refuses_duplicate_instruments).
- [medium] the parallel cell loop's C1 relationship lived only in the
git-ignored spec: C1 gains a realization note (docs/design/INDEX.md)
recording the chunked instrument-major schedule, the structural
residency bound, and the two scheduling-dependent fatal-path carve-outs
(fault attribution among completed cells; already-written family lines)
— both inert and outside the success-path bit-identity.
- [low] the fatal-path orphan-line honesty is part of that note.
- [low] the --parallel-instruments zero-reject acceptance criterion had no
protecting test: campaign_run_rejects_a_zero_parallel_instruments_bound
pins clap's NonZeroUsize usage error (exit 2).
Gates re-verified: workspace suite green, clippy -D warnings clean.
refs #277
closes#275
The CLI half of by-name source binding, plus the ledger record.
Every production run site that carries a ResolvedBinding — `run_signal_r`,
`run_blueprint_member` (sweep / reproduction), and the campaign re-run/trace path —
now keys its opened sources by role name via `key_supply(binding, sources)` and
drives the harness through `Harness::run_bound` instead of the positional
`run(sources)`. `key_supply` pairs each opened column with its declared role from
`binding.entries()` — the one place open-order and role-order meet, made explicit so
`bind_sources` verifies the wiring↔supply role match by name rather than by a
maintained canonical-order convention.
Because the current single-binding CLI derives both the `SourceSpec` roles (via
`wrap_r`) and the supply roles (via `key_supply`) from the same `binding.entries()`,
the bind cannot fail on this path — so the call site asserts the invariant with
`.expect`, matching the adjacent `close_handle.expect("ResolvedBinding guarantees a
close entry")` idiom. The named `SourceBindError` refusal path lives in
`bind_sources` for future decoupled-supply callers (independently-built multi-feed /
recorded sources, #124), where a mismatch becomes reachable.
Ledger: a C4 realization note (supply resolved by name into declaration order, so
supply order is no longer load-bearing; the tie-break guarantee is unchanged) and a
scoped C23 refinement (a `SourceSpec.role` is load-bearing for source binding; every
other flat-graph name stays a non-load-bearing raw-index symbol).
The fieldtest-corpus `SourceSpec` sweep (planned Task 5) was reverted: the fieldtest
packages already fail to build against the current engine API for reasons unrelated
to `SourceSpec` (bootstrap arity, removed `InputSpec`, drifted
`Recorder`/`SimBroker`/`RMetrics`, renamed `Scalar` helpers) — pre-existing bit-rot
from earlier cycles. A partial `SourceSpec` migration neither revives nor further
breaks them, so the scope decision to touch them (premised on their being
otherwise-buildable) was withdrawn; reviving the corpus is separate from #275.
Verification: `cargo test --workspace` green (0 failed; the real-data OHLC channel
e2e exercises the migrated `run_bound` path byte-identically); `cargo build
--workspace --all-targets` clean; `cargo clippy --workspace --all-targets -D
warnings` clean.
Drift review (architect) over a55e4cf..HEAD found the cycle substantially
clean — C25 closed vocabulary, C1 determinism/behaviour-preservation, the
additive-serde widening, exhaustive StageBlock matches, and the uniform exit-3
convention all hold. Two drift items resolved:
- RATIFY: the `SilencedPanic` member-boundary panic-hook silencer (added by the
#272 implementer, not named in the spec) is a legitimate mechanism — it keeps
"recorded, campaign continues" observably true on stderr by suppressing the
default crash backtrace around each contained `catch_unwind`. Its mutex
serialises only the ref-count/hook-swap (O(1)), never member computation, so
C1 disjoint-parallel determinism is preserved. Documented in the ledger's
#272 realization paragraph rather than left as undocumented global state.
- FIX: `cell_fault_kind_label` hand-wrote the snake_case strings that must match
`CellFaultKind`'s serde `rename_all` (two sources of truth an aggregate over
campaign_runs.jsonl could silently diverge from). Pinned with a test asserting
each label equals the serialized form; the efficient `&'static str` stays.
(The #272 commit's own concern-driven doc fixes and the wf panic-containment
test landed in d3b1a1a; this commit carries only the two audit-phase items.)
Suite: cargo test --workspace green (1311 tests, 0 failed); clippy clean.
closes#272
A member fault (no-data, bind, run, or a caught panic) is now a recorded
per-cell outcome instead of aborting the whole campaign and discarding every
already-computed cell. The incident that motivated this (a 22-instrument
campaign lost ~36 healthy cells ~6.7 min in because Copper had an archive gap)
now completes: the healthy cells persist, the gap cell is recorded as failed,
and the run exits 3.
Direction (owner decision 2026-07-14): run to completion and report
compromised results; no coverage preflight, no window synthesis.
Containment granularity:
- The CELL for a sweep-stage member fault (a grid hole structurally
compromises winner selection, so the whole cell fails).
- The FOLD for a walk_forward member fault (independent time windows): the
surviving folds pool into the family, failed folds are recorded as
StageRealization.window_faults, and the summary names the ratio.
- aura-registry: additive CellFault / CellFaultKind (closed:
no_data|bind|run|panic|window) / WindowFault / CellCoverage, plus
fault/coverage fields on CellRealization and window_faults on
StageRealization — all serde-default-skipped, so pre-#272 campaign_runs
lines parse and round-trip byte-identical.
- aura-campaign: run_cell returns a fault-annotated CellRealization instead of
Err (execute's accumulate-then-append-once tail is unchanged and now
persists every healthy cell + the one run record); a `contain` split keeps
ExecFault::Registry and doc-shape preflight faults global while Member/Window
become per-cell/per-fold. Member panics are caught with
catch_unwind(AssertUnwindSafe) at all three member-run sites (sweep IS/OOS)
and recorded as MemberFault::Panic — a member panic no longer aborts the
process. The wf stage partitions Registry faults (global) from Member/Window
(per-fold) and filters faulted-fold placeholders (the
"faulted-member-placeholder" broker sentinel) out of the persisted family.
- aura-cli: exec_fault_prose gains the Panic arm; CliMemberRunner::window_coverage
derives effective bounds + interior gap months from the #264 archive
primitives; present_campaign prints per-cell failure notes + a completion
summary and threads the failed-cell count; a run with >=1 failed cell exits 3
("completed with failed cells") uniformly across `aura campaign run` and the
dissolved sweep/walkforward/mc/generalize verbs (exit_on_campaign_result).
Usage stays 2, refused-before-running stays 1, clean stays 0.
Tests: the global-abort pins flip to containment (execute + the two wf fault
tests → fold-containment + all-folds-fail-the-cell); new panic-containment
tests on both the sweep path (PanicRunner) and the wf path (this commit adds
the wf mirror the loop left uncovered); a new gapped-archive e2e (one covered
cell + one gap cell → exit 3); the ~14 CLI exit-1 pins move to the exit-3
register; a pre-#272-line byte-identical round-trip guard.
Suite: cargo test --workspace green (1309 tests, 0 failed); clippy clean.
Decision log: #272 comments (fork rationale, the fold Registry/Member split,
the placeholder sentinel, uniform exit-3).
Follow-up (minor, not blocking): the plan under-scoped Task 1 to aura-registry
though the additive fields also touch aura-campaign's exec.rs literals — the
loop absorbed it mechanically; a future plan for a cross-crate additive-field
change should scope every crate's construction sites in the first task.
closes#256
Fork B (owner decision 2026-07-14): the dissolved walkforward/mc
translations' leading sweep executed the full grid over the whole campaign
window and persisted a Sweep family, yet only the enumerated parameter
points ever crossed the stage seam (the wf stage re-sweeps them per IS
window itself). The leading stage is now the fieldless vocabulary block
std::grid: it enumerates, executes nothing, persists nothing.
- aura-research: StageBlock::Grid ({"block":"std::grid"}), schema-strict
parse arm (empty slot list: every key but "block" is refused by the
generic unknown-slot check), PROCESS_BLOCKS entry, intrinsic-tier no-op
arm; the vocabulary test's non-empty-slots guard carries a pinned
std::grid-only exception (a nominal slot would misdescribe the
vocabulary to describe_block consumers).
- aura-campaign: the inter-stage seam is a typed two-armed StageFlow
(points-only vs executed members); gate / mc-per-survivor fence the
points-only arm with defensive PipelineShape faults; preflight admits
std::grid only as the first stage and only immediately before
std::walk_forward (every other neighbor consumes executed reports).
- aura-cli: translate_walkforward / translate_mc lead with
StageBlock::Grid; the two family-shape E2E pins flip to zero Sweep
families; translate_generalize keeps its executed sweep(argmax) — its
generalize stage consumes the argmax winner report as the cell nominee.
- docs: dated #256 amendment in the ledger's verb-dissolution narrative;
the authoring-guide vocabulary transcript gains the std::grid line.
Behaviour preservation: the exact-grade real-data pins
(walkforward_real_e2e_pins_the_exact_current_grade,
mc_r_bootstrap_real_e2e_pins_the_exact_current_grade) pass unmodified —
survivor points reach the wf stage in the same odometer order as before.
Measured (the #256 acceptance measurement; debug build, real GER40 2025,
2x2 grid, `aura walkforward --real`, 3 runs each):
before (a55e4cf): 6.27 / 6.18 / 6.18 s
after: 4.52 / 4.49 / 4.45 s (~ -27% wall clock)
Suite: cargo test --workspace green (0 failed); clippy clean.
Decision log: #256 comments (fork rationale incl. the StageFlow seam and
the slot-guard exception).
The second risk-regime variant vol_tf{period_minutes, length, k}
computes the vol estimator over completed time buckets: an H1 signal
gets an H1-matched stop in the research matrix instead of a hand-scaled
minute stop (the issue's k-inflation workaround retires). Additive
externally-tagged serde (stored Vol documents keep their content ids);
the executor arm wires the VolTfStop primitive via the builder+bind
chain; validate checks period_minutes/length/k positivity per regime.
The member-manifest round-trip holds for both variants (C1): vol_tf
members stamp stop_period_minutes/stop_length/stop_k and the reproduce
path re-derives the VolTf stop whenever stop_period_minutes is present
— never a silent default-Vol fallback. The Regimes slot label and the
unit notes name the new variant (period_minutes IS the stop's
timescale); glossary, authoring guide, and design ledger record the
second variant. Default regime and sugar paths byte-untouched.
Coverage: node units (rollover-only emission, bucket-delta math, the
constant-|delta| correspondence with the per-cycle regime), executor
fold, serde/validate units, the manifest round-trip unit, and a
hostless two-regime e2e (distinct ordinals; vol_tf members stamp their
timescale, vol members do not).
closes#262
campaign introspect --unwired now annotates the four silently
misreadable knobs with unit/semantics sub-lines: cost_per_trade is a
price-unit numerator charged as cost/|entry−stop| in R (not a cost in
R); slip_vol_mult multiplies the per-cycle vol estimate; vol.length
smooths the estimator and does not set the stop's timescale; vol.k
scales the stop distance (the risk-unit lever). Rendered additively
via a new OpenSlot.notes field, so the sibling tests pinning the slot
hint lines verbatim stay green byte-identically.
The glossary's cost-model and risk-regime entries mirror the same
semantics inline (two-sentence convention kept), the risk entry
pointing at #262 for the timescale-matched variant.
closes#265
The OOS bootstrap conduit RMetrics.trade_rs becomes net_trade_rs and
carries the COST-NETTED per-trade R (r − cost_in_r, trade order). Every
conduit consumer is thereby net-when-costed: the monte-carlo pooled-OOS
and per-survivor bootstraps, the walk-forward oos_r pooling, and the
deflation null-max — which previously compared a net observed statistic
against a gross-resampled null whenever a costed campaign selected on
net_expectancy_r. An uncosted run is bit-identical (empty cost stream
⇒ cost 0.0 per trade), so every existing golden pin stays green.
Design: one conduit, no knob — an explicit net: knob would let a costed
campaign silently produce a gross headline again (the exact misreading
of the issue's evidence). Per-member RMetrics scalar fields stay gross;
net_expectancy_r keeps its meaning. Wire shape unchanged (serde(skip)).
The net series is materialized as a separate expression in summarize_r;
the pinned-float expressions (net_sum, the SQN pair, the lockstep
r_metrics_from_rs copies) keep their tokens verbatim. Fork decisions
and rationales: issue #259 comments.
New coverage, both hostless over the synthetic SYMA archive: a costed
campaign twin shifts the pooled-OOS bootstrap (sweep→gate→wf→mc) and
every per-survivor bootstrap (sweep→mc) below its gross sibling, with
the trade population unchanged; a unit test pins the conduit as the
cost-netted series with the empty-stream degeneracy. Existing conduit
tests renamed with the field.
Verified: full workspace suite green (71 result blocks), clippy clean,
zero bare trade_rs tokens remain, ledger conduit prose updated in place.
closes#259
RunManifest gains defaults: Vec<(String, Scalar)> — the wrap-prefixed
bound_param_space() of the signal, read after axis reopening, so a
bound param an axis overrode has already left the space and flows
through params instead (disjoint by construction; verified end to end:
a sweep member's overridden fast.length sits in params while
slow.length/bias.scale sit in defaults). params keeps its "what varied"
semantics and stays the reproduce input. One-directional serde
widening (#[serde(default)]) per the selection/instrument/topology_hash
idiom — old records deserialize with an empty defaults; unlike the
Option fields it always serializes, mirroring params. ~20
struct-literal sites across five crates gained the field
(compile-mandated breadth, no behaviour change at those sites).
The C14 ledger records the underlying decision (2026-07-13): generated
outcome records spend redundancy on direct readability (single writer,
cannot drift); authored intent artifacts admit none (every redundancy
is a drift site) — so the fix lands in the manifest, never the
blueprint.
Verification: RED test run_manifest_stamps_untouched_bound_defaults
green; cargo build --workspace; cargo test --workspace green; clippy
-D warnings on the touched crates; binary-level sweep exclusivity
check.
The references to the relocated _open blueprints follow the plan's
two-way split: tests that only need a sweepable blueprint sweep the
closed twins via bound-override axes (#246) with their axis strings
byte-unchanged (the wrap prefix is the blueprint's internal name,
identical across twins); the 12 tests that genuinely exercise
open-param semantics (run/mc closed-guard refusals, subset-axes
MissingKnob, --list-axes open/bound line mix, gang dissolution and
campaign paths) read the fixtures under tests/fixtures/. The
research_docs/project_load store seeds sweep the closed twin (campaign
axis references resolve via the #246 reopen path); the INDEX.md history
note records the relocation.
Two deviations from the plan, both verified against the tree: doc
comments referencing the bare filename (no examples/ prefix) escaped
the global path swap and were reworded to stay truthful; the seed
variable open_bp was renamed closed_bp (the plan kept it, but the
rename touches only the binding and its uses and removes a
misdescription).
Verification: cargo test -p aura-cli --test cli_run (140 passed, 0
failed, real data exercised); --test research_docs; --test
project_load; the four byte-pinned exact grades unchanged; grep-clean
for examples/r_*_open.json across crates+docs; full workspace suite
green.
Per-cycle fieldtest over the #246 surface, consumer-perspective, public
interface only: the scaffold quickstart end to end (aura new -> run ->
--list-axes -> override sweep), --list-axes on a partially-open
blueprint, an override family reproduced bit-identically (3/3 members),
and the error surface (wrong axis name, kind-mismatched value — both
exit 2). Corpus under fieldtests/cycle-246-bound-override/.
Dispositions: the one bug (F6) is fixed in this commit — authoring-guide
§1 still asserted a bound param 'never shows up in --list-axes' and 'no
aura sweep --axis can reopen it', contradicting the shipped #246
semantics and the C12 amendment; the three-states paragraph now reads
open = must-bind, bound = overridable default. The friction finding (F5,
KindMismatch/MissingKnob printed as raw Debug structs beside polished
prose) is filed as #247.
refs #246
Task 7 of the bound-override cycle: the design ledger's C12 records the
bound-as-default semantics (axis 1 may name a bound param; identity reads
the authored document; the retired axes-bind-only-open-knobs restriction
was an implementation consequence, not a recorded decision), the glossary's
blueprint entry carries the same sentence, and the authoring-guide's
data-only starter section explains the one-file run+sweep quickstart.
closes#246
Task 6 of the bound-override cycle: SIGNAL_OPEN_JSON_STD and its write
retire — aura new scaffolds signal.json alone, and the project CLAUDE.md
quickstart advertises run AND sweep against that one file (the sweep line
overrides the bound fast.length; --list-axes shows open + bound axes).
The data-only e2e loop proves it end to end: scaffold, run the closed
starter, sweep it via an override axis, and assert signal.json is the
only starter blueprint. The authoring-guide's starter-pair sentence is
retargeted; the last signal_open reference outside the unrelated #196
fixture string is gone.
refs #246
Consumer-POV fieldtest evidence for the two-tier project model: the
data-only quickstart, the role-2 attach loop, real-data usage inside a
data-only project, and the refusal surfaces all match the docs.
Inline fixes for two of the friction findings:
- docs/project-layout.md: the day-in-the-life run example now shows real
Unix-ms window bounds instead of bare years (--from/--to take ms).
- `aura new` --help one-liner no longer calls the project a crate.
Remaining findings tracked on the Gitea queue (empty first-manifest
provenance on a fresh scaffold; misleading no-data message for an
uncovered --real window; the tier hint missing on the graph-build path;
the raw cargo-metadata leak for a missing [nodes] dir).
refs #241
- CLAUDE.md invariant 9 amended end to end: a project is a directory
anchored by a static Aura.toml, data-only by default; native node
logic lives in attached node crates (the nodes/ antecedent removed —
user decision 2026-07-12, re-opened by the role model's diagnosis).
- docs/project-layout.md reworked to the two tiers (data-only default
tree + sibling node crate, workspace note under the crate half); the
day-in-the-life walkthrough now attaches the project's node crate
once and wires the namespace the shown command actually produces.
- docs/authoring-guide.md: data-only quickstart (closed run target,
open sweep target) + `aura nodes new` as the native entry; section
cross-reference updated.
- docs/glossary.md: new "data-only project" and "node crate" entries;
Aura.toml and manifest entries carry the tier split.
- docs/design/INDEX.md: realization note (wiring-only tier, #241) in
the project-environment section.
- rustdoc: `[nodes]` intra-doc-link escapes in project.rs/main.rs.
Full gates green: workspace suite (all binaries), clippy -D warnings,
doc build without new warnings.
closes#241
The tail audit over d1e01ef..62f6592 found no code drift against the
contracts (C18 name-resolution read-only, C23 doc identity-blind and
inline-dissolved, C1 fit deterministic) but four lagging records, fixed
here: the #106 real-roller amendment gains the #239 fit-to-window note
(fixed sizes are a ceiling on the sugar path); a #125 realization note
records the composite doc as a C23 debug symbol with its serde/identity/
viewer treatment; the glossary identity-id and composite entries name
the doc; GraphSession::finish states the deliberate absence of a
doc-carrying op-script surface (refs #125) instead of leaving the
authoring asymmetry silent.
The NotFound arm of emit_chart now queries the recorded campaign runs:
records with a persisted trace_name whose stored campaign document
carries name == ARG. A unique match re-dispatches the existing charting
path onto the resolved handle; several matches refuse listing the
candidate handles in append order (refuse-don't-guess — re-running a
named invocation legitimately yields {campaign8}-0, -1, ...); zero
matches keep the not-found message verbatim. The exact trace-store
handle always wins; the name never becomes a store key (C18 untouched).
Guide §3 and the glossary tap entry document the name alternative.
closes#238
Tail audit (aaca18c..f591164) over #213, the milestone-fieldtest
fixtures, and the B1 chart fix: #213's intersection semantics conflict
with no ledger contract, the depth-2 trace resolution preserves C1
ordering read-side with the layout unchanged, and the new §0/§3/
glossary docs match the code vocabularies. One HIGH drift item
resolved: the two C22 --trace amendment notes still described the #168
refusal surface and deferred per-member trace-writing to #224 — both
now carry a delivered-2026-07-11 annotation (real-data --trace writes
the depth-2 fan-out, chart resolves it; synthetic still refuses).
Accepted LOW debt, recorded in-code: the disjoint-archive generalize
refusal has no e2e fixture (unreachable on this host's archives), the
pure helper is unit-tested.
- glossary "tap" + authoring-guide §3: the chart handle of a
sweep/walkforward --trace family is the printed family handle
(members keyed <cell>/<member>) — the campaign trace_name analog was
documented, the sweep case was not (fieldtest spec_gap).
- authoring-guide §0: state the input window's financial-style ordering
(index 0 = newest, index k = k cycles back) where the node pattern is
taught — previously only in the design ledger's RingBuffer note.
- authoring-guide §3: the #234 cost block joins the open-slot list, the
worked campaign example, and the prose (charge modes, net-vs-gross
default, manifest stamping) — it was discoverable only via
campaign introspect --unwired.
Fieldtest fixtures: 09da04f.
Architect review over dd23ea3..HEAD (the #234/#152 cycle, 6 commits).
What holds (architect-confirmed): CostSpec is a closed, deny-unknown-
fields vocabulary with typed slots mirroring RiskRegime, field names
conforming to the builders' ParamSpec names; cost-less docs hash
byte-identically (C18); cost threads through the single MemberRunner
seam so every campaign stage nets uniformly; costed families reproduce
bit-identically incl. Carry; the #221-deleted leg is rebuilt optional
(net = gross under the empty model); both cost_graph leaks replaced by
the interned single source (#152).
Resolved this close: [high] the C10 realization notes contradicted
shipped reality — the two 'wired on the run path via --cost-*' claims
carry superseded-annotations, the 0084 carried-debt note a discharged-
annotation, and a new cycle-net-r realization documents the campaign
cost block, the net-by-default decision, the manifest/reproduce
round-trip, and the returned net_r_equity tap; [medium] two dangling
persist_traces_r docstring references reworded to what exists;
[medium] the one-knob-per-cost-node invariant behind the manifest
round-trip is now unit-pinned (exactly one distinctly-named knob per
shipped builder).
Cycle spec and plan (git-ignored working files) discarded per
convention.
refs #234
The acceptance proof: hl_channel — a causal Donchian channel (Delay(1)
excludes the current bar, C2) consuming high/low/close roles, built from
rostered vocabulary, shipped as the r_channel/r_channel_open example
pair (builder + regenerator + serialize pins, the r_* pattern). Proven
at three layers: a non-gated loaded-vs-carve equivalence over inline
sources (non-zero-bias hardened per review), the unconditional synthetic
multi-column refusal, and archive-gated CLI e2e — aura run on GER40
end-to-end plus a sweep whose members run and reproduce 2/2
bit-identically.
Ledger: new C26 entry (the binding vocabulary — closed column set +
price alias, name-driven default + campaign data.bindings override,
canonical order = the C4 tie-break, Blockly-litmus argument, the #71
extension point for recorded non-price sources) and the C20/C24
scaffolding-clause annotations: the single-price data weld is retired,
wrap_r's remaining R-scaffolding retirement stays #159.
Verified: all proof tests green, full workspace suite green, clippy -D
warnings clean; independent quality review approved.
closes#231
New top-level section covering the three-part shape every node type
follows — Node impl (lookbacks/eval/label with the C8 warm-up filter),
PrimitiveBuilder recipe (NodeSchema + positional build closure), and
rostering (project-side aura_project! vs std-side roster macro) — with
the aura-new Scale starter as the verbatim worked example and the five
fresh #236 nodes as arity-variant references. Cross-linked from the
op-script vocabulary note, project-layout, and the README.
closes#228
The two open examples now expose their author-intended single knobs — the
closed builders always bound these pairs to one value by hand; the open forms
falsely offered them as independent axes:
- r_breakout_open: channel_hi.length + channel_lo.length -> channel_length
(the Donchian channel is structurally ONE parameter)
- r_meanrev_open: mean_window.length + var_window.length -> window; the band
factor stays an independent axis
Regenerated via the emitters (never hand-edited); the closed examples are
byte-unchanged. The carve builders gang in the open branch only.
Test migration: the two axis-namespace pins, five --real e2e invocations
(single gang axis, the 10,20 diagonal for the campaign pair — the mismatched
20,40/10,20 grid was exactly the configuration space the gang retires), and
the param_stability row counts (4 -> 3). The r-sma walkforward golden anchor
is untouched (it never swept a ganged pair).
Docs: authoring-guide gains the seventh op + the third param state
(open/bound/ganged) + the gang wrap note; README op list + Axis concept;
glossary gang entry; ledger C24 records the gang verb and the pre-ship
Tier-2 dormancy (no format-version bump while no out-of-repo reader exists).
Verified: full workspace suite 1104/0 (--real e2e included, local data
present), clippy -D warnings clean, cargo doc clean; live acceptance:
introspect --params prints channel_length:I64 alone / window:I64 +
band.factor:F64.
closes#61
The #28 cycle-close audit found aura graph <file> silently rendered the embedded
sample at exit 0 when the positional named an unreadable blueprint (typo,
nonexistent path, wrong extension): is_blueprint_file returns None both for "no
positional" and for a bad arg, and dispatch_graph's None arm conflated them —
re-introducing the exact #28 "I asked for my graph, got the sample" surprise in
the error path, and diverging from every sibling verb (a bad blueprint arg is a
usage fault, exit 2, per C14). Split the None arm: no positional -> the sample
(unchanged); a positional that is not a readable .json -> a usage error naming
the arg, exit 2, mirroring dispatch_run.
Also close the paired doc drift the audit flagged: the README blueprint-verb
table and the authoring guide now document the aura graph <file> render surface,
absent while graph was the last sample-bound verb.
refs #28
The #190 cycle-close audit found two now-stale claims that predate this
cycle's guard test: the ledger's known-debt note listed the metric-roster
triplication as tracked by #190 (the issue that just added the guard, closing
on push), and metric_vocabulary()'s doc comment told a hand-editor to keep the
list in sync by hand without mentioning that a red test now catches a desync.
Reconcile both to the honest state: the triplication remains (still a
hand-list) but drift from the shipped aura-analysis types is now caught by the
cross-crate guard `aura-campaign/tests/metric_vocabulary_e2e.rs`; the
single-source removal that would delete the hand-list is tracked under #147
(where the metric vocabulary's home is decided).
refs #190, #147
The #168 refusal left sibling --trace surfaces still lying and the design ledger
still describing CLI --trace as a live persist path (surfaced by the cycle-close
architect drift review). Reconcile them to record reality:
- the chart NotFound hint no longer points at `aura run --trace` (itself refused);
it names the live trace-writer (a campaign document's persist_taps presentation).
- RunCmd/McCmd --trace help stop promising persistence (both flags already refuse).
- README drops --trace from the live naming flags.
- the ledger records the CLI --trace retirement: per-member --trace was never
wired to the blueprint sweep (`let _ = persist`) and was silently dropped at
#159/#220 — it never migrated as the old HISTORY note claimed; the single live
TraceStore::write is the campaign persist_taps; restoration is deferred to #224.
Workspace suite green (62 groups / 0 failed), clippy -D warnings clean.
closes#168
refs #216
Architect drift review over 68317ec..6e22c98 (two items, both fixed here;
code and ledger verified drift-clean):
- The authoring guide's 'Register and run' transcript still showed the
pre-regime world (2 cells, regime-less family ids) directly under the
worked example this cycle made two-regime / 4-cell. The transcript now
shows the -r1 family-id suffix, per-cell regime fields (ordinal 0
serialize-skipped, the established byte-stability pattern), the four
cells, per-regime generalization entries, and a prose paragraph tying
the 4 cells to the validate count (regimes compared, never pooled).
- The fresh glossary entry declared 'Avoid: stop regime' while this very
cycle ships 'stop regimes' in its hint strings and guide prose — an
Avoid line the codebase itself violates records intent, not reality.
'stop regime' is dropped from Avoid ('risk section' stays: it wrongly
suggests a section rather than an axis list).
What holds (architect): content-id parity untouched (probe is read-only,
canonical bytes unchanged); the validate cell product mirrors the exec
cross-product exactly; all six spec acceptance criteria shipped.
Regression: cargo test --workspace 62 result groups / 0 failures; clippy
-D warnings clean; cargo doc 0 warnings (all at 6e22c98; this commit is
docs-only).
closes#216
The remaining introspection e2e pins: the bare-{} envelope probe (the
issue's exact reproduction) lists the optional risk slot; a bound
(non-empty) risk list closes it (no open slots); an EXPLICIT empty
'risk': [] still counts as open (pins the non-emptiness branch, which an
is_none()-based probe would get wrong); the --block std::risk assert is
sharpened to pin the optional rendering and the block doc line (the old
bare contains("risk") matched std::risk trivially).
Docs: the authoring guide's bare-{} transcript gains the risk line, the
§3 worked example gains a two-regime risk section (heading updated), a
prose paragraph explains the structural risk axis (stop defines R;
absent = the same default vol regime the verbs bind), and both validate
transcripts show the new matrix counts. The glossary gains the
'risk regime' entry (canonical term; avoid: stop regime, risk section).
Verified: cargo test --workspace 62 result groups / 0 failures; clippy
-D warnings clean; cargo doc 0 warnings; the guide's transcript strings
grep-match the shipped hint/summary bytes (1/2/1).
Cycle-close audit for the stop-default cycle (af8564d RED + 2102305
GREEN). Architect review: what holds — the argv extractors and the
campaign member runner default to the same single-sourced regime
constants; the multi-value refusal and the C10/C20 regime semantics
survive; manifests stamp the resolved stop (C18). Three drift items,
all resolved fix-path here:
- generalize's default path was shipped untested; added the
generalize_args_from_defaults_missing_knobs_to_the_regime unit test
mirroring mc's sibling.
- The None=>default / Some=>parse+single-value stop block recurred 4x
across the walkforward/mc extractors; extracted into the generic
stop_knob_or helper (refusal strings byte-identical; generalize's
typed unwrap_or extractor deliberately not unified — no parsing
there).
- Ledger stop-defaulting note now records the two default
representations: sweep binds no regime (late-resolved by the member
runner), the three verbs bind the default eagerly into the document
— same R behaviour, deliberately different content ids.
Regression gate green, no baseline moved: full workspace suite 0
failures across all test-result groups; clippy -D warnings clean;
cargo doc 0 warnings. No spec/plan working files existed (tdd entry
path — the committed executable spec is the contract).
refs #217
Cycle-close audit for the verb-axis-generalization cycle (a2294c7,
e7efe1f, b849bb7). Architect drift review: what holds — all four verbs
route through the one validate_and_register_axes seam into the campaign
executor; RGrid gone workspace-wide; zero too_many_arguments allows in
verb_sugar.rs; no r_sma_open embed at the verb dispatch sites (the
aura-graph default embed survives by design). Five drift items found,
all resolved fix-path in this commit:
- README (blueprint-verbs section + mc row), glossary (walk-forward
entry), and ledger (verb-dissolution passage) reconciled to the
shipped generic grammar; the ledger HISTORY note now separates the
two supersessions correctly (#159 retired the built-in --strategy
demo surface; #220 de-welded the verbs) instead of mis-attributing
the weld removal to #159.
- Four stale characterization doc-comments in cli_run.rs reworded:
the retired welded grammar is now named as the pinned grades'
provenance, not the current spelling; one dangling reference to a
deleted test repointed to the live anchor.
- The quadruplicated dispatch blocks extracted into two shared helpers
beside validate_and_register_axes: exit_axis_register_error (the
AxisRegisterError exit-map, byte-identical stderr + exit codes) and
campaign_window_ms (from_choice -> full_window -> unix-ms clip);
generalize's explicit unclipped (Some, Some) passthrough arm is
untouched — only its fallback arm, which already ran the identical
mechanics, calls the helper.
Regression gate green, no baseline moved (nothing to ratify):
cargo test --workspace exit 0, 62 test-result groups, 0 failures;
clippy --workspace --all-targets -D warnings clean; cargo doc
--workspace --no-deps 0 warnings. Spent cycle spec + plan working
files discarded per lifecycle.
refs #220
Drops docs/whitepaper/ (Bevy ECS visualizer concept). The evaluation
against the design ledger found it re-litigates the settled
web-from-disk visual-face fork (#101/#192/#209) on incorrect premises;
the salvageable ideas (on-demand trace slices, sweep-surface heatmaps,
param-binding-path highlighting) belong in the tracker, not a side doc.
Adds a conceptual whitepaper outlining how to use the Bevy game engine and its ECS architecture to build a highly performant, WASM-ready 3D/2D visualization layer (Playground) for Aura's massive backtest datasets.
Cycle-close audit tidy for the #159 demo-retirement arc: behaviour-free
reword of prose/comments/docs left pointing at symbols the arc deleted.
Adversarially verified by the audit workflow; cargo build --workspace
--all-targets green.
- README: drop the removed `--harness` token; restrict the legacy built-in
form claim to walkforward/mc/generalize (run/sweep no longer resolve one).
- glossary: the built-in `--strategy` sweep surface is retired (#159), not a
live inline path.
- INDEX.md ledger: append [HISTORY] supersession markers (house style) to the
dated realization notes advertising the retired `--harness` / built-in
`--strategy` CLI forms (cost-flag #153, cycles 0065/0066/0067, name-res,
#210 status); historical text preserved.
- aura-engine test-fixture docs: drop dead cross-crate refs to the deleted
aura-cli fns sample_harness / build_sample and the deleted `macd` composite.
- cli_run.rs: reword the #159-cut-2 pin doc to the current clap generic-usage
reality (no strategy_from / Strategy::RBreakout); r_sma_sweep_family ->
blueprint_sweep_family.
- verb_sugar.rs: fix the pre-existing `E[R]` unresolved-doc-link warning.
Remaining tidy — the main.rs comment cluster plus its rustdoc link at :1546
(the second cargo-doc warning) — held for a follow-up issue. The dead
cost-graph branch is tracked in #221. refs #159
The spec/plan lifecycle is a fixed plugin convention (skills/docs/conventions.md, sections File layout + Lifecycle). Restating it here is a drift vector: it had already gone stale (said git-tracked while the plugin moved to git-ignored on Jul 2). Remove the restated paragraph and enforce the git-ignored model with a project-local docs/.gitignore.
The verb-dissolution STATUS paragraph read "cycle 0110 dissolved the first verb
… Remaining: generalize, walkforward, and mc's R-bootstrap path" — stale at HEAD,
where all four verbs are dissolved. The per-cycle audits were each scoped to
their own commit range, so the cumulative status line lagged; the milestone
close fieldtest (#210, 2026-07-07) surfaced the drift.
Refresh the paragraph to the completed state, and record the per-verb
dissolved-form asymmetry as intended scope (ratify): for sweep the dissolved
form is the blueprint file (`<bp.json> --real`) while `aura sweep --strategy
r-sma --real` stays the inline built-in path — the built-in `--strategy` demo
surface is #159's hard-wired-harness retirement target, not the dissolution's;
for generalize/walkforward/mc the dissolved form is `--strategy r-sma --real`.
Glossary: note in the sweep and walk-forward entries that the CLI verbs are now
thin sugar over the campaign path.
Milestone closed 2026-07-07 on a green end-to-end fieldtest (0 bugs). Forward
findings filed: #216 (risk-axis discoverability), #217 (verb knob asymmetry),
#218 (no-project store litter).
refs #210
The FOURTH and LAST verb dissolution is closed. Architect drift review
over 18060cb..7f5f576: all design contracts hold, debt only.
What holds (architect-confirmed by reading the diff):
- Pure sugar: only crates/aura-cli/ changed; aura-campaign/aura-engine/
aura-research are byte-for-byte diff-free — invariant 10 (no
verb-special engine logic) and invariant 12 (the World runs it through
the one campaign path) both hold.
- The seed decision is C10/C18-sound: campaign.seed = the mc --seed is a
content-addressed manifest field (reproducible-from-manifest), and the
wf winners are argmax hence seed-independent — the pooled OOS series is
unchanged, only the bootstrap draw moves. Pinned by the dedicated e2e.
- Fork A / Fork C faithful: single RiskRegime::Vol, multi-value stop
refused (aligns with the C20 regimes-are-compared realization);
--name/--trace rejection mirrors the inline r-path; defaults match
McArgs::RealR exactly; zero deletions (run_mc_r_bootstrap / run_mc
retained for the fenced synthetic/non-real paths, until #159).
Regression gate (verified by the orchestrator, not the agent report):
full workspace suite green (0 failed), clippy --all-targets -D warnings
clean, and the byte-identity anchor mc_r_bootstrap_real_e2e ran the real
GER40 archive (6.83s, no skip) and passed byte-for-byte — the
equivalence gate is green.
Deferred debt, tracked, carry-on:
- #214 (arg-bundle consolidation): the fourth stacking of
too_many_arguments/type_complexity across the Generated*/translate_*/
*_args_from families. Deliberately deferred until all four verbs
dissolved; now unblocked — the recommended next iteration.
Ephemera removed at close (per the spec/plan lifecycle): the mc spec +
plan, plus a stray cycle-0110 fieldtest spec
(docs/specs/fieldtest-sweep-dissolution.md, added 36fbf49) that outlived
its cycle and was missed by the intervening closes.
refs #210
The FOURTH and LAST verb dissolution. `aura mc --strategy r-sma --real`
dissolves into thin sugar over the one campaign executor — a generated
[std::sweep(argmax), std::walk_forward, std::monte_carlo] campaign
document whose terminal monte_carlo stage reproduces the current
`mc_r_bootstrap` grade byte-for-byte (the committed anchor 3fc491a gates
it). Pure sugar, no executor change; mirrors the walkforward dissolution.
The one new load-bearing decision is the seed mapping: the campaign
carries the mc `--seed` (the wf winners are argmax hence
deflation-seed-independent, proven by the shipped walkforward anchor).
Fork C retention as walkforward: the built-in arm splits, run_mc_r_bootstrap
/ run_mc stay inline for the fenced synthetic/non-real paths (until #159).
Spec grounding-check PASS, auto-signed on #210 (2026-07-06).
refs #210
Cycle 3 of the verb-dissolution milestone (96dc783..a047af6). Architect drift
review over the range returned clean.
What holds:
- Pure sugar confirmed: `git diff 3a03617..a047af6 -- crates/aura-campaign
crates/aura-engine crates/aura-research` is EMPTY. All walkforward-specific
logic is confined to aura-cli. Invariant 10 (no verb-special engine logic) and
invariant 12 (one campaign path, via run_campaign_returning) preserved.
- No new campaign vocabulary: reuses StageBlock::WalkForward / WfMode::Rolling /
RiskRegime::Vol — the #160 3-copy vocab guard needs no update. C18:
content-addressed process + campaign + run recorded, e2e-verified.
- C10/C20: the stop binds as a single RiskRegime::Vol (structural, not a swept
param axis), consistent with the prior risk-regime cycle; Fork A refuses a
multi-value stop rather than silently re-semanticizing it.
- The two documented inline deviations are sound: full_window == wf_full_span on
the Real arm (both delegate to probe_window); raw_matches_wrapped correctly
matches wrapped fast/slow and the unwrapped stop.
Deferred debt (all [low], tracked, carry-on):
- Fork A narrows the surface: the inline path swept the stop as an IS-refit grid
axis (RGrid.stop_* are Vec); the dissolved path refuses a multi-value stop. The
byte-anchor covers the single-stop grade — "byte-for-byte" is scoped, not total.
Design-ratified on #210 (Fork A / the risk-regime model: the stop defines R and
is compared, never argmax-swept).
- The reducer's .expect()/unreachable! on report shape mirrors the inline
reducer's posture; anchor-guarded for today.
- The #214 arg-bundle is now at its third instance (rule-of-three trip point);
deferral to after the mc dissolution is deliberate (noted on #214).
Full workspace suite green; clippy --all-targets -D warnings clean; the
equivalence anchor walkforward_real_e2e_pins_the_exact_current_grade green through
the dissolution. Spec + plan removed (git rm) per the cycle lifecycle.
refs #210
Third verb dissolution: route `aura walkforward --strategy r-sma --real`
through the one campaign path as thin sugar, reproducing the summary line
byte-for-byte. Key finding (verified): this is PURE SUGAR — no executor
change — because the campaign walk_forward stage's per-window OOS reports
carry everything the summary needs (stitched_total_pips == the per-window
total_pips summed in roll order; pooled oos_r from the reports' in-memory
trade_rs). The built-in arm SPLITS: only r-sma-real dissolves; the synthetic
and non-r-sma-real branches keep the inline run_walkforward (Reading A). The
--select plateau path is carved out to #215; the equivalence anchor 96dc783
is the acceptance gate.
refs #210