Commit Graph

111 Commits

Author SHA1 Message Date
claude 394ac5e0ae test(cli): RED — empty --real window refusal misnames the symbol (#242)
An inverted or uncovered --from/--to window on a symbol the archive DOES
hold refuses via the symbol-absence message ("no local data for symbol")
instead of naming the window. RED pin:
run_real_empty_window_refusal_names_the_window_not_the_symbol
(archive-gated). Cause: probe_window routes both empty-in-window cases
through no_real_data although has_symbol already proved presence.

refs #242
2026-07-12 18:07:42 +02:00
Brummel 2532e8fcc3 test(cli): extract the shared demo-project fixture helpers into tests/common (#223)
built_project/project_lock/ScratchPath/ScratchGuard move from their three
per-binary copies (cli_run.rs, research_docs.rs, project_load.rs's
built_fixture near-copy) into tests/common/mod.rs; project_load's one
shared-store-mutating test now takes the lock it previously lacked. The
module doc states the boundary plainly: the Mutex serializes threads
within one process only — process-parallel runners still race on the
shared fixture store (documented, dormant under cargo test's sequential
binaries; see the issue thread for the observed process-parallel repro).

closes #223
2026-07-11 18:18:27 +02:00
Brummel 18f8e72946 feat(cli): chart resolves a --trace family by its chosen campaign name (#238)
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
2026-07-11 18:04:09 +02:00
Brummel c70b4cc2d2 test(cli): RED — chart cannot resolve a --trace family by its chosen name (#238)
--trace <NAME> lands the name in the generated campaign document, but no
lookup surface consumes it: the trace store knows only the content-derived
handle {campaign8}-{run}, so the user cannot address their own family by
the name they chose.

refs #238
2026-07-11 17:20:14 +02:00
Brummel a081c14532 test(cli): RED — mc --real refuses a short window instead of fitting the roller (#239)
The dissolved mc/walkforward dispatches stamp the fixed 90/30/30-day
roller into the generated process document regardless of the resolved
campaign window; a window under 120 days dead-ends at the executor with
no remedy reachable from the verb surface.

refs #239
2026-07-11 16:24:33 +02:00
Brummel abb6fbaa87 test(cli): RED — chart cannot open a sweep --trace family by its printed handle
Milestone-fieldtest finding B1: TraceStore::name_kind/read_family
resolve taps exactly one directory below the handle, so the depth-2
per-member layout sweep/walkforward --trace writes (#224,
<name>/<cell>/<member>/index.json) classifies as NotFound and
`aura chart <handle>` exits 1 — with a remedy string that suggests
re-running the very command that produced the data. The new autonomous
test fabricates the depth-2 family via TraceStore and pins: chart on
the printed family handle exits 0 and emits the HTML.

refs milestone fieldtest 09da04f
2026-07-11 14:27:28 +02:00
Brummel d3ad9f4e73 feat(cli): generalize no-window fallback resolves the intersection of all symbols' windows
The no---from/--to fallback in dispatch_generalize probes EACH listed
symbol's full archive window (same partial bounds threaded through) and
resolves the one shared campaign window as their intersection — latest
start, earliest end — instead of symbols[0]'s window alone, so the
cross-instrument floor is measured over a genuinely common period.
Disjoint archives refuse at runtime (exit 1) naming each symbol's span.
The intersect-or-refuse arithmetic lives in the pure
intersect_shared_window helper (unit-tested: single symbol, overlapping,
disjoint); the explicit --from+--to path stays byte-identical. The
superseded shape test is renamed to
generalize_without_explicit_window_resolves_a_shared_window_and_completes
with its doc comment reworded to the intersection contract.

closes #213
2026-07-11 14:02:41 +02:00
Brummel bb8c01895b test(cli): RED — no-window generalize resolves symbols[0], not the intersection
The new archive-gated e2e pins the #213 contract: a multi-symbol
generalize without --from/--to must resolve the shared campaign window
as the intersection (latest start, earliest end) of the listed symbols'
full archive windows. On the current tree it resolves symbols[0]'s full
window (AAPL.US listed first keeps AAPL's wider span on both bounds), so
the test fails RED. Expected windows are derived from the live archive
via single-symbol sweep probes, never hard-coded.

refs #213
2026-07-11 13:14:03 +02:00
Brummel 7748b53a10 feat(cli,docs): the r_channel OHLC example + ledger amendment C26 (#231 tasks 6-7)
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
2026-07-11 05:10:05 +02:00
Brummel 1d14ebc1cd feat(cli,engine): multi-column opening on every real-data path (#231 task 4)
The six Close-only open sites route through the resolved binding's
column set via open_columns (probe_window, open_real_source,
run_sources/windowed_sources, the campaign member open, the trace
re-run open) — a strategy declaring high/low/close roles gets exactly
those columns, merged in the canonical C4 order. Synthetic data refuses
any binding beyond {close} with the honest --real remedy (the walk
generates a close series only); the sweep/mc family builders ride their
established Err contract, run/walkforward/reproduce refuse exit-1.

Proof at two layers: an engine e2e composes an OHLC blueprint from data
and runs it over four inline VecSources to hand-computed rows (the
merge-order pin), and an archive-gated CLI e2e sweeps a high/low
blueprint over GER40 opening the High and Low columns.

Verified: engine OHLC e2e green, full workspace suite green, clippy -D
warnings clean.

refs #231
2026-07-11 03:37:14 +02:00
Brummel 00e15b9371 feat(cli): sweep/walkforward --trace writes every member's tap series (#168 refusal lifted)
--trace <fam> now translates through the verb sugar into a persist_taps
campaign-presentation block (was persist_taps: vec![] — the #168 no-op)
and persist_campaign_traces writes EVERY member of a selection-free
swept cell, one subdirectory per member under the cell dir, keyed by
the member's manifest params label in reproduce's exact format
(filesystem-sanitized, ordinal fallback); nominee cells keep their flat
layout. Members re-run at the cell's own stop regime and resolved pip,
streaming one member at a time, with the C1 drift alarm preserved per
member. The walkforward sibling lifts identically; run/mc --trace stay
refused by design (no campaign context / no family-name concept — their
help was already honest). Clap help lines and the chart NotFound hint
restored; the old #168 refusal pins re-pinned to the delivered
behaviour.

Post-review repairs: member_trace_key + cell_member_fanout extracted
pure and unit-pinned (6 tests), persisted_cells counts cells again,
cell-invariant space/geo hoisted out of the member loop, verb_sugar
tail comment corrected.

Verified: headline e2e green (GER40 archive), cli_run 124/0, full
workspace suite green, clippy -D warnings clean; independent spec
review compliant, quality review findings all repaired.

closes #224
refs #168
2026-07-10 22:16:09 +02:00
Brummel fe4ea0a08d test(cli): RED — sweep --real --trace refuses instead of writing tap series
Pins the #224 delivery headline: the #168 refusal lifts and at least
one non-empty tap-series file lands under runs/traces/. Layout-agnostic
on purpose — the per-member key convention is the GREEN slice's call.

refs #224
2026-07-10 21:13:52 +02:00
Brummel ed1e8a0d10 fix(cli): thread the resolved pip into wrap_r so real-data runs hit the graph broker
wrap_r hardwired SimBroker::builder(SYNTHETIC_PIP_SIZE); the resolved
geometry-sidecar pip reached only the manifest broker label, so every
real-data run computed total_pips/max_drawdown at 0.0001 regardless of
instrument (GER40 pip=1: inflated 10^4) while the label claimed the
real pip.

wrap_r now takes pip_size. Real-pip call sites pass the resolved pip:
run_signal_r (resolve_run_data reordered before the wrap),
run_blueprint_member, and the campaign trace re-run in
persist_campaign_traces (instrument_geometry lookup mirroring the
run_member refusal — required so the C1 drift alarm compares re-run
and nominee at the same pip). Probe-only call sites (param-space and
axis probes; metrics discarded) keep SYNTHETIC_PIP_SIZE explicitly.

The two golden stitched_total_pips pins in cli_run.rs rescaled by
exactly the 10^4 bug factor while n_trades/expectancy_r stayed
byte-identical — confirming R metrics were never pip-contaminated.

Alternative rejected: dropping pip-denominated metrics from real-data
reports — the manifest already stamps the resolved pip, so running the
graph at it restores label truthfulness without shrinking the report.

Verified: full workspace suite green (0 failed incl. the real-data e2e
suite), clippy -D warnings clean.

closes #232
2026-07-10 18:28:42 +02:00
Brummel 0408fc41a2 test(cli): RED — reproduce over real-data families diverges/panics (#229)
Two failing e2e pins (skip-on-no-data, GER40 archive present here):
- reproduce_real_sweep_family_re_derives_bit_identically: a --real sweep
  family must reproduce N/N bit-identically; currently 0/N DIVERGED because
  reproduce_family re-runs members over DataSource::Synthetic.
- reproduce_real_walkforward_family_does_not_panic: a --real WalkForward
  family must yield a verdict or a clean refusal; currently panics at the
  empty-OOS-window expect (main.rs:1123) for the same reason.

refs #229
2026-07-10 14:47:23 +02:00
Brummel fc9cf23b87 feat(cli,docs): ship the ganged open examples + document the gang construct (#61 tasks 7-8)
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
2026-07-10 14:07:22 +02:00
Brummel 692760d3b0 test(cli): RED — aura graph with a bad blueprint arg must be a usage fault (#28)
The #28 cycle-close audit found the just-shipped `aura graph <file>` silently
renders the embedded sample at exit 0 when the positional names an unreadable
blueprint (typo, nonexistent path, wrong extension): is_blueprint_file returns
None both for "no positional" (the legit sample default) and for a bad arg, and
dispatch_graph's None arm conflates them — re-introducing the exact #28 "I asked
for my graph, got the sample" surprise in the error path. This pins the fix: a
named-but-unreadable arg exits 2 like its siblings, while bare `aura graph` still
renders the sample.

refs #28
2026-07-10 03:46:30 +02:00
Brummel 7cc986486f test(cli): RED — aura graph must render a consumer blueprint, not the sample (#28)
aura graph is the last verb still sample-bound: its no-subcommand arm always
renders the embedded r_sma_open.json, ignoring any blueprint path, while
run/sweep/mc/walkforward/generalize consume a consumer blueprint file via
is_blueprint_file. This pins the target: aura graph <blueprint.json> renders
that file's own graph (its inlined model reflects the file's nodes, e.g.
r-breakout's channel_hi), not the embedded sample.

refs #28
2026-07-10 03:14:44 +02:00
Brummel 94cdbf90cc feat(campaign,cli): permit --select plateau in the walk_forward stage (#215)
The campaign walk_forward stage picked its per-window in-sample winner by
optimize_deflated (argmax) unconditionally, dropping the stage's `select`
field, and both the preflight and the `walkforward` verb's `--real` campaign
sub-branch refused plateau:* with a forward-pointer here (the #210 decision-4
deferral; the synthetic non-`--real` sub-branch has accepted plateau since
#173). Honour plateau selection when no gate precedes the wf stage.

- Preflight: the plateau-in-wf refusal is narrowed to gate-preceded stages.
  With no preceding gate the survivors are the full sweep grid, so the
  parameter lattice (grid.axis_lens) is exact and optimize_plateau is sound; a
  gate filters survivors below the grid, breaking the lattice, so plateau after
  any gate stays refused. The refusal is static — document validity must be
  data-independent (a gate's filtering depends on the data) — and keeps the
  existing "a gated survivor subset has no parameter lattice" message, which now
  describes exactly the only case it fires.
- Executor: run_walk_forward_stage threads the per-cell grid.axis_lens and picks
  each window's in-sample winner via select_sweep_winner — the same dispatch the
  sweep stage uses. (Argmax, deflate=true) is byte-identical to the old
  optimize_deflated call, so the argmax grade and content-id pins are unchanged;
  the plateau arms read axis_lens and ignore deflate.
- CLI: the walkforward verb threads --select through translate_walkforward /
  run_walkforward_sugar (a walkforward-local param, not the shared four-verb
  SugarInvocation), mapping the parsed Selection to the research SelectRule via
  select_rule_of; the refusal and its select_is_plateau helper are deleted.
  Scope is walkforward-only — the mc verb's wf-stage select is untouched.

The plateau:worst grade anchor (stitched_total_pips = -9683776.67) is distinct
from the argmax anchor (-10398606.67), proving the plateau path genuinely
changes winner selection rather than falling back to argmax. A gate-free plateau
process validates clean while a gate-preceded one is refused (a new
campaign-validate e2e). Argmax defaults stay byte-identical.

closes #215
2026-07-10 03:05:35 +02:00
Brummel abbfbdc208 fix(cli): refuse --trace on sweep/walkforward instead of silently dropping it (#168)
The clap help promised "persists each member's/OOS window's taps" and the chart
NotFound hint recommended `aura sweep --trace`, but every blueprint-mode arm
discarded the flag (run_blueprint_sweep's `let _ = persist`, the --real sugar's
persist_taps: vec![]) — the surface advertised trace-recording the code dropped.
Refuse --trace up front on both verbs (exit 2, "--trace is not yet available on
<verb>; see #224"), mirroring the existing aura run / aura mc refusals; drop the
false promise from the two help lines and the chart NotFound hint. Delivering
per-member trace-writing is deferred to #224.

Collateral: four synthetic-sweep tests that used --trace as a family-name handle
move to --name (same handle, still accepted); the walkforward name/trace test now
pins the up-front #224 refusal.

Workspace suite green (62 groups / 0 failed), clippy -D warnings clean.

refs #168
2026-07-09 23:47:32 +02:00
Brummel 53bbcedeec test(cli): RED — sweep/walkforward must refuse --trace with a #224 pointer (#168)
The clap help promises "persists each member's taps" and the chart hint
recommends `aura sweep --trace`, but every blueprint-mode arm discards the flag
(run_blueprint_sweep's `let _ = persist`, the --real sugar's persist_taps: vec![])
— the surface advertises trace-recording the code drops. Pin the decided honest
behaviour: sweep/walkforward refuse --trace up front (exit 2, naming the flag and
pointing forward to the deferred trace feature #224), mirroring the aura run / mc
refusals.

refs #168
2026-07-09 23:04:12 +02:00
Brummel f6d80486cd fix(cli): gate dissolved verbs on project presence, matching campaign run (#218)
A dissolved verb (sweep/generalize/walkforward/mc --real) run outside a project
silently created a content-addressed runs/ store in the cwd and ran to
completion — Env::runs_root falls back to a relative ./runs with no project, and
the four dispatchers skipped the provenance gate that `campaign run` applies
first (campaign_run.rs:322-333). It is now refused up front: one gate in the
shared validate_and_register_axes chokepoint, after axis validation and before
the first store touch (put_blueprint), via a new AxisRegisterError::NoProject
mapped to exit 1 carrying "<verb> needs a project …".

Placement is load-bearing: axis validation (an UnknownAxis usage error, exit 2)
stays ahead of the project gate, so a malformed --axis still exits 2 regardless
of project — a malformed command is malformed in any environment and already
writes no store. Four per-verb pins assert the refusal (exit 1, "needs a
project", no store); the six exit-2 usage-refusal tests stay green.

Workspace suite green (62 groups / 0 failed), clippy -D warnings clean.

closes #218
2026-07-09 22:40:48 +02:00
Brummel 2162bd6d97 test(cli): migrate dissolved-verb e2e tests into the project fixture (#218 prep)
The four dissolved verbs (sweep/generalize/walkforward/mc --real) are about to
gain a project gate that refuses outside a project (#218). 25 cli_run.rs tests
today run a well-formed verb to completion in a bare cwd and pin its grade /
store effect — i.e. they pin the pre-gate behaviour. Relocate them into the
built project fixture (fresh_project() over the demo-project cdylib, serialized,
store reset per test) so they still exercise the verb path once the gate lands.
Grades are byte-identical in-project (the fixture Aura.toml overrides no
instrument or data path — DEFAULT_DATA_PATH is absolute), verified against the
exact pins; local run 113 passed / 0 failed / 0 skipped with archive data.

Two new tests pin that the in-project run stamps manifest.project provenance
(the sweep chokepoint and generalize's run_blueprint_member path), a path no
cli_run.rs test previously exercised. The built_project/project_lock helpers are
duplicated into this binary to keep the change inside cli_run.rs; a shared
tests/common extraction is a separate hygiene item.

refs #218
2026-07-09 21:17:45 +02:00
Brummel 210230596e feat(cli): default the stop regime on walkforward/mc/generalize
GREEN side of the #217 executable-spec: --stop-length/--stop-k are now
optional on the three dissolved verbs; each missing flag independently
defaults to the single-sourced regime constants (length 3, k 2.0) at
the argv-boundary extractors — the same regime the campaign member
runner resolves for an unbound regime. The generated campaign document
still binds the regime explicitly, so the default spelling is
content-identical to an explicit --stop-length 3 --stop-k 2.0 (pinned
by registry dedup in the headline test). The multi-value stop refusal
survives byte-identically; sweep is untouched.

Sanctioned contract migrations (decision on the issue, 2026-07-09):
walkforward_dissolved_refuses_missing_knobs now pins the surviving
multi-value refusal; mc_args_from_refuses_missing_knobs pins the
default path. Usage strings name the defaults.

Held quality nit (deferred to cycle close, minimal-slice constraint):
the None=>default / Some=>parse stop block recurs 4x across the
walkforward/mc extractors.

closes #217
2026-07-09 16:38:05 +02:00
Brummel af8564d20b test: RED executable-spec for #217 — stop-less verb invocation defaults the regime
walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2
pins the decided contract (#217 decision comment, 2026-07-09): a
walkforward invocation without --stop-length/--stop-k succeeds, each
omitted knob defaulting to the single-sourced regime (length 3, k 2.0),
and produces a campaign document content-identical to the explicit
--stop-length 3 --stop-k 2.0 spelling (registry dedup: exactly one doc).
Currently RED: the verb refuses with the requires-knobs usage error.

refs #217
2026-07-09 16:10:30 +02:00
Brummel 4acea45519 chore(cli,docs): #220 cycle-close audit — doc reconcile + dispatch dedup
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
2026-07-09 15:35:47 +02:00
Brummel b849bb76b9 test(cli): weld-gone proof + new refusals for the generic verbs (#220 slice 3)
Task 5 of the verb-axis-generalization plan — the cycle's point, pinned:

- walkforward_campaign_runs_an_arbitrary_blueprint and
  mc_campaign_runs_an_arbitrary_blueprint drive r_breakout_open.json
  (wrapped channel_hi/channel_lo axes) over the real archive;
  generalize_campaign_runs_an_arbitrary_blueprint grades
  r_meanrev_open.json across two instruments. All three invocations
  were structurally impossible before this cycle (exit 2).
- New refusals: campaign mode without --axis (wf/mc), mc --real +
  --seeds mix, raw-form axis name before data access, unknown axis as
  a named Knob fault — each mirroring sweep's shipped contract.
- Final acceptance gates ran green in the loop: full workspace suite,
  clippy -D warnings, cargo doc warning-free, zero too_many_arguments
  allows in verb_sugar.rs, no RGrid in the workspace, no r_sma_open
  embed at the three verb dispatch sites (the aura graph default embed
  survives by design).

closes #220
closes #214
2026-07-09 15:17:51 +02:00
Brummel e7efe1f765 feat(cli): walkforward + mc go blueprint-generic (#220 slice 2)
Tasks 3-4 of the verb-axis-generalization plan:

- walkforward: the welded r-sma campaign branch moves out of the None
  arm into the blueprint mode, split on --real (campaign sugar) vs the
  unchanged synthetic family path; WalkforwardCmd drops
  --strategy/--fast/--slow; the full-window clip block, wf_ms_sizes and
  WINNER_SELECTION_METRIC move unchanged; the --select plateau #215
  refusal survives. walkforward_summary_json_from_reports is de-welded:
  the axis-name list is passed in from the invocation instead of the
  const AXES r-sma quadruple.
- mc: McCmd drops the welded flags and gains --axis; the blueprint arm
  splits on --real (campaign pipeline: requires --axis, refuses
  --seeds) vs the unchanged synthetic seed family (--seeds); block-len/
  resamples/seed defaults and the --name/--trace refusal stay
  byte-identical.
- translate_walkforward/run_walkforward_sugar and translate_mc/
  run_mc_sugar adopt SugarInvocation (+ WfWindows/McKnobs); ZERO
  #[allow(clippy::too_many_arguments)] remain in verb_sugar.rs (#214
  complete).
- Tests: wf/mc e2e groups migrate argv-only (grade anchors keep all
  pinned floats verbatim); collateral usage/retired-token tests assert
  clap's structural rejection of the deleted flags; the synthetic
  family control groups pass unmodified.

Necessary deviation from the plan's literal step text: re-added
#[allow(clippy::type_complexity)] on walkforward_args_from (clippy -D
warnings flags the 6-tuple; mirrors its siblings). Held quality nits
(validate/strip block now quadruplicated across the four dispatch fns;
inline exit-mapping recurrence) are deliberate plan-holds — dedup
follows as the rule-of-three cleanup at cycle end.

Verification: full workspace suite green in the loop's independent
verify; build + clippy -D warnings clean.

refs #220, refs #214
2026-07-09 14:48:59 +02:00
Brummel a2294c7c3a feat(cli): SugarInvocation + generalize goes blueprint-generic (#220 slice 1)
Tasks 1-2 of the verb-axis-generalization plan (spec auto-signed on a
grounding-check PASS, decision log on #220):

- verb_sugar gains the shared SugarInvocation<'_> carrier (#214) with
  VolStop and the factored doc_axes_from/risk_from/probe_params_from
  helpers; translate_sweep/run_sweep_sugar adopt it (no behaviour
  change — sweep argv and family output byte-identical), and
  translate_generalize/run_generalize_sugar follow, dropping their
  too_many_arguments allows (4 remain for wf/mc).
- generalize is de-welded: GeneralizeCmd takes a blueprint positional
  plus repeatable --axis <wrapped-name>=<value> (one value per axis);
  --strategy/--fast/--slow are deleted; dispatch_generalize runs the
  sweep sequence (read, wrapped-namespace validation, canonicalize +
  topology_hash registration, wrapped_to_raw_axis strip) with a new
  usage closure. RGrid is deleted workspace-wide.
- cli_run generalize tests migrate argv-only; the exact-grade anchor
  keeps all pinned floats verbatim; generalize_refuses_a_non_r_sma_
  strategy is deleted (its premise — a strategy gate — dissolves by
  design); the multi-value refusal now fires per --axis.

Verification: full workspace suite green in the loop's independent
verify; build + clippy clean. Held quality finding (kept as a
plan-hold): dispatch_generalize duplicates dispatch_sweep's
validation/strip block verbatim — dedup follows once wf/mc land the
same block (rule-of-three, end of this cycle).

refs #220, refs #214
2026-07-09 13:13:59 +02:00
Brummel 07a73fb66e docs(cli,ledger): sweep #159 demo-retirement drift — stale comments + ledger notes
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
2026-07-08 11:12:22 +02:00
Brummel ee7001647c feat(cli): retire the PIP demo pair (sma/momentum) to the generic path (#159 cut 4)
The two legacy pip-scored demos — the SMA-cross (`sma`/`Strategy::SmaCross`) and
momentum (`momentum`/`Strategy::Momentum`) — and the whole built-in *synthetic*
run/sweep/walkforward/mc + `macd` surface they anchored are dropped, not
migrated: both terminated in a `SimBroker(pip_size)` construction-arg node
absent from the zero-arg std vocabulary and reported the legacy pip yardstick
C10 retired with the realistic broker. With the R-family already migrated to
data (cuts 1-3) the `Strategy` enum held only these two, so the retirement
collapses the entire built-in-strategy machinery: `Strategy`, `HarnessKind`,
`strategy_from`, `run_sweep`/`sweep_family`/`momentum_*`,
`run_sample*`/`sample_*`/`sma_cross`, the built-in
`walkforward_family`/`sweep_over`/`run_oos` and `run_mc`/`mc_family`, the `macd`
composite cluster, and `run_dispatch`/`run_args_from`/`RunCmd.--harness`.

The generic surfaces survive untouched: `aura run <bp.json>`,
`aura sweep <bp.json> --axis`, `aura walkforward|mc --strategy r-sma --real`,
`aura campaign`, `aura generalize`. Grammar consequence (derived forks logged on
#159): `run`/`sweep` now require an explicit blueprint (no synthetic default);
`sma`/`momentum` join the retired r-breakout/r-meanrev tokens in the generic
`Usage:` error, pinned by a new negative
(`sweep_and_walkforward_reject_retired_pip_tokens_as_unrecognized`); bare
`aura graph` renders the shipped `examples/r_sma_open.json` (loaded via
`blueprint_from_json`), and the render self-containment test with it; the
`sample-model.json` viewer corpus + its two `.mjs` tests stay frozen as static
test data.

`member_key`/`MAX_KEY`/`fnv1a64` were removed too. The plan wrongly listed
`member_key` as a survivor; after the built-in sweep/mc retire it has no
production caller (campaign_run derives its own content key), so it and its unit
tests are genuinely dead. The spec-compliance gate surfaced this plan-deviation;
it was ratified after verifying the tree stays green.

Verified: `cargo build --workspace`, `cargo test --workspace` (0 failed), and
`cargo clippy --workspace --all-targets -- -D warnings` all clean.

refs #159
2026-07-08 02:14:43 +02:00
Brummel 257ab0b9f2 feat(std,cli): add the Scale node, retire the r-meanrev demo to data (#159 cut 3)
Cut 3 of the hard-wired demo retirement. Unlike r-sma/r-breakout, r-meanrev could
not round-trip as data: its band computes band_k*sigma (a constant times a stream)
and the closed 22-node std_vocabulary had no way to express it (EqConst is i64->bool,
Bias is clamp(signal/scale,±1), Mul is binary, LinComb is excluded as a
construction-arg node; no Div/Const/constant-emitter). Standard operators are missing
by chance, not by design, so this adds the one r-meanrev needs.

Scale node (aura-std): out = input * factor — one f64 input, one f64 `factor` param,
stateless, warm-up-filtered (the EqConst/Bias shape). A zero-input `Const` emitter was
rejected: the per-cycle eval loop gates every node through an input-firing test
(harness.rs `fires()`), so a node with no input never fires without an engine-core
change; `Scale` fits the existing model and, by IEEE-754 commutativity, reproduces the
retiring LinComb's band byte-for-byte. Rostered (`"Scale" => Scale`); the two
vocabulary count-pins bump 22 -> 23.

r-meanrev migration: r_meanrev_signal(window, band_k) is carved out of the fused
r_meanrev_graph as a #[cfg(test)] price->bias Composite whose band is `Scale` in place
of `LinComb(1)`; production loads the shipped JSON (examples/r_meanrev{,_open}.json),
never a builder. Before the fused builder was deleted, an equivalence test proved the
carved Scale-band signal reproduces its grade byte-for-byte on the synthetic stream
(window=3, band_k=2). Durable survivors: the byte-identity of the examples to the
carve, the loaded-grades-identically-to-carve proof, the closed/open introspect
anchors (mean_window.length, var_window.length, band.factor — band_k is now a
first-class sweepable param), and — re-pointed onto the carve rather than dropped —
the fades-short-above/long-below behavioural test, which becomes the durable
carve-correctness gate the deleted equivalence test used to be.

Deletions + the dead-code cascade the retirement opened: r_meanrev_graph,
r_meanrev_sweep_family, Strategy::RMeanRev (+ all arms). r-meanrev was the last reader
of run_sweep's grid, so the whole vestigial built-in-sweep grid apparatus retires with
it: run_sweep's `grid` param, the sweep call-site grid build, and SweepCmd's
--fast/--slow/--stop-length/--stop-k/--window/--band-k (sma/momentum sweeps build their
own blueprints and ignored these; the fast/slow/stop-* had been vestigial since r-sma's
cut 1b). RGrid the struct SURVIVES — the dissolved `generalize` verb resolves its
candidate from its fast/slow/stop_length/stop_k via generalize_args_from — so only its
window/band_k fields go; its stale doc-comment (r-sma-sweep / persist_traces_r / CLI
flags, all now gone) is rewritten to its true generalize-only role. Also retired as
transitively dead: persist_traces_r and the metrics_object test helper (their only
callers were the deleted families/tests); Add/Gt/Latch/Mul/Sqrt imports are now
#[cfg(test)] (their last production caller was r_meanrev_graph).

Test surface: the two built-in --strategy r-meanrev sweep tests drop; a new negative
pins that --strategy r-meanrev on both sweep and walkforward now falls into the generic
usage error; the grid-flag stray-positional negative is re-pointed to a surviving flag.

Verification (own, not the workflow's report): full `cargo test --workspace` green
(0 failed across 62 result lines); `cargo clippy --workspace --all-targets -- -D
warnings` clean, no dead-code residue; the r-sma + r-breakout anchors and the
dissolved-verb real goldens (generalize still reads RGrid) stay green. Bundled as one
commit (the Scale node's count-pin and the r-meanrev anchors share graph_construct.rs,
so a clean two-commit split was not path-separable); the implement-loop ran all three
plan tasks in one pass and the tree was verified by hand.

refs #159
2026-07-07 22:02:50 +02:00
Brummel 5856cadadd refactor(cli): retire the r-breakout demo — topology now lives only as data (#159 cut 2)
Cut 2 of the hard-wired demo retirement, applying the r-sma template one level
down. The r-breakout signal leg was fused into r_breakout_graph (which also built
the whole pip/R harness inline), so this cut first carves the signal leg out as a
pure price->bias Composite, ships it as a proven blueprint example, then deletes
the fused builder and the built-in --strategy r-breakout surface. The r-breakout
topology now exists only as data (crates/aura-cli/examples/r_breakout{,_open}.json).

Carved: r_breakout_signal(channel) -> Composite, a verbatim lift of the fused
builder's signal leg (Delay/RollingMax/RollingMin/Gt/Latch/Sub) exposing the raw
exposure as "bias"; the generic wrap_r/run_signal_r path (unchanged) supplies the
pip/R harness. It is #[cfg(test)] — its only role is regenerating the examples and
pinning them to a faithful serialisation; production loads the shipped JSON, never
this builder (so no dead-code residue, no production topology constructor — #159's
invariant).

Proof: before the fused builder was deleted, an equivalence test proved the carved
signal, wrapped and run, reproduces the retiring r_breakout_graph grade byte-for-byte
on the synthetic stream at channel=3 with the shared default R-stop (metric
computation is identical on both paths). That gate is deleted with the builder it
references; the durable anchors that survive are the byte-identity of the shipped
examples to the carve, the loaded-example-grades-identically-to-the-carve proof, and
the closed-ness / open-axis introspect anchors (channel_hi.length, channel_lo.length
per-node — ganging is the separate #61).

Deleted: r_breakout_graph, r_breakout_sweep_family (aura sweep --strategy r-breakout),
Strategy::RBreakout + all four match arms, the r-breakout token from the --strategy
usage strings and docs, and the now-dead --channel flag + RGrid.channel field (its
sole reader was r_breakout_sweep_family). The data successors: aura run
examples/r_breakout.json, and aura sweep examples/r_breakout_open.json --axis
channel_hi.length --axis channel_lo.length.

Test surface: the three built-in-surface sweep tests drop; the walkforward
valid-but-unsupported-token test is re-pointed to momentum (the property is generic
and stays live until Cut 4); and a new negative pins that --strategy r-breakout on
both sweep and walkforward now falls into the generic usage error, not a
special-cased message — catching a half-finished retirement that left the token
parseable.

Verification (own, not the workflow's report): full `cargo test --workspace` green
(0 failed across every binary); `cargo clippy --workspace --all-targets -- -D warnings`
clean, no dead-code residue; the r-sma introspect + grade anchors and the
dissolved-verb real goldens stay green. The implement-loop ran both plan tasks in one
pass (task_range was not honoured); the tree was verified by hand — grep-confirmed the
retired surface is gone and the carve is #[cfg(test)]-scoped.

refs #159
2026-07-07 19:25:13 +02:00
Brummel f7c809e5ce refactor(cli): retire the r-sma demo builder — topology now lives only as data (#159 cut 1b)
Cut 1b of the hard-wired demo retirement — the removal half. With the proven
example shipped in cut 1, delete the r-sma builder and its whole transitive
closure so no production function constructs the r-sma topology any more; it
exists only as data (crates/aura-cli/examples/r_sma{,_open}.json).

Deleted: sma_signal (the builder), r_sma_graph, run_r_sma (aura run --harness
r-sma), r_sma_sweep_family (aura sweep --strategy r-sma), the synthetic inline
r-sma walkforward/mc machinery (r_sma_space/r_sma_sweep_over/run_oos_r/
run_mc_r_bootstrap/mc_r_bootstrap_report + the walkforward_family RSma arm), both
fixture emitters, the round-trip + cut-1 byte-fidelity tests, the R_SMA_FAST/
SLOW/BIAS_SCALE consts, and the Strategy::RSma / HarnessKind::RSma / McArgs::RealR
variants. The three dissolved-verb synth sites (generalize/walkforward/mc
--strategy r-sma --real) now embed the shipped open example via include_str!
instead of building it — byte-safe, guarded by the unchanged dissolved-verb
real-archive goldens. The two now-redundant fixtures move to the examples
(deleted, ~40 readers repointed). The --cost-* flags on `aura run` retire with
the harness (their sole consumer was run_r_sma; cost-model coverage survives
engine/std/composites-level).

The retired demo surfaces (--harness r-sma, sweep/walkforward/mc --strategy r-sma
inline) have working data successors: aura run examples/r_sma.json, aura sweep
examples/r_sma_open.json --axis …, and the --strategy r-sma --real campaign path.

Deviation from the plan, noted: StopArm collapsed to StopRule in wrap_r's
signature (its only other variant, Open, died with r_sma_graph) — cleaner than
the planned vestigial single-variant enum; behaviour-preserving, touching
campaign_run.rs and the r-breakout/r-meanrev call sites.

Follow-up (not this cut): wrap_r's cost-node-building branch is now unreachable
(nothing passes Some(CostConfig) after run_r_sma's deletion) yet clippy-clean —
a latent cleanup for when the inline cost surface fully retires.

The stage1-r param-suffix remapping (r_sma_friendly_name + the FAST/SLOW/
STOP_LENGTH/STOP_K_SUFFIX consts) went dead and was removed, closing #167 by
deletion.

Verification (own, not the workflow's report): full `cargo test --workspace`
green; `cargo clippy --workspace --all-targets -- -D warnings` clean, no
dead-code residue; the cut-1 grade anchor (shipped_r_sma_example_reproduces_the_
builtin_grade) and the dissolved-verb real goldens (generalize/walkforward/mc
_dissolves_through_the_campaign_path) stay byte-identical. A first workflow
attempt was discarded after a subagent's `git checkout -- main.rs` recovery
silently reverted earlier tasks' edits (filed on the plugin tracker as
Brummel/Skills#23); this landing is the hardened re-run, verified by hand.

closes #167
refs #159
2026-07-07 17:41:59 +02:00
Brummel d7fa4b4743 feat(cli): ship r-sma demo as a proven blueprint-data example (#159 cut 1)
Cut 1 of the hard-wired demo retirement — the copy+prove phase. The r-sma demo
strategy now ships as runnable blueprint data under crates/aura-cli/examples/
(r_sma.json closed 2/4, r_sma_open.json open), emitted from the current builder
so it is byte-for-byte the builder's serialization.

Two load-bearing proofs, captured while the builder is still alive:
- byte-fidelity: the shipped examples equal blueprint_to_json(&sma_signal(..)) —
  the "the copy is identical to the original" evidence the retirement method
  requires before any original is removed;
- grade anchor: `aura run examples/r_sma.json` reproduces the built-in
  `--harness r-sma` metrics byte-for-byte (expectancy_r/sqn/total_pips/n_trades).

Plus two property tests on the shipped copies (beyond the plan, kept as on-topic
proof strengthening): the closed example introspects to zero unbound params
(genuinely closed), and the open example lists exactly its two SMA-length axes
from the public gallery location.

Purely additive: the builder (sma_signal) and every inline r-sma surface are
retained. The recon showed deleting the builder has a large transitive closure
(the synthetic inline surface, the Strategy::RSma enum, ~40 integration tests);
that removal is cut 1b, a separate cycle, made safe by the byte-fidelity proof
frozen here.

Full workspace suite green; clippy -D warnings clean.

refs #159
2026-07-07 12:40:14 +02:00
Brummel 7f5f576f41 feat(cli): dissolve aura mc --strategy r-sma --real (#210)
The FOURTH and LAST verb dissolution. `aura mc --strategy r-sma --real`
now runs as thin sugar over the one campaign executor: a generated,
content-addressed [std::sweep(argmax), std::walk_forward, std::monte_carlo]
campaign document whose terminal monte_carlo stage produces the same
StageBootstrap::PooledOos(RBootstrap). The sugar reads it from
run.outcome.record.cells[].stages[].bootstrap and reprints the existing
mc_r_bootstrap_json — one line, byte-identical to the inline path.

Pure sugar, NO executor change (aura-campaign/aura-engine/aura-research
diff-free). Mirrors the shipped walkforward dissolution:
- verb_sugar.rs: GeneratedMc, translate_mc, register_generated_mc,
  run_mc_sugar (record-read + one grade line, no member lines).
- main.rs: mc_args_from + the dispatch_mc built-in-arm split gated on
  `--strategy r-sma && --real`.

The one new load-bearing decision is the seed mapping: translate_mc sets
campaign.seed = the mc --seed. The wf winners are argmax hence
deflation-seed-independent (proven by the shipped walkforward anchor), so
the pooled OOS series is unchanged while the terminal r_bootstrap at that
seed reproduces the inline bootstrap. A new e2e
(mc_dissolved_seed_changes_the_bootstrap_draw_not_the_pooled_series)
pins exactly this: two --seed values yield identical n_trades but a moved
E[R] mean.

Fork C retention (as walkforward): the arm splits, run_mc_r_bootstrap /
run_mc stay inline for the fenced synthetic/non-real paths (until #159).
The three synthetic-r-sma tests use no --real, so they never enter the
sugar branch and run the inline path unchanged. --name/--trace are
rejected on the sugar path, matching the inline mc r-path.

Verification: the committed byte-identity anchor
mc_r_bootstrap_real_e2e_pins_the_exact_current_grade runs the real GER40
archive and passes byte-for-byte through the new path; full workspace
suite green; clippy --all-targets -D warnings clean.

refs #210
2026-07-07 00:23:33 +02:00
Brummel 3fc491a058 test(cli): pin the exact mc R-bootstrap grade — byte-identity anchor (#210)
The fourth and last verb dissolution (mc's R-bootstrap path) reroutes the inline
`walkforward_family -> pooled_oos_trade_rs -> r_bootstrap` through the campaign
`[std::sweep(argmax), std::walk_forward, std::monte_carlo]` process, whose terminal
monte_carlo stage does the identical `StageBootstrap::PooledOos(r_bootstrap(...))`
seeded from the campaign seed. Before that rewire, this pins the EXACT current
bootstrap grade of a fixed 2025 GER40 invocation (block_len=5, resamples=1000,
seed=42, multi-point fast 3,5 x slow 12,20 grid) so the dissolution must reproduce
it byte-for-byte (the acceptance gate).

The load-bearing subtlety the dissolution rests on: the campaign path uses ONE
campaign seed for both the wf-stage deflation and the mc-stage bootstrap, whereas
the inline path uses a fixed DEFLATION_SEED for the wf winners and the mc `--seed`
for the bootstrap. This reconciles because the wf winners are argmax (deflation
only annotates provenance), so they are seed-independent — the walkforward anchor
already proved it (the campaign path at seed 0 reproduced the inline
DEFLATION_SEED winners byte-for-byte). So `campaign.seed = mc --seed` leaves the
pooled OOS series unchanged and makes the bootstrap match. n_trades=20681 matches
the walkforward multi-grid anchor, confirming mc pools the same OOS-R series.
Deterministic; gated on the local GER40 archive, skips cleanly on a data refusal.

refs #210
2026-07-06 23:21:24 +02:00
Brummel a047af65fb feat(cli): dissolve aura walkforward --strategy r-sma --real (#210 T3-T4)
The dispatch split lands: `dispatch_walkforward`'s built-in arm now routes
`--strategy r-sma --real` through the one campaign path (`run_walkforward_sugar`
-> generated campaign doc -> executor -> summary reprint), reproducing the
`{"walkforward":{…}}` line byte-for-byte. Everything else — synthetic (any
strategy) and non-r-sma real — stays on the inline `run_walkforward`, fenced
until #159 (Reading A). The committed anchor
`walkforward_real_e2e_pins_the_exact_current_grade` passes: the dissolved path
reproduces windows=9, stitched_total_pips, oos_r, and the param_stability means
exactly. `--select plateau:*` is refused (exit 2 -> #215, Fork B); a multi-value
stop is refused (Fork A).

Two plan guesses were wrong and the anchor + real-data e2e caught both; the fixes
are documented inline:

- Window resolution: the plan mirrored generalize's `(--from,--to) -> (f,t)`
  shortcut, but the inline `wf_full_span` ALWAYS clips `--from`/`--to` to the
  archive's actual first/last bar (`probe_window`), even when both flags are
  explicit — a holiday/weekend edge shifts every IS/OOS window's calendar
  placement (stitched_total_pips diverged by ~5.16M, param_stability means intact,
  confirming only window boundaries moved). The dissolved path now calls
  `source.full_window(env)` unconditionally, matching the inline behaviour;
  verified byte-for-byte against the still-fenced inline path.
- Reducer axis match: the campaign path records the blueprint axes wrapped
  (`sma_signal.fast.length`) while the stop rides unwrapped via the risk regime, so
  the reducer's exact-name match panicked on real data. Fixed with the existing
  `campaign_run::raw_matches_wrapped` (#203) wrap-convention helper.

Tests: the dissolution-proof e2e `walkforward_dissolves_through_the_campaign_path`
(one process + one campaign + one campaign-run auto-registered, windows=9) plus
front-end refusal coverage (multi-value stop, `--select plateau`, missing knobs,
empty `--real`, unknown `--select`, `--name`/`--trace` exclusivity). Full workspace
suite green; `clippy --all-targets -D warnings` clean.

refs #210
2026-07-06 23:05:08 +02:00
Brummel 96dc7832e5 test(cli): pin the exact walkforward grade — byte-identity anchor (#210)
The third verb dissolution (walkforward) reroutes the inline per-window
IS-refit (`walkforward_family` -> `select_winner` -> OOS run -> stitch/pool)
through the campaign `std::walk_forward` stage. Two summary fields have no
campaign equivalent today: `stitched_total_pips` (the runner discards the raw
OOS pip curve, `oos_equity: vec![]`) and pooled `oos_r` (only a following
`std::monte_carlo` stage produces it). Before the executor work that closes
those gaps, this pins the EXACT current grade of a fixed 2025 GER40 invocation
so the dissolution must reproduce it byte-for-byte (the acceptance gate).

The grid is deliberately multi-point (fast 3,5 x slow 12,20): it makes the
per-window winner selection non-degenerate (`param_stability` varies across the
9 windows), so a winner-pick divergence between the inline and campaign refit
cannot ship green — the same gap the generalize anchor closed for its sibling
shape-only test. Deterministic (C1); gated on the local GER40 archive, skips
cleanly on a data refusal.

refs #210
2026-07-06 20:47:09 +02:00
Brummel f5bb38ed59 test(cli): dissolution-proof e2e for generalize (#210 T4)
`generalize_dissolves_through_the_campaign_path` asserts the verb now runs
through the one campaign path: exactly one generated process document, one
generated campaign document (carrying the --name handle and the stop as a
non-empty risk regime), and one campaign-run record are durably auto-registered,
plus the campaign-path family set (1 CrossInstrument grade family + 2
per-instrument Sweep families). The observable proof the inline path is gone.

refs #210
2026-07-06 18:00:07 +02:00
Brummel 256ec7320c feat(cli): dissolve aura generalize into the campaign path (#210 T3)
`dispatch_generalize` synthesizes the bare `sma_signal` blueprint, resolves the
window (explicit --from/--to, else the first symbol's full window), and calls
the new `run_generalize_sugar`; the inline `run_generalize` is deleted. The
sugar builds the selection-bearing campaign doc, runs it through
`run_campaign_returning`, reprints the byte-identical `{"generalize":…}` +
`{"family_id":…}` lines, and persists the CrossInstrument family.

The exact-grade anchor stays green through the path shift — proving the two stop
mechanisms (former grid axis, now the risk-regime seam -> StopRule::Vol) yield
identical R. The family pin relaxes to "exactly one CrossInstrument family"
since the campaign executor now also persists a per-instrument Sweep family per
cell (the durable audit trail; #210 Q4). Two new e2e pin the window fallback and
the family member attribution order.

Deviations from the plan (necessary, verified): cross_instrument_members returns
Vec<aura_engine::RunReport> (aura_registry::RunReport is not pub); a shared
validate_before_register helper is factored out of run_sweep_sugar (DRY); the
run_generalize doc-comment and the now-unused `generalization` import removed.

refs #210
2026-07-06 17:42:36 +02:00
Brummel f3f32b8269 test(cli): pin the exact generalize grade — byte-identity anchor (#210)
The current `aura generalize` binds its protective stop as a grid axis
(`r_sma_sweep_family` with `stop_open=true`); the pending generalize
dissolution rebinds the same stop through the risk-regime seam
(`RiskRegime::Vol -> StopRule::Vol`). The existing generalize e2e
(`generalize_grades_a_candidate_across_two_instruments`) asserts shape
only — `worst_case` and the per-instrument R floats are never checked —
so a divergence between the two stop bindings would ship green.

This adds `generalize_real_e2e_pins_the_exact_current_grade`, capturing
the exact current R grade of the identical invocation (GER40 0.0105637…,
USDJPY 0.0057959…, worst_case 0.0057959…, sign_agreement 2). Green today
against the inline path; after the dissolution the same command must
reproduce these bytes — the acceptance gate that catches a stop-mechanism
divergence loudly instead of silently.

Resolves the generalize-dissolution grounding block (the unpinned
stop-mechanism R equivalence) by the risk-regime characterization-pin
pattern. refs #210
2026-07-06 15:49:46 +02:00
Brummel c62f7e9878 feat(cli): bind the resolved regime stop and stamp it into the manifest (#210 T4)
The runtime-binding slice — the regime's stop now actually reaches the graph and
the record:

- `StopArm { Open, Bound(StopRule) }` replaces `wrap_r`'s `stop_open: bool` so a
  bound stop carries its params; all call sites threaded (the r-sma sweep path
  stays Open, every bound site passes the resolved rule).
- `run_blueprint_member` takes a `stop: StopRule` and stamps `stop_length` /
  `stop_k` into the member manifest (the ratified C18 extension — the stop is no
  longer invisible on the campaign/blueprint path); its callers thread the
  default, and `CliMemberRunner` maps `cell.regime` (None -> baked default,
  Some(Vol) -> that vol stop).
- The characterization pin flips from asserting the stop's ABSENCE to requiring
  the stamped default; a real two-regime e2e pins that each regime stamps its
  own k.

Known deferred gap (concern, documented descope): `persist_campaign_traces`
re-runs the nominee to persist its traces and — since `CellRealization` carries
no regime — binds that re-run to the DEFAULT stop, so a non-default-regime
nominee's persisted trace would reflect the wrong stop. Only the persist path
(non-empty `persist_taps`) is affected; the core run/stamp/generalize path is
correct. Filed as a follow-up. Full workspace suite green.

refs #210
2026-07-06 14:52:20 +02:00
Brummel 455916db73 test(cli): pin the stop-less campaign member manifest (risk-regime grounding)
The dissolved real-data sweep member manifest carries only the swept signal
axes today — no stop param; the R-path stop is a wrap_r constant baked outside
the signal param_space. The existing member-line pin
(sweep_real_blueprint_member_lines_pin_the_dissolved_contract) locates each
binding by `find`, so it never pinned this absence. This characterization pin
does, as the grounding ratification for the risk-regime-axis design pass: the
resolved-regime manifest stamp will flip it to require the stop key — the
ratified additive contract extension, exactly like the instrument stamp.

refs #210
2026-07-06 13:04:55 +02:00
Brummel c9d962f0ea fix(cli): sweep-entry ergonomics — honest refusals, no store litter (#210 c0110 fieldtest)
Three test-first fixes from the cycle-0110 fieldtest findings:

- Axis-name preflight on the dissolved sweep arm: every --axis name is
  checked against the wrapped probe namespace (the --list-axes names)
  BEFORE the #203 strip; an unknown or raw-form name is refused
  house-style echoing exactly what the user typed, with a --list-axes
  pointer — no more stripped-then-mangled fragments from the deep
  referential refusal. Data-free, fires before the archive is touched.

- Validate-before-register in the sweep sugar: intrinsic doc checks +
  executor preflight + the pure bind_axes coverage check (reused, not
  re-inlined; now pub(crate)) all run in-memory before
  register_generated — a refused invocation leaves no generated
  document in the content-addressed store and no campaign_runs.jsonl
  line (previously P2/P3 refusals littered runs/campaigns, one doc
  malformed).

- blueprint_slot_prose: the sweep/walkforward/mc loaded-blueprint slots
  share one dispatch-boundary presenter — an op-script array gets a
  targeted 'run aura graph build first' hint, every load error goes
  through blueprint_load_prose + the missing-project hint, and the raw
  {e:?} Debug leak (#184's pattern, deliberately left on these three
  slots back then) is gone. Two stale Debug-leak pins flipped, two new
  refusal tests.

Suite 1060/0, clippy -D warnings clean.

refs #210
2026-07-04 19:48:57 +02:00
Brummel b7aaa0ba59 feat(cli): dissolve the real-data blueprint sweep into the campaign path (#210 T4)
aura sweep <bp.json> --real ... --axis ... now runs as thin sugar over
its generated campaign document through the one campaign executor: the
dispatch registers the canonical blueprint (topology_hash IS
content_id_of over the same canonical bytes, so the strategy ref
resolves against the existing single store write), strips the CLI's
wrapped axis names to the raw campaign namespace (#203 bind
convention), converts the probed archive window back through the
ingest seam's own epoch_ns_to_unix_ms (made pub; C3 — the seam owns
the ms/ns convention, never an inline division), and hands off to
verb_sugar::run_sweep_sugar. The inline real-data arm is gone; the
synthetic arm and --list-axes are untouched.

campaign_run gains RunPresentation {Full, MemberLinesOnly}:
run_campaign keeps Full (record line unchanged, project gate
unchanged); the sugar path runs MemberLinesOnly — the generated doc's
emit already limits stdout to family_table member lines, the mode
suppresses only the final record line. The record append is identical
in both modes; no project gate on the by-id path (the dissolved verb
works project-less, exactly as before).

The characterization pin survives the re-cut with exactly the one
sanctioned assertion flip (instrument-absence -> instrument==GER40)
and now also pins the generated artifacts: one process + one campaign
document auto-registered (dedup on a second identical invocation),
raw axis keys in the stored document, the stored window a ms sub-range
of the request, C1 at the member-report level with the registry's
run-suffix increment isolated.

Suite 1055/0, clippy clean, gated real-data paths executed locally.

refs #210
2026-07-04 19:01:27 +02:00
Brummel 8235cee4d7 test(cli): characterization pins ahead of the sweep dissolution
Two green pins the sweep-dissolution cycle rests on (refs #210):

- sweep_real_blueprint_member_lines_pin_the_inline_contract: the inline
  real-data blueprint sweep prints one member line per grid point in
  odometer axis order, carries the swept bindings in manifest.params,
  persists exactly one Sweep family — and never stamps an instrument
  key (RunManifest::instrument stays None on this path).
- campaign_run_real_e2e_sweep_gate_walkforward (extended): every
  family_table member line emitted by the real campaign member-runner
  path is stamped with the instrument under test.

Together they pin the one behavioural delta between the two paths (the
additive instrument stamp) that the dissolution's parity contract is
phrased against.
2026-07-04 16:31:30 +02:00
Brummel 61c8828fad feat(cli): house-style loader-error prose + missing-project hint on the run path
aura run leaked the raw LoadError Debug form (UnknownNodeType("..."))
while graph build already phrased the same fault house-style (#162
convention: engine error types are Display-free, the CLI presents them).
Route the run path through the shared blueprint_load_prose presenter (now
pub(crate), env-agnostic) so both surfaces phrase a LoadError identically
and neither leaks the Debug token (#184). Add unresolved_namespace_hint:
when the unresolved id looks project-namespaced (::) and no project was
loaded (env.provenance() is None), append a hint pointing at the missing
Aura.toml — the newcomer running the host from the wrong directory now
gets an orienting diagnosis instead of a bare type error (#185). The hint
is applied on both the run and graph-build paths. RED-first: the two
existing run-path pins were flipped to assert the prose + absence of the
Debug token before implementing; suite green (1043/0).

closes #184
closes #185
2026-07-04 15:19:58 +02:00
Brummel 9402afe958 polish(cli): normalize usage-message casing to the clap house style
Every hand-rolled usage line now reads 'Usage: aura <verb> ...' — capital
prefix plus the program name, the shape clap itself and the built-in run
branch already spoke. 10 literal edits: 6 lowercase 'usage:' flips (graph
introspect, run blueprint x2, runs family, mc blueprint, mc built-in), 4 bare
closures gain the prefix (sweep/walkforward, blueprint + built-in branches);
the mc built-in line names the program in both |-separated alternatives.
Refusal diagnostics, the conforming run_args_from literal, clap output, and
the fieldtests corpus stay byte-identical; exit codes keep the usage=2 /
runtime=1 partition.

Lockstep pin: the single casing-sensitive assertion (mc --real) flips to
capital 'Usage'. Three additive regression tests pin the house style itself
(bare-closure prefix, both mc alternatives, mc blueprint branch) — the
pre-existing pins checked only verb tokens / a single Usage occurrence;
deviation from the spec's no-new-test-bytes wording recorded on the issue.

Verification: cargo test --workspace rc=0 (cli_run 133 passed), clippy -D
warnings clean, grep gates exact (zero lowercase 'usage:' literals; 11
'Usage: aura' source lines), smoke 'aura sweep --strategy bogus' prints the
house-style line and exits 2.

closes #179
2026-07-02 12:56:23 +02:00
Brummel 4de6d5cbad rename: retire the stage1-* family for the r-family (r-sma / r-breakout / r-meanrev)
The stage1 ordinal has been dead vocabulary since the C10 reframe dropped
the two-stage research model: a 1 structurally implies a 2 that no longer
exists. The family is renamed by its live discriminator - the R yardstick -
with members named by their signal, uniform with their signal-named
siblings (sma, macd, momentum):

- selectors: stage1-r -> r-sma, stage1-breakout -> r-breakout,
  stage1-meanrev -> r-meanrev (old tokens are usage errors, exit 2 - no
  silent alias)
- identifiers: Strategy::RSma/RBreakout/RMeanRev, HarnessKind::RSma,
  r_sma_*/r_breakout_*/r_meanrev_*, wrap_r, run_signal_r, RGrid, R_SMA_*
- persisted identity: the sma_signal composite (param prefix
  sma_signal.fast.length / .slow.length; fixtures regenerated; content-ids
  shift - no test pins a literal hash, the registry parses no record names)
- e2e test files git-mv'd to r_sma_e2e / r_breakout_e2e / r_meanrev_e2e
- dead Stage-1/Stage-2 prose reworded to post-C10 vocabulary across
  rustdoc, Cargo.tomls, ledger live lines, and the glossary (historical
  entries stay; fieldtests corpus untouched)
- CLAUDE.md invariant 7 rewritten to record the ratified C10 reframe
  faithfully (the token-swap alone would have laundered the retired
  gated-currency/realistic-broker design into unmarked live prose); the
  unbacked account-mode clause dropped

Verification: cargo build/test --workspace green (51 targets), clippy
-D warnings clean, doc build clean, acceptance grep gate leaves exactly
the one resampling-stage false positive (harness.rs), smoke: --harness
r-sma runs, --harness stage1-r exits 2 with the new usage line.

Decision log: forks and rationale recorded on the issue (reconciliation
+ implementation-phase comments).

closes #174
2026-07-02 12:03:09 +02:00
Brummel 88e9c9a539 fix(cli): symmetric sweep/mc family-verb polish (closed error, mc seed label, mandatory-knob docs)
Three polish items surfaced by the World/C21 milestone fieldtest:

- A sweep of a fully-bound (closed) blueprint is refused up front with a clear
  "this blueprint is fully bound; nothing to sweep" message — the symmetric
  inverse of blueprint_mc_family's closed-blueprint guard — instead of leaking a
  misleading UnknownKnob for a knob that is not unknown but bound out.
  blueprint_sweep_family's error contract widens to String; the sweep terminal's
  BindError (a genuine unknown/kind-mismatch axis) still surfaces verbatim.
- mc reproduce lines now carry each member's seed=<N> label. Monte-Carlo members
  hold no tuning params, so the member label was blank; the seed is each draw's
  realization identity. Sweep / walk-forward labels (which echo params) unchanged.
- docs (design ledger + glossary): state that every open knob --list-axes prints
  is mandatory on sweep / walkforward — a subset is refused with the missing knob
  named (BindError::MissingKnob), there is no default; pin a knob you do not want
  to vary with a single-value axis.

RED-first for the two behaviour items. One existing unknown-axis E2E is re-pointed
from the closed fixture (which now hits the new "nothing to sweep" pre-check) to
the open fixture; it still exercises UnknownKnob("nope") specifically — resolve
raises UnknownKnob in phase 1 before MissingKnob in phase 2 — so the property is
preserved, not weakened.

closes #178
2026-07-02 09:41:54 +02:00