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
The `aura new` scaffold shipped one node, a param-less Identity, so a
downstream author had no public example of a build closure consuming a
bound param — the highest-value gap the project-environment fieldtest found
(F2): a newcomer could copy a param-less node but had to read engine source
to write their first *tunable* one, which the consumer contract forbids.
Swap that single starter node for a Scale node (mirroring the std gain
operator): its schema declares a `factor` ParamSpec and its build closure
reads the bound value (`|p| Box::new(Scale::new(p[0].f64()))`), and the
starter blueprint binds `factor` so `aura run` exercises the bound-param
path with zero hand-editing.
factor is bound to 1.0 — a neutral identity default the newcomer then tunes,
not the only C7-safe value (only |factor|>1 would push the terminal `bias`
output past the bounded [-1,+1] contract; the knob is genuinely read and
multiplied each cycle). The param-reading closure is the copyable template
content #183 delivers; a hypothetical `|_|` regression is caught by the
scaffold unit test's source guard, and tests/project_new.rs re-validates the
swap by building and running the scaffold end to end.
Template-side only (#181): the frozen 0102 demo-project fixture is untouched.
closes#183
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
The clap help promises "persists each member's taps" and the chart hint
recommends `aura sweep --trace`, but every blueprint-mode arm discards the flag
(run_blueprint_sweep's `let _ = persist`, the --real sugar's persist_taps: vec![])
— the surface advertises trace-recording the code drops. Pin the decided honest
behaviour: sweep/walkforward refuse --trace up front (exit 2, naming the flag and
pointing forward to the deferred trace feature #224), mirroring the aura run / mc
refusals.
refs #168
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
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
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).
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.
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
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
Cycle-close audit for the verb-axis-generalization cycle (a2294c7,
e7efe1f, b849bb7). Architect drift review: what holds — all four verbs
route through the one validate_and_register_axes seam into the campaign
executor; RGrid gone workspace-wide; zero too_many_arguments allows in
verb_sugar.rs; no r_sma_open embed at the verb dispatch sites (the
aura-graph default embed survives by design). Five drift items found,
all resolved fix-path in this commit:
- README (blueprint-verbs section + mc row), glossary (walk-forward
entry), and ledger (verb-dissolution passage) reconciled to the
shipped generic grammar; the ledger HISTORY note now separates the
two supersessions correctly (#159 retired the built-in --strategy
demo surface; #220 de-welded the verbs) instead of mis-attributing
the weld removal to #159.
- Four stale characterization doc-comments in cli_run.rs reworded:
the retired welded grammar is now named as the pinned grades'
provenance, not the current spelling; one dangling reference to a
deleted test repointed to the live anchor.
- The quadruplicated dispatch blocks extracted into two shared helpers
beside validate_and_register_axes: exit_axis_register_error (the
AxisRegisterError exit-map, byte-identical stderr + exit codes) and
campaign_window_ms (from_choice -> full_window -> unix-ms clip);
generalize's explicit unclipped (Some, Some) passthrough arm is
untouched — only its fallback arm, which already ran the identical
mechanics, calls the helper.
Regression gate green, no baseline moved (nothing to ratify):
cargo test --workspace exit 0, 62 test-result groups, 0 failures;
clippy --workspace --all-targets -D warnings clean; cargo doc
--workspace --no-deps 0 warnings. Spent cycle spec + plan working
files discarded per lifecycle.
refs #220
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#220closes#214
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
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
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
Cycle-close audit tidy for the #159 demo-retirement arc: behaviour-free
reword of prose/comments/docs left pointing at symbols the arc deleted.
Adversarially verified by the audit workflow; cargo build --workspace
--all-targets green.
- README: drop the removed `--harness` token; restrict the legacy built-in
form claim to walkforward/mc/generalize (run/sweep no longer resolve one).
- glossary: the built-in `--strategy` sweep surface is retired (#159), not a
live inline path.
- INDEX.md ledger: append [HISTORY] supersession markers (house style) to the
dated realization notes advertising the retired `--harness` / built-in
`--strategy` CLI forms (cost-flag #153, cycles 0065/0066/0067, name-res,
#210 status); historical text preserved.
- aura-engine test-fixture docs: drop dead cross-crate refs to the deleted
aura-cli fns sample_harness / build_sample and the deleted `macd` composite.
- cli_run.rs: reword the #159-cut-2 pin doc to the current clap generic-usage
reality (no strategy_from / Strategy::RBreakout); r_sma_sweep_family ->
blueprint_sweep_family.
- verb_sugar.rs: fix the pre-existing `E[R]` unresolved-doc-link warning.
Remaining tidy — the main.rs comment cluster plus its rustdoc link at :1546
(the second cargo-doc warning) — held for a follow-up issue. The dead
cost-graph branch is tracked in #221. refs #159
The 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
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
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
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
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
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
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
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
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
`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
`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
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
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
The first slice of the risk-regime structural axis (spec/plan abab1f1):
- aura-research: a serializable `RiskRegime` closed vocabulary (externally
tagged, sole variant `Vol { length, k }` — a content-addressable mirror of
the shipped two-variant StopRule, so a future Fixed regime is additive), and
an optional top-level `CampaignDoc.risk: Vec<RiskRegime>` that absent-
serializes (the `description` precedent) — content-id parity for every
risk-less document.
- validate_campaign refuses a non-positive regime (length < 1 or k <= 0, NaN
included) via a new `DocFault::BadRegime`; the CLI renders it as prose at the
research_docs seam (register/validate paths).
- The three CLI-seam E2E tests pin that a well-formed risk section passes
`campaign validate` and a bad one is refused with prose (exit 1, no Debug
leak) — the field is reachable at the seam a data author drives, not only in
library units.
Adding the field is compile-breaking at every `CampaignDoc { .. }` literal
(serde default does not cover Rust construction), so this slice also threads
`risk: vec![]` into the sites across aura-cli and aura-campaign — the
field-addition's compile-gate completion the plan should have scoped here. The
BadRegime prose reads `risk[i]: ...` (the implementer's terser wording over the
plan's illustrative string; the spec pinned no exact text). Full workspace
suite green.
refs #210
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
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
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
The std::sweep selection triple (metric/select/deflate) becomes ONE
optional group, Option<SweepSelection>, serde-flattened so the wire
form keeps the flat slots: every existing document parses to Some and
its canonical bytes — and content id — are unchanged (golden pin
byte-untouched). A half-populated group is refused by the schema-strict
parser (all-or-nothing); a bare {"block":"std::sweep"} is the
selection-free sweep.
Campaign side: preflight permits a selection-free sweep only as the
pipeline's terminal stage (new ExecFault::SelectionFreeSweepNotTerminal,
prose in aura-cli's exhaustive exec_fault_prose); the executor's
selection-free arm appends the family exactly as before but records no
StageSelection and produces no nominee (recording one would fabricate
intent — #210 fork decision 1).
Introspection follows: the sweep block's schema table marks the group
optional; CLI introspection pins updated. New tests: group-or-nothing
refusals, selection-free round-trip + distinct content id, terminal-only
preflight accept/refuse, selection-free execute realization.
refs #210
Two green pins the sweep-dissolution cycle rests on (refs #210):
- sweep_real_blueprint_member_lines_pin_the_inline_contract: the inline
real-data blueprint sweep prints one member line per grid point in
odometer axis order, carries the swept bindings in manifest.params,
persists exactly one Sweep family — and never stamps an instrument
key (RunManifest::instrument stays None on this path).
- campaign_run_real_e2e_sweep_gate_walkforward (extended): every
family_table member line emitted by the real campaign member-runner
path is stamped with the instrument under test.
Together they pin the one behavioural delta between the two paths (the
additive instrument stamp) that the dissolution's parity contract is
phrased against.
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#184closes#185
The --unwired help line names the whole-envelope starting move (start
from a bare {}), closing the 0106-F1 discoverability gap the narrowed
issue kept open; the wrong-kind refusal names the exact key a blind
author must fix ('the "kind" key must be "process"') instead of
describing the mismatch abstractly. The third original item (block-ids
vs field names in --vocabulary) is sufficiently served by the
envelope probe per the issue's narrowing comment.
RED-first (tdd-author handoff): both pins observed failing on the old
help text / prose.
Gates: workspace 1043/0, clippy -D warnings clean.
closes#193
The deferral line dies; persist_taps is honored: after execute()
returns with a claimed trace_name, the CLI re-runs each cell's nominee
once in non-reduce mode (all four channels drained; windowed to the
nominee manifest's own ns bounds; bound via the shipped
point_from_params inverse against the wrapped probe space), asserts
METRICS equality against the recorded nominee (the C1 drift alarm —
hard refusal on divergence), and writes the requested-and-producible
taps through the existing TraceStore as
traces/{campaign8}-{run}/{strategy8}-{instrument}-w{n}/{tap}.json
(ensure_name_free once; window ordinal doc-derived by position match).
Loud lines: per-cell no-nominee skip, per-run net_r_equity
unproducible skip (no cost leg in the campaign runner), one summary.
The Task-1(b) seam test tightened to pin the deferral line's absence.
Gated e2e extended: persist_taps [equity, r_equity] over GER40 —
trace_name in the record, both tap files on disk, aura chart reads
the cell back — ran its FULL path on this host (local archive
present), not the skip branch.
Gates: workspace 1041/0, clippy -D warnings clean.
closes#201
Task 1: the tap namespace is a closed vocabulary — tap_vocabulary()
(the wrap convention's four sink names; escalation = new entry or an
authored blueprint sink, never an open node-path namespace) with
DocFault::UnknownTap in validate_campaign (index-addressed, prose
enumerating the vocabulary via the single source), SlotKind::TapKinds
on the persist_taps slot, open-slot hint naming the taps. The 0107
deferral seam test split into its two 0109 pins (unknown tap refuses
at validate; a valid tap passes validate and hits the member-data
seam) — the deferral eprintln itself survives until task 3.
Task 2: CampaignRunRecord.trace_name lands serde-default sparse; the
claim contract holds across the seam — execute stamps the sentinel iff
persist_taps is non-empty, append_campaign_run composes
Some("{campaign8}-{run}") onto the stored line (safe prefix take at
the registry seam), execute mirrors the same derivation onto the
returned copy; None stays None; the hand-seeded byte-identity dump pin
survives (absent -> skipped).
Gates: workspace 1037/0, clippy -D warnings clean.
refs #201
The recorded bootstrap/generalization evidence is no longer write-only:
'aura campaign runs' lists every stored record (campaign id, run
counter, cell count, first cell's stage signature); 'aura campaign
runs <id> [run]' dumps the BARE stored line(s) — the store form,
byte-identical, never the {"campaign_run": ...} run-emit wrapper (the
F10 parsing trap). A lookup, not an action: empty/unmatched notes and
exits 0 (the runs-family convention); outside a project the campaign
family's project refusal.
RED-first (tdd-author handoff): hermetic seed of one hand-written
store line into the fixture project, list + byte-identical dump pinned,
observed failing on clap's unknown-subcommand exit 2.
Gates: workspace 1027/0, clippy -D warnings clean.
closes#206
--metrics now tags the four generalizable names (expectancy_r,
net_expectancy_r, sqn, sqn_normalized) via check_r_metric as the
predicate — the registry's own R-expectancy rule, no fourth roster
site (#190). The GeneralizeNonRMetric refusal names the real rule
with the roster pointer instead of mislabeling R-denominated-but-
unranked metrics (max_r_drawdown is a drawdown in R, not a 'pip
metric').
RED-first (tdd-author handoff): the extended --metrics pins and the
flipped prose pins (unit + seam) observed failing on the old output.
Gates: workspace 1026/0, clippy -D warnings clean.
closes#207
valid == runnable for every pure document/campaign property: inside a
project, once intrinsic + referential pass, validate fetches the
referenced process from the store and runs the data-free
aura_campaign::preflight (promoted to pub) — an executable shape gains
'campaign document valid (executable): pipeline shape and static
guards pass'; a shape/param/metric/instrument fault refuses under
'campaign is not executable:' with the exec_fault_prose detail, exit 1,
before any member runs. Outside a project the skip line is unchanged
(the third tier needs the store like the second).
Collateral the RED author predicted: the referential e2e's OK fixture
(PROCESS_DOC: deflate+plateau sweep, overfit_probability gate) was
intrinsically-valid-but-never-executable — that test now registers an
executable twin (argmax/no-deflate, per-member gate metric); the base
PROCESS_DOC fixture stays untouched for all other consumers.
RED-first (tdd-author handoff): the third-tier pin observed failing on
the two-line validate output.
Gates: workspace 1026/0, clippy -D warnings clean.
closes#205
The retired v1-boundary test is replaced by v2 pins: mc-before-wf
refuses with the executor shape prose (while process validate accepts
the same doc — the tier boundary observable across two commands, added
by the task-2 e2e phase and kept), single-instrument generalize
refuses data-free on the built fixture project, and the two
addressing-mode tests' stale rationale comments now name the
member-data seam they actually hit. PROCESS_BLOCKS doc strings drop
'in v1' (introspection text only — no content id moves). The gated
real-data e2e extends its process with the monte_carlo suffix and
asserts the pooled_oos bootstrap in the final campaign_run line — ran
its full assert path on this host.
Task-5 duplicates the plan had double-drafted (task 2's e2e phase
pre-landed three of its tests) were correctly NOT re-added.
Gates: workspace 1025/0 (fully green again), clippy -D warnings clean.
refs #200
Task 1: RBootstrap and Generalization gain serde derives;
lineage.rs gains StageBootstrap (per_survivor | pooled_oos) and
CampaignGeneralization; StageRealization.bootstrap and
CampaignRunRecord.generalizations land serde-default sparse (a
pre-0108 campaign_runs.jsonl line still parses — pinned), threaded
None/empty through every literal site. No behaviour change.
Task 2: ExecFault::UnsupportedStage is gone; the preflight now admits
the v2 shape sweep [gate]* [wf]? [mc]? [generalize]? via a monotone
rank walk (precise per-pair PipelineShape prose) and statically
refuses: single-instrument generalize (GeneralizeNeedsInstruments),
non-R generalize metrics via check_r_metric (GeneralizeNonRMetric —
no fourth roster site), zero mc params (ZeroBootstrapParam). CLI
prose swapped accordingly. The loop's E2E phase added a tier-boundary
pin: process validate accepts [sweep, mc, walk_forward] while campaign
run refuses it data-free.
KNOWN RED (documented in the plan, flips in task 5):
crates/aura-cli/tests/research_docs.rs::campaign_run_v1_boundary_refuses_mc_process
still pins the retired 'not executable in v1' prose. Interim gates:
registry 57/0, campaign 33/0, cli --bin 111/0, workspace build clean.
refs #200
The cargo-new mirror completed: a scaffold landing inside a tracked
tree no longer nests a .git (the gitlink/submodule hazard that bit the
fieldtest corpus); outside a work tree the best-effort init is
unchanged. Oracle: git rev-parse --is-inside-work-tree in the target
dir; any failure reads as not-in-a-work-tree so the init stays
best-effort.
RED-first (tdd-author handoff): the inside-a-work-tree pin observed
failing on the nested .git; the outside guard pinned against
over-removal.
Gates: workspace 1003/0, clippy -D warnings clean.
closes#204
The 17-name metric vocabulary lists with applicability tags derived
from aura-campaign's existing pub rosters (rankable | gate |
annotation) — no fourth roster site (#190); the mode rides the shared
introspect struct and answers for both doc families. The three
metric refusals (intrinsic unknown-metric, executor unrankable and
gate-not-per-member) now point at the mode, so the
exists-but-not-rankable split (profit_factor is emitted per member yet
not selectable) is no longer trial-and-error.
RED-first (tdd-author handoff): six same-seam pins (roster lines +
tags, one-mode guard incl. the extended usage line, both-families
parity, three refusal pointers) observed failing, then the mode +
hints landed.
The worked-examples half of #197 stays open (docwriter territory
post-stability, per the issue itself).
Gates: workspace 1001/0, clippy -D warnings clean.
refs #197
A campaign draft can now validate-so-far AND report its open ref slots
(0106 fieldtest F4): open_slots_campaign treats an absent ref, {} and
null uniformly as the open slot (strategies[i].ref with the two-arm
hint; process.ref with the content-id-only hint). Draft affordance of
the probe only — the strict parse tier keeps refusing placeholders (a
placeholder is not a valid document form; canonical bytes stay
untouched).
RED-first (tdd-author handoff): library pin + CLI --unwired e2e
observed failing (probe reported 'no open slots' over placeholder
refs), then the two ref predicates routed through ref_slot_is_open.
Gates: workspace 995/0, clippy -D warnings clean.
closes#195
The copyable token is now the valid ref form everywhere: all three
register lines print 'registered <family> <id> (<path>)' and both doc
introspect --content-id modes print the bare 64-hex id (converging on
graph introspect's existing form). CLI FILE-or-id targets (campaign
run, graph introspect --params) strip a leading 'content:' before
resolving — a CLI arg is ephemeral input, leniency costs nothing. Doc
ref FIELDS stay bare-only (two accepted spellings would make
semantically identical docs hash apart — canonical-form byte
stability outranks input leniency); instead the referential not-found
prose appends 'refs use the bare 64-hex id — drop the content: prefix'
when the offending ref carries it.
RED-first (tdd-author handoff): eight same-seam assertions across the
three surfaces observed failing on the prefixed forms, then the five
display sites, two target ladders, and the conditional hint landed.
Gates: workspace 993/0, clippy -D warnings clean.
closes#194
Both verbs now route through the same composite_from_any shape
discrimination file-mode --content-id shipped with (op-script array ->
one-build tail; envelope object -> loader), so the three on-ramp verbs
agree on accepted shapes and an op-script registers under the same
shape-invariant content id as its built envelope — no more raw serde
refusal ('invalid type: map, expected u32') hiding the graph build
bridge.
RED-first (tdd-author handoff): two same-seam behavioural pins
(register stores the envelope id for an op-script; --params matches
the envelope's axis lines) observed failing on the serde refusal, then
the two call sites rerouted.
Gates: workspace 990/0, clippy -D warnings clean.
closes#202
validate == runnable (fieldtest 0107 F7): validate_campaign_refs now
checks the reverse direction — every open param of the resolved
strategy must be bound by some campaign axis — emitting the new
RefFault::ParamNotCovered { strategy, param } with the RAW
param_space() path; ref_fault_prose phrases it ('open param "X" is
bound by no campaign axis'). The executor's every-open-knob-required
rule is thereby mirrored at validate time, and campaign run's
referential gate catches an uncovered knob before any member binds.
The e2e fixture that legitimately under-covered (fast only) gains the
slow.length axis.
Second half, RED-first: the member-bind refusal no longer leaks the
wrapped bind-time path — it names the raw campaign-axis namespace with
the wrapped path as a parenthetical ('open param "threshold" ...
(wrapped path: sma.threshold)').
RED evidence: E0599 on the pinned ParamNotCovered test (tdd-author
handoff), then the flipped bind_axes prose pin observed failing on the
wrapped form.
Gates: workspace 988/0, clippy -D warnings clean.
closes#203