5eedf060dd02d1e8da66ab44c3cccbb4e39c83ab
721 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5eedf060dd |
feat(engine): gang data model + validation gate (#61 task 1)
One public knob fanning into >=2 sibling params: Gang/GangMember types
(the Role name/targets split + BoundParam pos/name dual), Composite.gangs
behind the single fallible Composite::with_gangs gate, check_gangs as the
shared $A predicate (all three future minting boundaries route here), and
CompileError::BadGang(GangFault) as the typed refusal.
PrimitiveBuilder::original_pos (aura-core) extracts bind's pos-reconstruction
loop verbatim so gang members and BoundParam share one coordinate space.
Deviation ratified vs the plan: GangFault::KindMismatch carries structural
{node, name} coordinates instead of a stringly member label, consistent with
the sibling arms (plan amended). A redundant test-local fixture wrapper was
dropped after a fresh quality re-review of the blocked task diff.
Verified: cargo test -p aura-engine --lib 249/0, -p aura-core 53/0, clippy
-D warnings clean on both crates.
refs #61
|
||
|
|
962b249814 |
fix(cli): aura graph rejects a bad blueprint arg, not a silent sample (#28)
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 |
||
|
|
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 |
||
|
|
518c5e9017 |
feat(cli): render a consumer's own blueprint with aura graph <file> (#28)
aura graph was the last verb still sample-bound: its no-subcommand arm always rendered the embedded r_sma_open.json, so a downstream researcher who authored their own blueprint could introspect it as data but not RENDER it to see mis-wiring before a run — the Construction-layer milestone payoff only half realized. Give GraphCmd a first-positional blueprint (args_conflicts_with_subcommands so it coexists with build/introspect/register), and branch the no-subcommand arm on is_blueprint_file, mirroring dispatch_run: read the file, reload it via blueprint_from_json against the project/std vocabulary, and render_html it; the no-argument form still renders the embedded sample. The library-packaging half (a downstream-linkable render crate) is dropped — the ratified consumer surface is CLI-over-blueprint-data (decision on #28). The nested-composite render is not a concern: the old graph.rs ascii render that carried the unimplemented! was retired, render_html serializes via model_to_json with no panic, and #26 is already closed. closes #28 |
||
|
|
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 |
||
|
|
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 |
||
|
|
35b996e3bf |
docs(ledger,research): note the metric-vocabulary drift guard (#190 close)
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 |
||
|
|
682f0abfec |
test(campaign): guard the metric vocabulary against the shipped metric types (#190)
metric_vocabulary() hand-lists 17 scalar metric names; a field added or renamed on RMetrics/RunMetrics/FamilySelection (aura-analysis) silently desynced the gate/stage checks — a new scalar stayed un-gateable until every roster was hand-updated (the 3-copy drift class #160 closed for node type ids). Pin the vocabulary against the types' real serde output instead of a second hand-copy: enumerate the scalar (JSON-Number) fields of real instances of the three metric-bearing types and assert set-equality with metric_vocabulary(). A new un-gated scalar (present in the type, absent from the vocabulary) or a stale vocabulary entry (present in the vocabulary, gone from every type) now goes red. RMetrics comes from the real producer summarize_r; RunMetrics and FamilySelection from exhaustive struct literals, so a new field also breaks the test's compilation until it is consciously classified. Selection provenance numbers (n_trials, raw_winner_metric) and the Optional annotation identifiers are excluded with rationale — the types mix scalar metrics with scalar provenance and carry no type-level marker between them, so that one distinction stays explicit. Green on arrival (the rosters agree today); the roster-macro maximal shape that would remove the hand-list entirely waits on #147 (where the metric vocabulary's home is decided). Guard test only, no production change. closes #190 |
||
|
|
67f98b6bc5 |
feat(cli): swap the scaffold's starter node for a param-bearing Scale (#183)
The `aura new` scaffold shipped one node, a param-less Identity, so a downstream author had no public example of a build closure consuming a bound param — the highest-value gap the project-environment fieldtest found (F2): a newcomer could copy a param-less node but had to read engine source to write their first *tunable* one, which the consumer contract forbids. Swap that single starter node for a Scale node (mirroring the std gain operator): its schema declares a `factor` ParamSpec and its build closure reads the bound value (`|p| Box::new(Scale::new(p[0].f64()))`), and the starter blueprint binds `factor` so `aura run` exercises the bound-param path with zero hand-editing. factor is bound to 1.0 — a neutral identity default the newcomer then tunes, not the only C7-safe value (only |factor|>1 would push the terminal `bias` output past the bounded [-1,+1] contract; the knob is genuinely read and multiplied each cycle). The param-reading closure is the copyable template content #183 delivers; a hypothetical `|_|` regression is caught by the scaffold unit test's source guard, and tests/project_new.rs re-validates the swap by building and running the scaffold end to end. Template-side only (#181): the frozen 0102 demo-project fixture is untouched. closes #183 |
||
|
|
93a2e2bb60 |
test(cli): RED — scaffold's starter node must read a bound param (#183)
The `aura new` scaffold ships one node, a param-less Identity, so there is no public example of a build closure consuming a bound param — the F2 gap the project-environment fieldtest rated its highest-value authoring miss. This pins the target: the rendered starter lib.rs declares a ParamSpec and reads p[0] in its build closure, and the starter blueprint binds `factor` on the project's own node, so `aura run` exercises the bound-param path with zero hand-editing. refs #183 |
||
|
|
f2526b1720 |
docs(cli,ledger): #168 cycle-close audit — reconcile the --trace surfaces (closes #168)
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
08b0f9c783 |
docs: #216 cycle-close audit — run-transcript regimes + glossary avoid reconcile
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). |
||
|
|
6e22c98e23 |
test(cli),docs: risk-axis introspection pins + guide/glossary coverage (#216 slice 2)
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). |
||
|
|
a74c3d116e |
feat(research,cli): make the risk axis discoverable — probe, roster, validate counts (#216 slice 1)
The structural risk-regime axis worked end to end but was invisible on the public surface (refs #216): the --unwired probe swept only required slots (risk is #[serde(default)]), no vocabulary block described RiskRegime, and the validate summary counted axes/windows but no regimes or cells. - open_slots_campaign gains its one optional-slot probe: a risk-less (or empty/non-array) document lists 'open slot: risk (optional, list of stop regimes { vol: { length, k } }; absent = one default regime)'; a bound list closes the slot. Never a required flip — content-id parity of risk-less documents is load-bearing. - CAMPAIGN_SECTIONS gains std::risk (SlotKind::Regimes, required: false) so --vocabulary lists it and --block std::risk describes the regime shape. - The intrinsic validate summary now reports the realized matrix, mirroring the exec cross-product exactly (strategies x instruments x windows x regimes; empty risk = one default cell, marked '(default)'). Points stay a grid property, deliberately not a cell factor. Sanctioned migrations (recorded on #216): the two unit pins encoding the old invisibility — open_slots_report_partial_documents_by_path now pins exactly one open slot on the complete risk-less fixture (doubling as the probe's unit pin; fixture bytes unchanged), and the presentation-hint test migrates from full-vector equality to locating its entry (its purpose is the hint text, not the vector length). Verified: cargo test -p aura-research (29/0), -p aura-cli --test research_docs (60/0), clippy -D warnings clean. |
||
|
|
68317ec95d |
chore(cli,ledger): #217 cycle-close audit — coverage backfill + stop-knob helper
Cycle-close audit for the stop-default cycle ( |
||
|
|
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 |
||
|
|
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 |
||
|
|
4acea45519 |
chore(cli,docs): #220 cycle-close audit — doc reconcile + dispatch dedup
Cycle-close audit for the verb-axis-generalization cycle ( |
||
|
|
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 |
||
|
|
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 |
||
|
|
4f629ee2ab |
docs: remove the whitepaper folder
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. |
||
|
|
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 |
||
|
|
51b8550237 |
docs: add whitepaper for Bevy ECS visualizer concept
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. |
||
|
|
b5ad53d233 |
refactor(cli): cut wrap_r's dead cost-graph branch (#221)
Behaviour-preserving removal of the unreachable single-run cost seam: no caller ever passed Some(cost). Drops the cost parameter from wrap_r (the compiler enumerated 8 sites, all passing None), the dead branch, the vol_proxy arm, CostConfig, SLIP_VOL_LENGTH, and the 4 inert cost imports; RollingMax/RollingMin/Sub move to #[cfg(test)] (last production caller gone). The cost_graph composite stays alive in aura-composites; the campaign/sweep reduce path (C10) is untouched. Decision + rationale on the issue. Suite green unchanged (62 groups), clippy clean. closes #221 |
||
|
|
a1551a6097 |
fix(campaign): thread the risk regime through trace persistence
The persist-side C1 drift alarm re-ran every nominee under the hardcoded
default vol-stop because CellRealization dropped the regime at the record
boundary — a non-default risk regime + persist taps false-failed C1 and
refused to persist a correct trace; two regimes over the same cell would
also have collided in one trace dir.
Fix at the record boundary: CellRealization carries regime + regime_ordinal
(serde-default + skip-serializing keeps pre-existing stored records
byte-stable), populated where the cell is realized; the regime->StopRule
binding is single-sourced in stop_rule_for_regime, shared by the member
runner and the persist re-run so the two sites cannot drift apart again;
campaign_cell_key appends -r{regime_ordinal} for non-default regimes
(default keys unchanged, no stored dir renamed).
closes #219, closes #212
|
||
|
|
70a045cde9 |
test: red for the false C1 refusal on non-default regime trace persist
Two RED tests pinning the lossy record boundary: CellRealization carries no regime, so persist_campaign_traces' drift-alarm re-run hardcodes the default vol-stop and false-fails C1 for any campaign with a non-default risk regime + persist taps (facet A), and campaign_cell_key has no regime discriminator so two regimes' traces would collide in one dir (facet B, masked by A's refusal today). refs #219, refs #212 |
||
|
|
3198747b48 |
docs(cli): finish the #159 tidy — sweep dead-symbol comments in main.rs
Behaviour-free comment/rustdoc sweep of the last file the #159 demo-retirement audit flagged: repoint run_sweep/run_mc/mc_family/ walkforward_family/r_sma_sweep_over/run_oos_r/sample_blueprint_with_sinks references to their live blueprint_* analogs or mark them retired. De-links the r_sma_sweep_family intra-doc link at blueprint_sweep_family — cargo doc --workspace --no-deps is now warning-clean. Verified: suite 1029/0, clippy clean, diff comment-only. closes #222, refs #159 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
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
|
||
|
|
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
|
||
|
|
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 |
||
|
|
83b375af66 |
chore: drop restated spec/plan lifecycle from CLAUDE.md; git-ignore docs specs/plans
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. |
||
|
|
b761b3c9c2 |
docs: close the verb-dissolution milestone — ledger status 4/4 + glossary sugar note
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 |
||
|
|
f95de8fcc2 |
fieldtest: milestone verb-dissolution — 3 scenarios, 8 findings
Milestone-close fieldtest for "Verb dissolution" (#210), authored from the public interface only. Three end-to-end scenarios over the real GER40/USDJPY 2025 archive: 1. all four dissolved verbs on the r-sma real path (sweep via blueprint; generalize/walkforward/mc via --strategy) — correct + deterministic; 2. reach-through — `aura generalize` auto-registers a campaign+process document and `aura campaign run <id>` reproduces worst_case byte-for-byte; 3. a hand-authored 2-instrument × 2-window × 2-regime matrix (8 cells → 4 generalizations) no verb can express — regimes compared, never argmax'd. Result: 0 bugs; the core promise holds empirically (behaviour preservation, campaign-substrate reach-through, and the risk-regime axis all confirmed). Residual findings are discoverability/ergonomics follow-ups, filed as #216 (risk-axis discoverability), #217 (verb knob asymmetry), #218 (no-project store litter). The friction-and-bug spec stays a git-ignored working file. |
||
|
|
2a7caa6e38 |
audit: cycle close mc dissolution — drift-clean (#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
|
||
|
|
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 |
||
|
|
764dc2adbf |
docs(specs,plans): mc dissolution — spec + plan (#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
|
||
|
|
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 |
||
|
|
18060cb9dc |
audit: cycle close walkforward dissolution — drift-clean (#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 |
||
|
|
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
|
||
|
|
a72cd6b115 |
feat(cli): walkforward campaign translator + summary reducer (#210 T1-T2)
The additive machinery for the walkforward dissolution, unwired this slice (dispatch_walkforward untouched — `aura walkforward` behaviour byte-for-byte unchanged). Two pieces, both mirroring the generalize seam: - `translate_walkforward` + `register_generated_wf` + `GeneratedWalkforward` (verb_sugar.rs): argv -> a `[std::sweep(argmax), std::walk_forward]` process + a campaign carrying the multi-value fast/slow IS-refit grid over one instrument under a single Vol risk regime (the stop). The preceding sweep enumerates the survivor grid; the wf stage refits it per window. - `walkforward_summary_json_from_reports` (main.rs): the pure-sugar reducer that reconstructs the verb's summary line from the campaign's recorded per-window OOS reports — `stitched_total_pips` = the per-window total_pips summed in roll order, `param_stability` over the four r-sma axes via the same `MetricStats::from_values`, pooled `oos_r` via `r_metrics_from_rs`. Byte-identical to the inline `walkforward_summary_json` (the committed anchor gates it once Task 3 wires it). Both carry `#[allow(dead_code)]` until Task 3's dispatch split calls them. The third `Generated*`/`register_generated_*` trio duplicates the sweep/generalize shape by design (per-verb mirror); the bundle consolidation is deferred to #214. Unit test note: `r_metrics_from_rs` clears `trade_rs` (it is the terminal pooled reducer), so the reducer's characterization fixture rebuilds each window's `RMetrics` with `trade_rs` restored, modelling what a real OOS window's report carries (from `summarize_r`). refs #210 |
||
|
|
f7835353de |
docs(specs,plans): walkforward dissolution — spec + plan (#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
|
||
|
|
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 |
||
|
|
3a03617d6b |
audit: cycle close generalize dissolution — drift-clean (#210)
Second verb dissolution (#210) closed drift-clean. Architect: no ledger drift. What holds (architect, verified against the diff + a live test run): - Byte-parity proven, not assumed — the exact-grade anchor runs green through the dissolved path; {"family_id":"generalize-0"} survives (the per-cell Sweep families take a distinct campaign-prefixed name). - C18 stamping preserved — the member runner maps cell.regime -> StopRule::Vol and stamps manifest.instrument; the anchor confirms regime-seam R == former grid-axis R. - run_campaign_returning/present_campaign split is behaviour-preserving — the run record is written inside execute, so the sugar path (bypassing the presenter) still records it. - Q4 is not drift — the +2 per-instrument Sweep families are the executor's uniform, reproducible audit artifacts (sweep-dissolution precedent, C18-consistent); stdout unchanged; the relaxed family pin is faithful. Regression: cargo test --workspace green (every crate 0 failed); clippy --workspace -D warnings clean. Drift resolution: - [medium] no-window generalize grades over symbols[0].full_window (one shared window) vs the inline path's per-instrument windows. RATIFIED: a single shared window is the campaign model's natural expression, the explicit-window path (both e2e + the exact-grade anchor) is byte-identical, and symbols[0] is a pragmatic pick; no value pin because a full-archive grade is fragile against archive growth. Deferred design reconsideration filed as #213. - [low] two #[allow(too_many_arguments)] in verb_sugar.rs — carry-on; the arg-bundling refactor is best done once the remaining verbs dissolve (#214). Spec + plan removed at cycle close (git-tracked in this project). refs #210 |