143c87c0aeca8a7ff6422a7479cb6137967bd296
156 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c1971d4ed7 |
fix(cli): the graph render positional shape-discriminates op-scripts like its siblings
aura graph <op-script.json> exited 2 mislabeling the valid JSON array as 'blueprint document is not valid JSON' — dispatch_graph parsed the render positional with the envelope-only blueprint_from_json while the introspect siblings (register, --params, --content-id) route through composite_from_any. The render positional now uses the same shape-discriminating path (made pub(crate)); an op-script renders byte-identically to its built envelope, and a genuinely-bad document gets the shared honest error including the unresolved-namespace hint (preserved verbatim in composite_from_any's envelope arm). Verified: RED test green, full workspace suite green (independent mini-verify). closes #226 |
||
|
|
fb145f5a15 |
fix(cli): reproduce re-derives the stop regime from the member manifest
reproduce_family_in hardcoded the default StopRule::Vol{3,2.0} at both
the param-space probe and the member re-run, so a family minted under a
non-default vol-stop (campaign risk-regime cell, or wf/mc/generalize
with --stop-length/--stop-k) spuriously reported DIVERGED.
New stop_rule_from_params re-derives StopRule::Vol from the manifest's
stamped stop_length/stop_k, falling back to the defaults when the keys
are absent (pre-stamp members) — the same one-directional widening
point_from_params applies to missing manifest params, mirroring
campaign_run's stop_rule_for_regime None arm. The stop rides outside
the wrapped param space, so point_from_params could never recover it.
Verified: RED test green, full workspace suite green (independent
mini-verify), clippy -D warnings clean.
closes #233
|
||
|
|
ab14eeed3c |
test(cli): RED — reproduce re-runs members under the default stop regime
A member minted under a non-default vol-stop (manifest stamps
stop_length/stop_k) spuriously reports DIVERGED on reproduce: both the
param-space probe and the member re-run hardcode StopRule::Vol{3,2.0}.
Closed blueprint isolates the stop as the only differing dimension.
refs #233
|
||
|
|
ed1e8a0d10 |
fix(cli): thread the resolved pip into wrap_r so real-data runs hit the graph broker
wrap_r hardwired SimBroker::builder(SYNTHETIC_PIP_SIZE); the resolved geometry-sidecar pip reached only the manifest broker label, so every real-data run computed total_pips/max_drawdown at 0.0001 regardless of instrument (GER40 pip=1: inflated 10^4) while the label claimed the real pip. wrap_r now takes pip_size. Real-pip call sites pass the resolved pip: run_signal_r (resolve_run_data reordered before the wrap), run_blueprint_member, and the campaign trace re-run in persist_campaign_traces (instrument_geometry lookup mirroring the run_member refusal — required so the C1 drift alarm compares re-run and nominee at the same pip). Probe-only call sites (param-space and axis probes; metrics discarded) keep SYNTHETIC_PIP_SIZE explicitly. The two golden stitched_total_pips pins in cli_run.rs rescaled by exactly the 10^4 bug factor while n_trades/expectancy_r stayed byte-identical — confirming R metrics were never pip-contaminated. Alternative rejected: dropping pip-denominated metrics from real-data reports — the manifest already stamps the resolved pip, so running the graph at it restores label truthfulness without shrinking the report. Verified: full workspace suite green (0 failed incl. the real-data e2e suite), clippy -D warnings clean. closes #232 |
||
|
|
eb5fc2145b |
test(cli): RED — real-data member pips ignore the resolved sidecar pip
The in-graph SimBroker divides by SYNTHETIC_PIP_SIZE regardless of the resolved instrument pip; the sidecar pip reaches only the manifest label. Pins the pip-invariance property total_pips * pip == raw price move. refs #232 |
||
|
|
3016bd6b11 |
fix(cli): reproduce re-derives real-data families from the member manifest (#229)
reproduce_family no longer hardcodes DataSource::Synthetic: it reads the family's instrument + window from the persisted manifest and rebuilds the same local-archive source the dissolved verbs run over; a missing archive refuses with the verbs' established named-data refusal (exit 1) instead of silently re-running over wrong data. Sweep families now reproduce N/N bit-identically; WalkForward families yield a verdict instead of the empty-OOS-window panic. The synthetic-family path is byte-unchanged. Accepted minimal duplication: reproduce_family performs its own family lookup to read the instrument before delegating to reproduce_family_in, whose signature and unit-test seam stay byte-identical (constraint: minimal fix). MonteCarlo reproduce still runs the synthetic seed walk regardless of a real manifest — pre-existing, semantically distinct (bit-identity over bootstrap resamples), filed forward. Verified: both #229 RED tests green over the GER40 archive, full workspace suite 1107/0, clippy -D warnings clean. closes #229 |
||
|
|
fc9cf23b87 |
feat(cli,docs): ship the ganged open examples + document the gang construct (#61 tasks 7-8)
The two open examples now expose their author-intended single knobs — the closed builders always bound these pairs to one value by hand; the open forms falsely offered them as independent axes: - r_breakout_open: channel_hi.length + channel_lo.length -> channel_length (the Donchian channel is structurally ONE parameter) - r_meanrev_open: mean_window.length + var_window.length -> window; the band factor stays an independent axis Regenerated via the emitters (never hand-edited); the closed examples are byte-unchanged. The carve builders gang in the open branch only. Test migration: the two axis-namespace pins, five --real e2e invocations (single gang axis, the 10,20 diagonal for the campaign pair — the mismatched 20,40/10,20 grid was exactly the configuration space the gang retires), and the param_stability row counts (4 -> 3). The r-sma walkforward golden anchor is untouched (it never swept a ganged pair). Docs: authoring-guide gains the seventh op + the third param state (open/bound/ganged) + the gang wrap note; README op list + Axis concept; glossary gang entry; ledger C24 records the gang verb and the pre-ship Tier-2 dormancy (no format-version bump while no out-of-repo reader exists). Verified: full workspace suite 1104/0 (--real e2e included, local data present), clippy -D warnings clean, cargo doc clean; live acceptance: introspect --params prints channel_length:I64 alone / window:I64 + band.factor:F64. closes #61 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
4acea45519 |
chore(cli,docs): #220 cycle-close audit — doc reconcile + dispatch dedup
Cycle-close audit for the verb-axis-generalization cycle ( |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
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
|
||
|
|
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 |
||
|
|
2c729965db |
audit: cycle 0110 tidy — verb-dissolution cycle 1 closed drift-clean
Architect review (scope e7c7bde..b7aaa0b) found three items; resolutions: - fix: docs/authoring-guide.md + glossary aligned with the shipped vocabulary (std::sweep selection group optional/all-or-nothing, selection-free = terminal-only, worked example verified against the live binary). - fix: the #203 wrapped/raw axis-name convention is now single-sourced in campaign_run.rs (wrapped_to_raw_axis + raw_matches_wrapped, a cross-documented inverse pair; dispatch_sweep and both bind sites call through it; inverse-property unit test). Ratify note: this unifies two previously different algorithms onto the documented first-segment semantics — the old loose ends_with suffix-match also accepted sub-segment shorthands (e.g. axis "length" against "sma_signal.fast.length") that no stored document or test used; such shorthands now refuse loudly (unbound/unknown axis) instead of matching, the refuse-don't-guess reading of #203. - carry: run_blueprint_sweep/blueprint_sweep_family remain generically real-capable though the dispatch no longer routes real data to them; the residual capability is shared DataSource machinery, its removal falls out with the built-in/synthetic branch retirement (#159), and the synthetic-only status is documented at the fn. What holds: C24 canonical form (flatten group leaves every stored content id unchanged, golden pin untouched), C18 lineage (topology_hash IS content_id_of, single store write resolves the strategy ref; selection-free arm still persists the full family), C25 closed- vocabulary discipline (all-or-nothing group, Blockly-clean), C3 (ms/ns conversion through the ingest seam's own named fn). Ledger: #109 open-thread status lifted to cycles 0107-0110 reality (executor shipped, amendment package landed, verb dissolution running as milestone #210 with sweep dissolved). Regression gates: cargo test --workspace 1056/0; clippy -D warnings clean; cargo doc clean. Cycle spec+plan removed at close per convention. refs #210 |
||
|
|
b7aaa0ba59 |
feat(cli): dissolve the real-data blueprint sweep into the campaign path (#210 T4)
aura sweep <bp.json> --real ... --axis ... now runs as thin sugar over its generated campaign document through the one campaign executor: the dispatch registers the canonical blueprint (topology_hash IS content_id_of over the same canonical bytes, so the strategy ref resolves against the existing single store write), strips the CLI's wrapped axis names to the raw campaign namespace (#203 bind convention), converts the probed archive window back through the ingest seam's own epoch_ns_to_unix_ms (made pub; C3 — the seam owns the ms/ns convention, never an inline division), and hands off to verb_sugar::run_sweep_sugar. The inline real-data arm is gone; the synthetic arm and --list-axes are untouched. campaign_run gains RunPresentation {Full, MemberLinesOnly}: run_campaign keeps Full (record line unchanged, project gate unchanged); the sugar path runs MemberLinesOnly — the generated doc's emit already limits stdout to family_table member lines, the mode suppresses only the final record line. The record append is identical in both modes; no project gate on the by-id path (the dissolved verb works project-less, exactly as before). The characterization pin survives the re-cut with exactly the one sanctioned assertion flip (instrument-absence -> instrument==GER40) and now also pins the generated artifacts: one process + one campaign document auto-registered (dedup on a second identical invocation), raw axis keys in the stored document, the stored window a ms sub-range of the request, C1 at the member-report level with the registry's run-suffix increment isolated. Suite 1055/0, clippy clean, gated real-data paths executed locally. refs #210 |
||
|
|
fd0b21d070 |
feat(cli): verb_sugar translator — argv to generated campaign documents (#210 T3)
The pure half of the sweep re-cut: translate_sweep maps a real-data blueprint-sweep invocation (axes, name, symbol, window, canonical blueprint bytes) to a selection-free single-stage process document plus a campaign document carrying exactly the invocation's intent (seed 0 — no stage consumes it — so identical invocations dedupe onto identical content ids); register_generated puts both into the content-addressed stores. Unit tests pin determinism, name flow, mixed-kind-axis refusal, and that generated documents pass intrinsic validation + preflight. Dead-code-allowed until the dispatch rewire wires the module (next task removes the bridge attribute). Plan bytes reconciled with three necessary deviations the spec gate surfaced: derive(Debug) for unwrap_err, ScalarKind lives in aura_core, and 'gen' is reserved in edition 2024. refs #210 |
||
|
|
61c8828fad |
feat(cli): house-style loader-error prose + missing-project hint on the run path
aura run leaked the raw LoadError Debug form (UnknownNodeType("..."))
while graph build already phrased the same fault house-style (#162
convention: engine error types are Display-free, the CLI presents them).
Route the run path through the shared blueprint_load_prose presenter (now
pub(crate), env-agnostic) so both surfaces phrase a LoadError identically
and neither leaks the Debug token (#184). Add unresolved_namespace_hint:
when the unresolved id looks project-namespaced (::) and no project was
loaded (env.provenance() is None), append a hint pointing at the missing
Aura.toml — the newcomer running the host from the wrong directory now
gets an orienting diagnosis instead of a bare type error (#185). The hint
is applied on both the run and graph-build paths. RED-first: the two
existing run-path pins were flipped to assert the prose + absence of the
Debug token before implementing; suite green (1043/0).
closes #184
closes #185
|
||
|
|
5256b3d0ca |
refactor(registry): single-source the deflation-bootstrap constants (#199)
DEFLATION_N_RESAMPLES (1000) and DEFLATION_BLOCK_LEN (5) were duplicated as independent copies in aura-cli and aura-campaign — a silent-drift risk (an edit in one crate but not the other refuses nothing, it just produces a wrong deflation result). Promote a single definition into aura-registry, which owns optimize_deflated (the sole consumer of these counts); aura-cli imports it and aura-campaign re-exports it to keep its public path stable. DEFLATION_SEED stays cli-local (no campaign twin). Values unchanged; behaviour-preserving, suite green unchanged (1043/0). closes #199 |
||
|
|
367f8678f1 |
feat(cli): #196 blueprint on-ramp — graph register, introspect --params, blueprint-file --content-id (0107 task 10)
Closes the F5 authoring gap: a campaign's strategy ref + axes are now authorable from the public surface without running a sweep first. 'aura graph register <blueprint.json>' puts the canonical form into the content-addressed store (id == topology hash) and prints the stored path via the pub blueprint_path accessor; 'aura graph introspect --params <FILE|64-hex-ID>' lists the RAW composite param_space — the exact namespace campaign axes are validated against (the wrapped --list-axes namespace stays the sweep-verb view); --content-id gains an optional FILE that shape-discriminates a blueprint envelope from an op-list document (stdin behaviour byte-identical without FILE). is_content_id promoted pub(crate) and shared with graph_construct so the two FILE-or-id surfaces cannot drift on the id shape. RED evidence was produced retroactively by the loop (stash-probe: the 5 new tests fail for the expected reasons without the src changes) — ordering deviation noted, outcome unaffected. Gates: graph_ 46/0, workspace 987/0, clippy -D warnings clean. closes #196 refs #198 |
||
|
|
aeb0366aed |
feat(cli): aura campaign run — the executor verb over the MemberRunner driver (0107 tasks 8-9)
campaign_run.rs: target resolution (file is register-then-run sugar; bare 64-hex is the canonical id — #198 decision 1), project gate before any store write, zero-fault referential gate, process fetch + v1 preflight, then aura_campaign::execute with the CLI MemberRunner over the shipped loaded-blueprint convention: per-member blueprint reload, reduce-mode wrap, unique suffix-join of raw campaign axis names onto the wrapped param_space, windowed real data via the shipped ms->ns source seam (absent archive/zero-bar windows -> NoData). Emission: family_table / selection_report lines gated on the doc's emit list (names debug_asserted against emit_vocabulary), the campaign_run record line always, persist_taps deferred LOUDLY on stderr before execution (F7 lesson), zero-survivor cells noted on stderr with exit 0. exec_fault_prose/member_fault_prose keep the Debug-free house seam. Seam tests: 8 campaign_run_* e2e tests incl. outside-project, bogus target, unknown id, v1-boundary (mc process registers fine, run refuses), persist_taps ordering, and the gated real-data sweep->gate->walkforward e2e — which ran its full assert path on this host (local GER40 2024-09 archive) rather than the data-less skip. Also: exec.rs campaign-id guard tightened to lowercase hex, matching is_content_id and the store's self-keyed form (review nit). Gates: workspace 975/0, clippy -D warnings clean. refs #198 |
||
|
|
a9e047eca7 |
feat(cli): 0106 tasks 11-12 — aura campaign verb family + workspace gates
The campaign half of research_docs.rs: validate (intrinsic always; referential tier when a project env is discovered, with the explicit skip line outside one), introspect (--vocabulary/--block/--unwired/ --content-id, same exactly-one-of usage guard), register via the registry's campaign_path accessor, and RefFault prose (Display-free, unit-pinned). Five binary seam tests (skip line, empty-axis prose + exit 1, section vocabulary, spec-example open slots, content-addressed registration) reuse the file's temp_cwd helper — the plan's tempfile::tempdir() snippet deviated from the crate's zero-extra-dep test convention and was adapted, disclosed in the loop's report. The referential CLI branch inside a real loadable project stays fieldtest territory by the plan's own scope note (project::load needs a built dylib); its logic is pinned by the aura-registry tests. Gates: cargo test --workspace 916/0 (885 pre-cycle + 31 new), clippy -D warnings clean, cargo doc --no-deps 0 warnings. The worked spec example runs end-to-end headless: author both documents as text, validate with per-tier report lines, introspect vocabularies and open slots, obtain stable content ids, register into the store — no Rust compile, no run executed. closes #189 |
||
|
|
db09e5de52 |
feat(cli): 0106 task 10 — aura process verb family (validate/introspect/register)
New research_docs.rs module: role-addressed process verbs with house-style fault prose (Display-free enums phrased at the seam), the introspect exactly-one-of usage guard (exit 2), and content-addressed registration. Seven binary seam tests pin intrinsic-ok/refusal prose, vocabulary/block/ unwired/content-id introspection, exit codes, and the stored file's location under the runs root. Quality-gate finding folded in (and back into the plan): register no longer re-derives the store path from runs_root — aura-registry now exposes process_path/campaign_path, the same single mapping its put/get pair routes through, so the printed path cannot drift from the store layout. Verification: research_docs seam tests 7/0, registry 49/0, clippy clean. refs #189 |
||
|
|
ef3bec5844 |
feat(research): 0106 tasks 1-9 — document layer, content-id move, stores + referential tier
The aura-research leaf crate: ProcessDoc/CampaignDoc with strict parsing (hand-rolled StageBlock deserializer — deny_unknown_fields does not compose with internally-tagged enums; Axis declares its ScalarKind once with bare values, per the #189 user veto), canonical form + content_id_of (the SHA-256 primitive moved from aura-cli, which now delegates — the id pin set incl. the independent sweep-store recompute stayed green untouched), intrinsic validation for both document types (P1 constraints structural), and the introspection contract (schema tables single-source parse strictness AND vocabulary/describe/open-slots — the Blockly litmus made checkable). aura-registry: content-addressed processes/ + campaigns/ stores on the blueprint-store pattern (Ok(None) treat-as-empty) and the referential tier (validate_campaign_refs: process/strategy refs incl. identity-scan, axis-name + declared-kind checks against param_space). Plan deviations, verified by hand and folded back into the plan file: - Task 9's fixture could not use aura-composites as planned: every shipped composite with an open param routes through LinComb, which the zero-arg std_vocabulary roster deliberately excludes, so none round-trips through blueprint_from_json with the by-type-name resolver. The test hand-builds a minimal Bias composite instead (generic over param_space, as planned); the unused pro-forma aura-composites dev-dep from the repair pass is dropped. This is why the loop reported task 9 BLOCKED (review-loop-exhausted on the literal code block); the tree itself is complete and green. - A plan-verbatim test assertion false-matched ("deflate" as substring of "deflated-positive"); tightened to the JSON key. - OpenSlot doc comment backticked (rustdoc read strategies[0] as a link; doc gate back to 0 warnings). Verification: cargo test --workspace 898/0; clippy -D warnings clean; cargo doc --no-deps 0 warnings. Tasks 10-12 (CLI verb families, final gates) follow. refs #189 |
||
|
|
45fb06dba3 |
feat(engine+cli): topology-identity hash — --identity-id beside --content-id
Engine (aura-engine/blueprint_serde): blueprint_to_json factored into build_doc/serialize_doc (byte-preserving — canonical golden unchanged) and blueprint_identity_json added: the canonical document with every non-load-bearing debug symbol (C23) blanked (composite name, instance names, bound-param names, role names, output re-export names) while everything load-bearing survives (type ids, node order, edges, role targets/order, output pairs/order, bound pos/kind/value — openness stays identity-bearing). Re-exported from lib.rs. Six property tests: renamed twins (equal identity, unequal canonical), open-vs-bound, bound-value, edge-swap, nested-composite interior names, role/output renames — the last two are additive beyond the plan and cover the recursion and role/output arms the planned four did not. CLI (aura-cli): graph introspect gains --identity-id, a sibling of --content-id through the same shared content_id SHA-256 primitive; composite_from_str factored out of build_from_str (fault strings byte-identical). The exactly-one introspect dispatch is DELIBERATELY relaxed: the two id flags form one group and may combine — one build, both ids, one per line, content id first. In-crate cross-path twin test (Rust sma_signal vs op-script twin: distinct content ids, one identity id) beside the existing cross-surface pins; four e2e tests incl. the previously uncovered count!=1 usage exit-2 path. topology_hash, the blueprint store, reproduce, and every --content-id byte stay untouched (spec acceptance 3; all existing pins green). Verification: cargo build clean; cargo test --workspace 884 passed / 0 failed (873 baseline + 11 new); clippy -D warnings clean; doc build 0 warnings. The implement-loop's Task-4 spec-compliance block was a plan-byte count mismatch only (plan under-counted pre-existing blueprint_serde tests 6-vs-7 and did not anticipate the two sibling-accepted extra tests); gates re-run by hand, all green. closes #171, refs #180 |
||
|
|
560f594902 |
feat(cli): aura new — CLI wiring, load-bypass guard, authoring-loop e2e (0103 tasks 2-4)
Command::New + NewCmd + dispatch_new (usage faults exit 2, runtime refusals exit 1, the C14 partition); the main() guard exempts aura new from the eager project load so scaffolding works inside unbuilt trees. E2e (tests/project_new.rs, 6 tests): scaffold -> cargo build -> aura run twice byte-identical with manifest.project.namespace stamped; introspect lists the project type beside std; refusals for existing destination (1), invalid names (2), missing engine path with --engine-path hint (1); aura new inside an unbuilt project tree succeeds (the bypass proof); namespace override reaches the built vocabulary (an additive e2e beyond the plan, kept). Verification: cargo build --workspace clean; cargo test --workspace 873 passed / 0 failed; clippy -D warnings clean. refs #180 |
||
|
|
01500cba5e |
feat(cli): aura new scaffold module — validation, templates, emit (0103 task 1)
Token-replace templates (Cargo.toml/Aura.toml/.gitignore/lib.rs/ signal.json/CLAUDE.md) parameterized by name/namespace/engine root; argv validation (usage exit-2 class), refusals before first write (existing destination, missing engine root with --engine-path hint), best-effort git init. Unit tests: validation table, snake derivation, engine-root default, token-free rendering, refusal pair — 5/5 green. Verified against the fixed plan (derive(Debug) — see the plan-fix commit); CLI wiring follows as task 2. refs #180 |
||
|
|
4928e289f7 |
feat(project): the project-as-crate load boundary (cycle 0102)
A research project is now a loadable external cdylib crate. Inside a directory whose ancestry holds an Aura.toml, aura discovers the project root cargo-style, locates the compiled dylib via cargo metadata (debug default, --release opt-in), loads it load-and-hold, and refuses mismatches before trusting anything: the AURA_PROJECT descriptor (aura-core::project, #[repr(C)]) carries a C-ABI stamp prefix (rustc + aura-core version, baked per consuming build by the new aura-core build.rs) validated before any Rust-ABI field is read. The vocabulary charter gates the merged resolution: project type ids are ::-namespaced (std stays bare), duplicates refuse, and the enumerable type-id list must agree with the resolver, so introspection can never silently omit a project type. All blueprint verbs resolve through the merged project + std vocabulary via a per-invocation Env threaded through the dispatch chains; registry, trace-store, and data paths anchor at the project runs root (Aura.toml [paths], paths-only by design — instrument geometry stays the recorded sidecar, C15). RunManifest gains the Tier-1 project provenance field (namespace + dylib sha256 + best-effort commit), stamped beside topology_hash on the blueprint-run paths; pre-0102 registry lines load unchanged. Default node names strip the namespace, so :: never reaches the param-path address space. Proven by the demo-project fixture (built by the e2e via cargo, path-dep on this workspace): run twice bit-identical, provenance recorded, introspection lists demo::* beside std, registry anchors at the discovered root from a subdirectory; the badcharter fixture proves the charter refusal through the real libloading path; a never-built project refuses with a cargo-build hint. Outside a project every path collapses to the previous literals — goldens and manifest pins byte-identical. Verification: cargo build --workspace clean; cargo test --workspace 862 passed / 0 failed (incl. 7 project_load e2e); clippy -D warnings clean (one precedent-matching allow(too_many_arguments) on run_oos_blueprint, whose arity the Env threading raised to 8); doc build unchanged. Docs/ledger aligned: Aura.toml field lists are paths-only in project-layout.md, glossary, C16/C17; new C13 realization note records the per-invocation-reload reading and the load-and-hold one-shot scope boundary. New deps, per-case review (aura-cli leaf binary only, never the frozen artifact): libloading, toml. refs #180 |
||
|
|
9402afe958 |
polish(cli): normalize usage-message casing to the clap house style
Every hand-rolled usage line now reads 'Usage: aura <verb> ...' — capital prefix plus the program name, the shape clap itself and the built-in run branch already spoke. 10 literal edits: 6 lowercase 'usage:' flips (graph introspect, run blueprint x2, runs family, mc blueprint, mc built-in), 4 bare closures gain the prefix (sweep/walkforward, blueprint + built-in branches); the mc built-in line names the program in both |-separated alternatives. Refusal diagnostics, the conforming run_args_from literal, clap output, and the fieldtests corpus stay byte-identical; exit codes keep the usage=2 / runtime=1 partition. Lockstep pin: the single casing-sensitive assertion (mc --real) flips to capital 'Usage'. Three additive regression tests pin the house style itself (bare-closure prefix, both mc alternatives, mc blueprint branch) — the pre-existing pins checked only verb tokens / a single Usage occurrence; deviation from the spec's no-new-test-bytes wording recorded on the issue. Verification: cargo test --workspace rc=0 (cli_run 133 passed), clippy -D warnings clean, grep gates exact (zero lowercase 'usage:' literals; 11 'Usage: aura' source lines), smoke 'aura sweep --strategy bogus' prints the house-style line and exits 2. closes #179 |
||
|
|
4de6d5cbad |
rename: retire the stage1-* family for the r-family (r-sma / r-breakout / r-meanrev)
The stage1 ordinal has been dead vocabulary since the C10 reframe dropped the two-stage research model: a 1 structurally implies a 2 that no longer exists. The family is renamed by its live discriminator - the R yardstick - with members named by their signal, uniform with their signal-named siblings (sma, macd, momentum): - selectors: stage1-r -> r-sma, stage1-breakout -> r-breakout, stage1-meanrev -> r-meanrev (old tokens are usage errors, exit 2 - no silent alias) - identifiers: Strategy::RSma/RBreakout/RMeanRev, HarnessKind::RSma, r_sma_*/r_breakout_*/r_meanrev_*, wrap_r, run_signal_r, RGrid, R_SMA_* - persisted identity: the sma_signal composite (param prefix sma_signal.fast.length / .slow.length; fixtures regenerated; content-ids shift - no test pins a literal hash, the registry parses no record names) - e2e test files git-mv'd to r_sma_e2e / r_breakout_e2e / r_meanrev_e2e - dead Stage-1/Stage-2 prose reworded to post-C10 vocabulary across rustdoc, Cargo.tomls, ledger live lines, and the glossary (historical entries stay; fieldtests corpus untouched) - CLAUDE.md invariant 7 rewritten to record the ratified C10 reframe faithfully (the token-swap alone would have laundered the retired gated-currency/realistic-broker design into unmarked live prose); the unbacked account-mode clause dropped Verification: cargo build/test --workspace green (51 targets), clippy -D warnings clean, doc build clean, acceptance grep gate leaves exactly the one resampling-stage false positive (harness.rs), smoke: --harness r-sma runs, --harness stage1-r exits 2 with the new usage line. Decision log: forks and rationale recorded on the issue (reconciliation + implementation-phase comments). closes #174 |
||
|
|
88e9c9a539 |
fix(cli): symmetric sweep/mc family-verb polish (closed error, mc seed label, mandatory-knob docs)
Three polish items surfaced by the World/C21 milestone fieldtest:
- A sweep of a fully-bound (closed) blueprint is refused up front with a clear
"this blueprint is fully bound; nothing to sweep" message — the symmetric
inverse of blueprint_mc_family's closed-blueprint guard — instead of leaking a
misleading UnknownKnob for a knob that is not unknown but bound out.
blueprint_sweep_family's error contract widens to String; the sweep terminal's
BindError (a genuine unknown/kind-mismatch axis) still surfaces verbatim.
- mc reproduce lines now carry each member's seed=<N> label. Monte-Carlo members
hold no tuning params, so the member label was blank; the seed is each draw's
realization identity. Sweep / walk-forward labels (which echo params) unchanged.
- docs (design ledger + glossary): state that every open knob --list-axes prints
is mandatory on sweep / walkforward — a subset is refused with the missing knob
named (BindError::MissingKnob), there is no default; pin a knob you do not want
to vary with a single-value axis.
RED-first for the two behaviour items. One existing unknown-axis E2E is re-pointed
from the closed fixture (which now hits the new "nothing to sweep" pre-check) to
the open fixture; it still exercises UnknownKnob("nope") specifically — resolve
raises UnknownKnob in phase 1 before MissingKnob in phase 2 — so the property is
preserved, not weakened.
closes #178
|
||
|
|
8b2defa653 |
fix(cli): reject a silent-vacuous blueprint Monte-Carlo instead of returning it
`aura mc <bp.json>` builds a per-seed synthetic price walk with a fixed 60-bar SyntheticSpec. A deeper-lookback loaded blueprint warms poorly over that walk, so every seed can collapse to a bit-identical draw and the family was returned as an Ok result — a single point masquerading as a distribution, a wrong result with no error. Guard it: with >= 2 seeds, if every draw's metrics equal the first draw's, refuse with a named error (C10 refuse-don't-guess), rather than auto-sizing the walk (which would have to guess a warm-up depth the harness does not surface). Compares `report.metrics`, not the whole RunReport — the manifest seed differs per draw by construction, so a whole-report compare could never detect the collapse. RED-first; the existing `blueprint_mc_family_seeds_differ` pins that a legitimate (differing-draw) MC is not falsely rejected. Interim guard only — the real-data moving-block-bootstrap headline stays gated on the DataServer seam. refs #172 |
||
|
|
e20dd9ae49 |
Merge main into the clap-adoption cycle — re-integrate the two parallel CLI fixes
main advanced with two `fix(cli)` commits ( |