Generalize generalize/walkforward/mc to arbitrary blueprint axes; dissolve RGrid #220
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Motivation
In a topology-as-data world (C16/C24, the engine ships no project signals and
the World runs opaque blueprint data), the orchestration verbs must not know
any one strategy's parameter names. Three of the four dissolved verbs —
generalize,walkforward,mc— still do: they are structurally welded to theretired
r-smademo (blueprint namesma_signal), hardcoding its two SMAnode-names
fast/slowas their only signal axes and embedding its blueprintliterally. This is the last hardwired-harness residue #159 set out to remove;
#210's milestone-close already scoped these r-sma-specific verb front-ends to
that retirement.
sweepis the proof it can be generic — it already takesarbitrary
--axis name=csv.The weld
fast/sloware not a "candidate vocabulary": they are the node labels of onetopology. The sibling demos expose disjoint axis namespaces —
r_breakout→channel_hi.length/channel_lo.length,r_meanrev→mean_window.length/var_window.length/band.factor. No axisname is shared. The bind-key
"fast.length"is literally<node-name>.lengthof
crates/aura-cli/examples/r_sma_open.json(twoSMAprimitives namedfast/slow, wiredfast − slowviaSubintoBias).The literals are re-asserted at ~13 sites across the three verbs (four layers
each), never derived from blueprint introspection:
GeneralizeCmd/WalkforwardCmd/McCmd(
crates/aura-cli/src/main.rs:3081/3199/3241);*_args_fromextractors (main.rs:3372/3411/3440) and theRGridstruct(
main.rs:1100);translate_*doc-axis inserts andRiskRegime::Vol(
crates/aura-cli/src/verb_sugar.rs:164/168, 409/413, 568/572; risk at188/431/590);probe_paramspre-register bind check (verb_sugar.rs:325/480/643);include_str!("../examples/r_sma_open.json")at all three dispatch sites(
main.rs:3652/3974/4093).The core is already generic
The fix is CLI-sugar-local; no engine change is needed for the axes:
CampaignDocper-strategyaxes: BTreeMap<String, Axis>(
crates/aura-research/src/lib.rs:466) is keyed by arbitrary bind-key strings.referenced blueprint's own
param_space()(campaign_run.rs:bind_axes~154);no
fast/slowbelow the sugar.translate_sweep(verb_sugar.rs:73) already loops arbitrary--axis name=csvthroughaxis_from_valueswith no hardcoded name — the exact shapethe other three should adopt.
Proposed change
Replace the hardcoded
--fast/--slowargv front-ends ofgeneralize/walkforward/mcwith the generic--axis name=csvmechanism thesweeppath already runs, and replace theinclude_str!("../examples/r_sma_open.json")embed with a user-suppliedblueprint-file argument.
RGridthen dissolves entirely (it is not reshapedinto a flat struct — that would just rename the sma-cross vocabulary).
Scope boundary
The stop is the one part that is not pure sugar.
RiskRegimehas a singleVol { length, k }variant (aura-research/src/lib.rs:487); an arbitrary stoprule needs an additive enum variant + its
StopRulemapping — a core change.Keep
--stop-length/--stop-kas the single Vol regime for now; the additiveseam is a separate follow-up (the enum comment already flags a future
Fixed).Fold in #214 (bundle the translator args into a struct): it is ergonomics-only
and is obviated by this generalization, so it should be merged into this work
rather than done independently.
Cross-references
Reframed final cut of #159 (the original "dead-code sweep" is in fact this
generalization). Related: #210 (verb-dissolution, which scoped these front-ends),
#214 (arg-bundling, subsumed here).
Design reconciliation (specify)
Spec: verb-axis-generalization. The issue body settles the headline design
(generic
--axis name=csv+ user-supplied blueprint; RGrid dissolves; the stopstays the single Vol regime; #214 folded in). The forks below are finer-grained
choices the body leaves open; this records their resolution.
campaign path becomes each verb's blueprint mode with
--real:aura <verb> <blueprint.json> --real <SYM[,SYM]> --axis <name>=<csv> …. A blueprintwithout
--realkeeps the existing synthetic in-process family paths(
run_blueprint_walkforward/run_blueprint_mc) unchanged.Basis: derived —
--realis already the dissolution split's discriminator(the #210 "Reading A" comments at the walkforward/mc dispatch sites: only the
real-archive execution routes through the campaign path), and the shipped
sweep grammar discriminates exactly this way (real → campaign sugar,
synthetic → family builder).
--fast/--slow/--strategyare deleted from the three command structs; a legacy invocation fails as a
clap unknown-argument usage error (exit 2, same class as today's refusals).
Basis: derived — the issue's dissolve-don't-rename rationale; a stub flag
whose only behaviour is reproducing the same exit-2 is dead surface.
--stop-length/--stop-kstay explicit, single-value,required, mapping to
RiskRegime::Volexactly as today (sweep alone continuesto bind no regime).
Basis: derived — preserves the generated campaign-document bytes for
equivalent invocations, so the committed exact-grade anchors survive as pure
argv migrations; also keeps the issue's stop scope boundary intact.
--axison generalize carries exactlyone value; a multi-value CSV is refused (the single-candidate semantics; the
refusal migrates from the retired
--fast 2,3refusal).Basis: derived — generalize grades one candidate across instruments; a value
grid would silently turn grading into sweeping.
renderer's hardcoded axis-name list (
fast.length/slow.length/…) becomes aparameter derived from the invocation's axes plus the stop columns.
Basis: derived — the issue's motivation clause ("the orchestration verbs must
not know any one strategy's parameter names") covers user-visible output; a
welded summary renders wrong for every non-sma blueprint, which would defeat
the generalization it ships with.
--seeds(synthetic closed-blueprint seed family)and
--real+--axis(campaign pipeline) stay disjoint modes of the one mcverb; mixing flags across modes stays a usage error.
Basis: derived — they are two distinct shipped operations; the disjointness
mirrors the cross-mode refusal lists already in the mc dispatch.
(axes, name, symbols, window, blueprint reference, plus an optional stop —
sweep passes none, mirroring its empty risk vector) with per-verb extras
(walk-forward window sizes; mc resamples/block-len/seed) as explicit
parameters; acceptance: zero
#[allow(clippy::too_many_arguments)]remainsin the verb-sugar module.
Basis: derived — #214's own resolution ("bundle once over the settled common
shape, covering all four verbs"), now unblocked since all four verbs are
dissolved.
Non-goals confirmed: the
aura graphdefault blueprint embed (not a dissolvedverb), a
RiskRegime::Fixedvariant (future additive seam),--select plateauon the campaign path (tracked separately as #215), and the synthetic family
paths, which stay byte-identical.
Design reconciliation addendum (specify)
Spec: verb-axis-generalization. One further fork surfaced during the spec's
grounding review and is resolved here.
generalized verbs take the wrapped-typed form
--axis <blueprint-name>.<node>.<param>=<csv>(e.g.sma_signal.fast.length=2,3), validated against the blueprint's wrappedparam_space()and stripped to the raw document key(
wrapped_to_raw_axis) before the sugar — so the generated campaigndocuments keep today's raw keys and grades.
Basis: derived — this is sweep's shipped, test-pinned contract: the raw form
is refused at the CLI before any data access
(
aura_sweep_real_blueprint_refuses_a_raw_form_axis_name_before_data_access),and the wrapped→raw strip is pinned as an inverse pair
(
raw_matches_wrapped_and_wrapped_to_raw_axis_are_inverses). Diverging fromthat contract for the three migrated verbs would fork the axis vocabulary
across verbs — the exact inconsistency the generalization exists to remove.
Spec auto-signed (grounding-check PASS)
Spec: verb-axis-generalization (git-ignored working file, docs/specs/). Signed
autonomously: the signature is an independent fresh-context grounding review
returning PASS — every load-bearing current-behaviour assumption is ratified by
a currently-green test (sweep's grafted dispatch sequence, the wrapped→raw axis
strip inverse pair, generic bind_axes suffix-matching, the three exact-grade
anchors as migration baseline). No human signed. One earlier grounding BLOCK
(raw vs wrapped axis form) was resolved by adopting sweep's shipped wrapped
contract — see the reconciliation addendum in this thread — and the corrected
spec re-passed. Status: spec signed, ready for implementation planning.