Commit Graph

313 Commits

Author SHA1 Message Date
Brummel 07a73fb66e docs(cli,ledger): sweep #159 demo-retirement drift — stale comments + ledger notes
Cycle-close audit tidy for the #159 demo-retirement arc: behaviour-free
reword of prose/comments/docs left pointing at symbols the arc deleted.
Adversarially verified by the audit workflow; cargo build --workspace
--all-targets green.

- README: drop the removed `--harness` token; restrict the legacy built-in
  form claim to walkforward/mc/generalize (run/sweep no longer resolve one).
- glossary: the built-in `--strategy` sweep surface is retired (#159), not a
  live inline path.
- INDEX.md ledger: append [HISTORY] supersession markers (house style) to the
  dated realization notes advertising the retired `--harness` / built-in
  `--strategy` CLI forms (cost-flag #153, cycles 0065/0066/0067, name-res,
  #210 status); historical text preserved.
- aura-engine test-fixture docs: drop dead cross-crate refs to the deleted
  aura-cli fns sample_harness / build_sample and the deleted `macd` composite.
- cli_run.rs: reword the #159-cut-2 pin doc to the current clap generic-usage
  reality (no strategy_from / Strategy::RBreakout); r_sma_sweep_family ->
  blueprint_sweep_family.
- verb_sugar.rs: fix the pre-existing `E[R]` unresolved-doc-link warning.

Remaining tidy — the main.rs comment cluster plus its rustdoc link at :1546
(the second cargo-doc warning) — held for a follow-up issue. The dead
cost-graph branch is tracked in #221. refs #159
2026-07-08 11:12:22 +02:00
Brummel ee7001647c feat(cli): retire the PIP demo pair (sma/momentum) to the generic path (#159 cut 4)
The two legacy pip-scored demos — the SMA-cross (`sma`/`Strategy::SmaCross`) and
momentum (`momentum`/`Strategy::Momentum`) — and the whole built-in *synthetic*
run/sweep/walkforward/mc + `macd` surface they anchored are dropped, not
migrated: both terminated in a `SimBroker(pip_size)` construction-arg node
absent from the zero-arg std vocabulary and reported the legacy pip yardstick
C10 retired with the realistic broker. With the R-family already migrated to
data (cuts 1-3) the `Strategy` enum held only these two, so the retirement
collapses the entire built-in-strategy machinery: `Strategy`, `HarnessKind`,
`strategy_from`, `run_sweep`/`sweep_family`/`momentum_*`,
`run_sample*`/`sample_*`/`sma_cross`, the built-in
`walkforward_family`/`sweep_over`/`run_oos` and `run_mc`/`mc_family`, the `macd`
composite cluster, and `run_dispatch`/`run_args_from`/`RunCmd.--harness`.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Full workspace suite green; clippy -D warnings clean.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

refs #210
2026-07-06 17:42:36 +02:00
Brummel ed8b179e34 feat(cli): the generalize campaign translator (#210 T2)
`translate_generalize` builds the selection-bearing campaign document
(`[std::sweep(argmax), std::generalize]`, a single risk regime carrying the
stop, the candidate as single-value raw axes) + `GeneratedGeneralize` +
`register_generated_g`, mirroring the sweep translator. A determinism unit test
pins identical-invocation -> identical content id and the regime/pipeline shape.
The new items are unused until the dispatch-rewire task (#210 T3) wires the
runner; a bridge `#![allow(dead_code)]` (removed in T3) mirrors the sweep
sibling's landing.

refs #210
2026-07-06 17:04:56 +02:00
Brummel 9ddea840dd refactor(cli): factor the campaign run outcome out of the presenter (#210 T1)
`run_campaign_returning` runs the resolve->execute body and returns a
`CampaignRun` bundle (outcome + campaign/strategies/server); `present_campaign`
consumes it for the stdout tail; `run_campaign_by_id` becomes their composition.
Behaviour-preserving — the existing campaign + sweep-sugar e2e stay green
(research_docs 54/0 incl. the real-data e2e, sweep dissolution e2e green).
Exposes the outcome so the forthcoming generalize sugar can read it without the
presentation tail.

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

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

Resolves the generalize-dissolution grounding block (the unpinned
stop-mechanism R equivalence) by the risk-regime characterization-pin
pattern. refs #210
2026-07-06 15:49:46 +02:00
Brummel d96af7e1a2 audit: cycle close risk-regime axis — drift-clean after the C20 ledger fix (#210)
Architect drift review (c9d962f..HEAD). What holds: the nominee key is the
3-tuple (strategy, window, regime) and no argmax over regime_ordinal exists, so
the ledger's kept-separate / compared-not-selected semantics is what the code
does; the C18 manifest stamp is honest (the characterization pin flipped from
asserting absence to requiring the exact resolved 3/2.0, a strengthening, and a
real two-regime e2e catches per-cell mis-resolution); no dead code (cell.regime
and regime_ordinal are both read after the runtime-binding slice), absent/empty
risk keeps content-id parity, and CampaignGeneralization.regime_ordinal is
serde-default so pre-feature records still parse.

Resolution:
- FIX (inline, this commit): the spec promised a C10 AND a C20 note; only C10
  landed. C20's enumerated structural-axis list (strategy/instrument/broker/
  window) was left silently incomplete — the risk regime is added as the fourth
  matrix axis, with a dated realization pointer to the C10 semantics.
- carry (documented descope): no std::risk descriptor in CAMPAIGN_SECTIONS —
  risk is a top-level array, not a slot-bearing section; no invariant broken.
- carry (filed #212): the persist re-run + campaign_cell_key omit the regime
  because CellRealization gained none; the architect verified this is a LOUD C1
  drift-alarm refusal for a non-default regime + persist_taps, not silent wrong
  data (#212 corrected accordingly).
- trivial: validate_campaign's regime-check comment corrected (it catches NaN,
  not all non-finite; +inf is unreachable via JSON).

Regression gate: full workspace suite green, clippy clean (no dedicated
regression script; the suite + lint are the gate). Spec and plan removed
(git rm) at cycle close per the project convention. No baseline moved.

refs #210
2026-07-06 15:05:21 +02:00
Brummel 50d3db7028 feat(cli): finalize the risk-regime axis — ledger note + clippy-clean regime check (#210 T5)
The last slice of the risk-regime structural axis. The sugar parity
(`translate_sweep` emitting `risk: vec![]`, absent-serializing) and the
real-data two-regime e2e landed in the earlier slices (the loop's self-correction
and its e2e phase), so this closes the remaining pieces:

- design-ledger note: the risk regime realizes the StopRule structural axis at
  the campaign-document level — kept-separate keying, compared-not-selected (the
  R-unit argument), the C18 stamp, absent-parity, and the deferred trace gap
  (#212).
- clippy: `validate_campaign`'s regime check reads `k <= 0.0 || k.is_nan()`
  instead of `!(k > 0.0)` (neg_cmp_op_on_partial_ord), behaviour-identical.

Full workspace suite green; clippy clean.

refs #210
2026-07-06 14:56:16 +02:00
Brummel c62f7e9878 feat(cli): bind the resolved regime stop and stamp it into the manifest (#210 T4)
The runtime-binding slice — the regime's stop now actually reaches the graph and
the record:

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

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

refs #210
2026-07-06 14:52:20 +02:00
Brummel a0d8aec3f7 feat(campaign): expand the campaign matrix over risk regimes (#210 T3)
The regime becomes the fourth structural coordinate of the campaign matrix, a
kept-separate axis (a peer of window, not aggregated-over like instrument):

- aura-campaign: CellSpec gains `regime` + `regime_ordinal`; the executor
  resolves the regime list (absent/empty risk -> one default cell, ordinal 0),
  runs a cell per (strategy, instrument, window, regime), and keys the nominee
  map by the 3-tuple including the regime ordinal — so generalize aggregates
  across instruments WITHIN a regime, never across regimes. Family names gain a
  `-r{regime_ordinal}` discriminator so two regimes' families cannot collide.
- aura-registry: `CampaignGeneralization` gains `regime_ordinal` (with
  `#[serde(default)]`, so pre-feature stored records still parse — pinned by a
  new backward-compat test).

The existing single-regime execute tests shift their family ids by `-r0`; a new
test pins that two regimes yield distinct `-r0`/`-r1` families. `CellSpec.regime`
is write-only in this slice — its sole reader is the deferred member runner
(the next slice) — a held quality nit, seam-prep as the plan prescribes. Full
workspace suite green.

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

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

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

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

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

Suite 1060/0, clippy -D warnings clean.

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

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

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

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

refs #210
2026-07-04 19:01:27 +02:00
Brummel 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
2026-07-04 18:01:51 +02:00
Brummel c37abb2a27 feat(research,campaign): optional selection group on the sweep stage (#210 T1-T2)
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
2026-07-04 17:43:53 +02:00
Brummel 8235cee4d7 test(cli): characterization pins ahead of the sweep dissolution
Two green pins the sweep-dissolution cycle rests on (refs #210):

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

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

closes #184
closes #185
2026-07-04 15:19:58 +02:00
Brummel 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
2026-07-04 15:12:40 +02:00
Brummel 7604651579 feat(cli): aura.css becomes the style pin — tokens, component library, CLAUDE.md rule
Second half of the style pin. assets/aura.css now carries (a) the full
:root token palette (Catppuccin-Mocha-derived; --bg deliberately darker
than Mocha base; new --border3 #585b70 completes the border ladder at an
identical computed value), (b) the shell rules migrated color-literal to
var() with identical computed values (font stacks stay literal — the
--mono token's longer fallback chain would change the computed value),
and (c) an opt-in, class-scoped component library distilled from the
milestone demo page (.aura-doc register, .wrap, .badge, .stats, .cards,
.term, .callout, table.res, .pipe) — unused by the runtime pages, which
render pixel-identically. CHART_CSS migrates to the same tokens (it
lands in the same document after the shell <style>, so :root resolves).
CLAUDE.md gains the binding '## HTML surfaces' rule: every HTML surface
embeds this asset; extend, never fork.

Gates: suite 1043/0, clippy -D warnings clean, doc build 0 warnings;
regenerated chart page resolves all 17 used var() names against its
:root. Independent CSS audit passed (two cosmetic notes: --bg3/--orange
currently unused; the pre-existing html,body height:100% also reaches
future .aura-doc pages).

closes #209
2026-07-04 11:24:07 +02:00
Brummel 652ad34b3f refactor(cli): extract the shared shell stylesheet into assets/aura.css
Pure byte-preserving extraction: the CSS block inside SHELL_HEAD moves
verbatim into crates/aura-cli/assets/aura.css and is spliced back via
concat!(.., include_str!(..), ..). Proven against a pre-change baseline:
chart HTML (aura chart 42edebd2-0/597d719b-GER40-w0) and graph HTML
(bare aura graph) regenerate sha256-identical (bcc2cc0f.., a0e6a97f..),
each deterministic across two generations. Suite 1043/0.

First half of the style pin: the asset becomes the single source the
runtime pages embed; tokens + component library follow separately.

refs #209
2026-07-04 11:11:09 +02:00
Brummel 6f2cf443c4 fieldtest: milestone-research-artifacts corpus (GREEN) + Aura.toml data-key doc fix (F7)
MILESTONE VERDICT: GREEN — the whole span (0106-0109) holds end to
end as one continuous consumer scenario: authored from a bare {} via
introspection alone, every ref resolved from the public surface, three
validation tiers, run by content id over two real instruments through
the full v2 pipeline, realization recorded (selection/survivors/
bootstrap/generalizations/trace_name), traces on disk and charted,
read back via campaign runs, byte-identical re-runs (C1), refusals
precise. 0 bugs, 0 frictions, 0 spec gaps; 2 doc-gaps.

F7 fixed in this commit (the docs claimed an archive-root path without
naming the key; a guessed [paths] archive silently no-ops): project-
layout and the glossary Aura.toml entry now name [paths] data, and the
aura new template advertises the commented-out key. F8 (op-script
grammar not CLI-discoverable) filed as #208, sibling of the #197 docs
half — one docwriter pass once desired.

The milestone-close gate evidence is this corpus; the formal milestone
close and the push remain user-reserved.

refs #189
2026-07-04 04:45:36 +02:00
Brummel 01925e0619 feat(cli): envelope-probe discoverability + kind-key refusal prose (#193 residue)
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
2026-07-04 04:27:52 +02:00
Brummel 179c2f8bf0 audit: cycle 0109 tidy (drift resolved by ledger/glossary lift + tap-channel cross-pin)
Architect verdict: drift_found — the code holds (C1 honored not
assumed: the nominee re-run's metrics-equality hard refusal; C22/C14
clean: existing TraceStore + unchanged viewer, serde-default widening
round-trips, name composition single-sourced in derive_trace_name).
Resolved here:

- C18: the 0107 paragraph's 'persist_taps is deferred' points forward;
  new cycle-0109 realization paragraph records the closed tap
  vocabulary + UnknownTap tier, the nominee-only non-reduce re-run
  with the C1 metrics guard, the campaign trace family layout, the
  trace_name claim contract, the loud-skip lines, and the noted
  chart-over-family-root debt.
- Glossary: tap entry names the closed vocabulary + escalation rule;
  campaign document's presentation clause references it; campaign run
  gains the trace_name pointer.
- Debt fixed inline (architect med): tap_channel gains the
  emit_vocabulary-twin debug_assert cross-pin so a fifth vocabulary
  tap fails loudly instead of silently skipping.

The consumed 0108 fieldtest spec is removed with the cycle's spec+plan
(all its dispositions shipped: F6 #205, F8 #207, F11 #206, F7/F9/F10
doc-tightens).

Regression: cargo test --workspace 1041/0; clippy -D warnings clean;
cargo doc 0 warnings.

refs #201
2026-07-04 04:22:41 +02:00
Brummel 46a85d3ea4 feat(cli): persist_campaign_traces — campaign presentation persists nominee traces (0109 tasks 3-4)
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
2026-07-04 04:16:22 +02:00
Brummel b8ef77b1c7 feat(research,registry,campaign): tap vocabulary + trace_name record contract (0109 tasks 1-2)
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
2026-07-04 03:41:01 +02:00
Brummel 0bb203a38f feat(cli): aura campaign runs — read-back for stored realizations (fieldtest 0108 F11)
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
2026-07-04 02:22:36 +02:00
Brummel 891c40d64b feat(cli): generalize applicability tag + honest R-expectancy refusal prose (fieldtest 0108 F8)
--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
2026-07-04 02:14:58 +02:00
Brummel 38ffe50b57 feat(cli): campaign validate runs the executor preflight as a third tier (fieldtest 0108 F6)
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
2026-07-04 02:08:28 +02:00
Brummel 197936b27d docs: annotator-stage discoverability tightens (fieldtest 0108 F7/F9/F10)
The std::monte_carlo block doc now names the position-dependent
semantics (pooled OOS after wf / per-survivor otherwise) and the > 0
execution guard the generic u32 slot hint could not carry; the
std::generalize block doc names campaign scope, the >= 2-instrument
need, and the R-expectancy metric family. Glossary: new 'stage
bootstrap' entry defines the realization payload incl. prob_le_zero;
the campaign-run entry gains the generalizations schema and the
stdout-wrapper-vs-bare-store-line note. Introspection text and
glossary only — no document content id moves.

Gates: workspace green, clippy clean.

refs #200
2026-07-04 01:56:48 +02:00
Brummel 7e4e9a8827 feat(cli): annotator-stage seam flips + doc tidy + gated e2e with the mc suffix (0108 task 5)
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
2026-07-04 01:28:06 +02:00
Brummel a1cd7bc6f5 feat(campaign): monte_carlo stage arm + campaign-scope generalize phase (0108 tasks 3-4)
The mc arm bootstraps the stage's incoming R-evidence with one
semantics, input-shaped by position (#200 decision 1): after a
walk_forward it pools the wf family's per-window OOS trade_rs in roll
order into ONE r_bootstrap (PooledOos); after sweep/gates it
bootstraps each surviving member's fresh in-memory series
(PerSurvivor, ordinals into the population family; a zero-trade
member records the engine's defined all-zero degenerate). Seeded from
the campaign doc's seed (C1). The wf family is read by reference from
the cell's families vec — no clone. run_cell's Generalize arm is an
explicit no-op naming the campaign scope.

The generalize phase runs after the cell loop (#200 decision 2):
nominees (last wf window's OOS report, else the sweep winner; None on
gate truncation) grouped by (strategy, window) across instruments via
a BTreeMap (deterministic order); >= 2 winners feed the shipped
generalization(), fewer record the shortfall (generalization: None,
missing named) — divergent per-instrument winners are exposed via
their params, never averaged away.

Gates: campaign 40/0 (4 execute_mc + 3 execute_generalize new),
workspace build clean; the known-RED cli integration test flips in
task 5.

refs #200
2026-07-04 00:57:35 +02:00
Brummel ab778f2361 feat(campaign,registry,engine): record widenings + preflight v2 fault set (0108 tasks 1-2)
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
2026-07-04 00:38:25 +02:00
Brummel 38f7098bdb spec: 0108 annotator stages (boss-signed) + tier-boundary characterization pin
Cycle 0108, anchored on #200. Encodes the five swarm-triaged decisions
(#200 comment 2026-07-03, all convergent on cited ground, no bounce):
std::monte_carlo bootstraps the stage's incoming R-evidence (per-survivor
after sweep/gates, pooled per-window OOS series after walk_forward) via
the shipped r_bootstrap; std::generalize runs at campaign scope over
per-cell nominees per (strategy, window); both stay terminal annotators
(unanimous); realization widens existing records (StageRealization.bootstrap,
CampaignRunRecord.generalizations, serde-default); v2 executable shape
sweep [gate]* [wf]? [mc]? [generalize]? with static preflight guards.
No document-schema bytes move — every existing content id survives.

Boss-signed via grounding-check PASS (11/11). The first dispatch
BLOCKed on one unpinned current-behaviour assumption ([sweep, mc,
walk_forward] passes the intrinsic tier); the gap was closed by the
green characterization test
validate_process_permits_walk_forward_after_an_annotator (pins the
intrinsic/executor tier boundary), and the fresh re-dispatch ratified
all eleven assumptions.

refs #200
2026-07-03 23:44:20 +02:00
Brummel 4da47cb919 fix(cli): aura new skips git init inside an existing work tree (0107 fieldtest, orthogonal)
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
2026-07-03 23:19:34 +02:00
Brummel 8ef829b8cc feat(cli): process|campaign introspect --metrics — the roster split becomes discoverable (0107 F9)
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
2026-07-03 23:15:16 +02:00
Brummel 1d87397c95 feat(research): draft placeholder refs ({} / null) read as open slots in the unwired probe
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
2026-07-03 23:07:10 +02:00
Brummel 63d2657850 feat(cli): one bare content-id display form; CLI targets tolerate the content: prefix (0106 F3 / 0107 F6)
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
2026-07-03 23:01:20 +02:00
Brummel c3d62f2ce3 feat(cli): graph register / --params accept the op-script form (fieldtest 0107 F5)
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
2026-07-03 22:49:12 +02:00