2162bd6d97aa1de9f46924c27d9abb9e24217bb3
707 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
ed8b179e34 |
feat(cli): the generalize campaign translator (#210 T2)
`translate_generalize` builds the selection-bearing campaign document (`[std::sweep(argmax), std::generalize]`, a single risk regime carrying the stop, the candidate as single-value raw axes) + `GeneratedGeneralize` + `register_generated_g`, mirroring the sweep translator. A determinism unit test pins identical-invocation -> identical content id and the regime/pipeline shape. The new items are unused until the dispatch-rewire task (#210 T3) wires the runner; a bridge `#![allow(dead_code)]` (removed in T3) mirrors the sweep sibling's landing. refs #210 |
||
|
|
9ddea840dd |
refactor(cli): factor the campaign run outcome out of the presenter (#210 T1)
`run_campaign_returning` runs the resolve->execute body and returns a `CampaignRun` bundle (outcome + campaign/strategies/server); `present_campaign` consumes it for the stdout tail; `run_campaign_by_id` becomes their composition. Behaviour-preserving — the existing campaign + sweep-sugar e2e stay green (research_docs 54/0 incl. the real-data e2e, sweep dissolution e2e green). Exposes the outcome so the forthcoming generalize sugar can read it without the presentation tail. refs #210 |
||
|
|
fd9a7322a0 |
docs(specs,plans): generalize dissolution — spec + plan (#210)
Second verb dissolution: `aura generalize` becomes thin sugar over the one
campaign executor, byte-identical output, the stop carried by a single risk
regime. Spec grounding-check PASS (auto-signed) after the exact-grade anchor
(
|
||
|
|
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 |
||
|
|
d96af7e1a2 |
audit: cycle close risk-regime axis — drift-clean after the C20 ledger fix (#210)
Architect drift review (c9d962f..HEAD). What holds: the nominee key is the 3-tuple (strategy, window, regime) and no argmax over regime_ordinal exists, so the ledger's kept-separate / compared-not-selected semantics is what the code does; the C18 manifest stamp is honest (the characterization pin flipped from asserting absence to requiring the exact resolved 3/2.0, a strengthening, and a real two-regime e2e catches per-cell mis-resolution); no dead code (cell.regime and regime_ordinal are both read after the runtime-binding slice), absent/empty risk keeps content-id parity, and CampaignGeneralization.regime_ordinal is serde-default so pre-feature records still parse. Resolution: - FIX (inline, this commit): the spec promised a C10 AND a C20 note; only C10 landed. C20's enumerated structural-axis list (strategy/instrument/broker/ window) was left silently incomplete — the risk regime is added as the fourth matrix axis, with a dated realization pointer to the C10 semantics. - carry (documented descope): no std::risk descriptor in CAMPAIGN_SECTIONS — risk is a top-level array, not a slot-bearing section; no invariant broken. - carry (filed #212): the persist re-run + campaign_cell_key omit the regime because CellRealization gained none; the architect verified this is a LOUD C1 drift-alarm refusal for a non-default regime + persist_taps, not silent wrong data (#212 corrected accordingly). - trivial: validate_campaign's regime-check comment corrected (it catches NaN, not all non-finite; +inf is unreachable via JSON). Regression gate: full workspace suite green, clippy clean (no dedicated regression script; the suite + lint are the gate). Spec and plan removed (git rm) at cycle close per the project convention. No baseline moved. refs #210 |
||
|
|
50d3db7028 |
feat(cli): finalize the risk-regime axis — ledger note + clippy-clean regime check (#210 T5)
The last slice of the risk-regime structural axis. The sugar parity (`translate_sweep` emitting `risk: vec![]`, absent-serializing) and the real-data two-regime e2e landed in the earlier slices (the loop's self-correction and its e2e phase), so this closes the remaining pieces: - design-ledger note: the risk regime realizes the StopRule structural axis at the campaign-document level — kept-separate keying, compared-not-selected (the R-unit argument), the C18 stamp, absent-parity, and the deferred trace gap (#212). - clippy: `validate_campaign`'s regime check reads `k <= 0.0 || k.is_nan()` instead of `!(k > 0.0)` (neg_cmp_op_on_partial_ord), behaviour-identical. Full workspace suite green; clippy clean. refs #210 |
||
|
|
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
|
||
|
|
a0d8aec3f7 |
feat(campaign): expand the campaign matrix over risk regimes (#210 T3)
The regime becomes the fourth structural coordinate of the campaign matrix, a
kept-separate axis (a peer of window, not aggregated-over like instrument):
- aura-campaign: CellSpec gains `regime` + `regime_ordinal`; the executor
resolves the regime list (absent/empty risk -> one default cell, ordinal 0),
runs a cell per (strategy, instrument, window, regime), and keys the nominee
map by the 3-tuple including the regime ordinal — so generalize aggregates
across instruments WITHIN a regime, never across regimes. Family names gain a
`-r{regime_ordinal}` discriminator so two regimes' families cannot collide.
- aura-registry: `CampaignGeneralization` gains `regime_ordinal` (with
`#[serde(default)]`, so pre-feature stored records still parse — pinned by a
new backward-compat test).
The existing single-regime execute tests shift their family ids by `-r0`; a new
test pins that two regimes yield distinct `-r0`/`-r1` families. `CellSpec.regime`
is write-only in this slice — its sole reader is the deferred member runner
(the next slice) — a held quality nit, seam-prep as the plan prescribes. Full
workspace suite green.
refs #210
|
||
|
|
2f9f3571ae |
feat(research): RiskRegime vocabulary + CampaignDoc.risk field + validation (#210 T1-2)
The first slice of the risk-regime structural axis (spec/plan
|
||
|
|
abab1f1684 |
docs(specs,plans): risk-regime structural campaign axis — spec + plan (#210)
Spec auto-signed on a grounding-check PASS: the risk regime becomes a structural campaign axis (a peer of instruments/windows, kept-separate in the executor, each member's resolved stop stamped into its manifest; regimes are compared, never argmax-selected across — a cross-regime E[R] argmax would mix R units, C10). The 5-task plan threads it through aura-research (RiskRegime + CampaignDoc.risk), aura-campaign + aura-registry (matrix expansion + regime_ordinal), and aura-cli (StopArm + wrap_r + the manifest stamp). Two bounded, documented descopes: the descriptive CAMPAIGN_SECTIONS introspection entry and multi-regime trace-dir isolation. refs #210 |
||
|
|
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 |
||
|
|
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 |