Dissolve the orchestration verbs into the campaign path (sweep, mc, walkforward, generalize) #210
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?
Context
Fork A of the role-model design pass (issues/188#issuecomment — "Direction forks resolved", 2026-07-03) settled the fate of the four standalone orchestration verbs: dissolve them into the campaign path — "Das Ziel muss sein, das bestehende im Neuen aufzulösen - aber erst wenn das Neue trägt" (user, 2026-07-03). The reading on record: a verb is re-cut as thin sugar over a generated campaign document only when (not before) the campaign substrate reproduces its behaviour, then retires on the #159 pattern; the "once it carries" gate applies per verb.
The gate's precondition is now satisfied: the "Research artifacts — process & campaign documents" milestone closed 2026-07-04 with a green end-to-end milestone fieldtest — the campaign substrate demonstrably carries its own path. What remains per verb is coverage: whether the campaign path reproduces that verb's behaviour. This issue anchors the dissolution milestone and is the decision log for its design forks.
Coverage map (recon against the current tree, 2026-07-04)
sweep — The verb performs no selection: it appends a
FamilyKind::Sweepfamily and prints one member line per grid point; ranking is deferred toaura runs family <id> rank. The campaign sweep stage's core job IS selection (metric+select+deflaterequired; aStageSelectionis always recorded). Verb-only surface: ad-hoc CSV axes (--fast/--slow/…),--axison blueprints,--list-axes, per-member--tracedirs, the five built-in demo strategies, the synthetic data path. Campaign-only: trials-deflation and plateau as first-class slots, the multi-instrument x multi-window matrix,selection_report.mc — Three verb sub-paths. Only the R-bootstrap path (
--strategy r-sma: walk-forward, pool OOS trade-R,r_bootstrap) has a campaign analog (thestd::monte_carlostage'sPooledOosarm). The synthetic seed-resweep and blueprint--seeds Npaths re-run the harness across distinct synthetic price realizations and reduce metric distributions (McAggregateovertotal_pips/max_drawdown/bias_sign_flips) — no campaign equivalent exists. Genuine semantic difference: the verb (synthetic/blueprint paths) resamples the data-generating seed; the campaign stage resamples the realized R series. The campaign'sPerSurvivorarm conversely has no standalone verb.walkforward — The verb hard-wires the selection metric per strategy (
total_pipsfor sma,sqn_normalizedfor r-sma/blueprints) and the window geometry (synthetic: bar-index 24/12/12; real: 90/30/30 days), always rolling, and supports--select plateau:*(it sweeps the full grid per window, so a lattice exists). The campaign wf stage takes explicitin/oos/step_ms+mode(incl. anchored, which the verb lacks), a free metric slot, but refuses plateau (it re-sweeps only post-gate survivors — no lattice) and emits no stitched summary (stitched_total_pips,param_stability, pooledoos_r).generalize — The verb grades one fixed candidate (identical params applied to every instrument, r-sma only, single window): the cross-instrument floor of the same params everywhere. The campaign generalize stage grades each cell's own selected nominee (per-instrument-optimized winners) per (strategy, window). The verb's question is expressible in the campaign vocabulary as a degenerate case: single-point axes make each cell's nominee exactly the fixed candidate.
Presentation — Each verb prints a bespoke summary line (
walkforward_summary_json,mc_aggregate_json/mc_r_bootstrap_json,generalize_json) with no campaign stdout equivalent;aura campaign runemits emit-gatedfamily_table/selection_reportlines plus one finalcampaign_runrecord line. A sugar re-cut needs a presentation adapter reconstructing the verb's pinned lines from record fields. Thefamily_member_linebyte shape is already shared between both paths.Cross-cutting — The campaign member runner executes stored blueprints over real archive windows only. The verbs' built-in demo strategies and synthetic data branches have no campaign target at all; their retirement is #159's subject.
Open design forks (gate the first cycle's spec)
Acceptance (per verb)
The verb's dissolved invocation forms run as thin sugar over an internally generated campaign document through the one campaign executor; the behaviour pinned by the verb's existing integration tests is reproduced; the campaign run record captures the realized intent that previously evaporated into shell history. Undissolved branches (per fork 2) stay untouched.
Refs #188, refs #159.
Fork triage: verb dissolution (grounded stance review, 2026-07-04)
The seven open forks in this issue's body were populated by five adversarial design lenses (minimalist / composability / maintainer / skeptic / off-ledger wildcard), each grounding its answers in the design ledger, glossary, issues #188/#159/#210, and the code. Decisions below are made on cited ground; veto by replying here. No fork remained a genuine direction fork: where lenses split, one side's cited ground is dispositive and the adjudication is recorded.
Decided
Fork 1 — selection becomes optional in the
std::sweepstage vocabulary, permitted when no downstream stage consumes a nominee (at minimum: when the sweep is terminal). All lenses rejected a sugar-injected default selection: a run record claiming selection intent the invocation never expressed is a permanent audit lie (C18's reproducible-manifest discipline in spirit). The counter-position "leave the selection-free form un-dissolved" grounds on the fork-A timing clause ("until then the verbs stay untouched"), which governs when, not whether to cover a branch — the same user decision's goal sentence ("Das Ziel muss sein, das bestehende im Neuen aufzulösen", 2026-07-03) settles the direction. The optional slot wraps shipped, inventoried behaviour (aura sweepappends a family and defers ranking toaura runs family <id> rank), so the "no invented taxonomy" guard does not apply. The exact structural rule (terminal-only vs. required-iff-a-nominee-consumer-follows) is a vocabulary-spec detail.Fork 2 — dissolution covers the real-data blueprint branches only; the built-in demo strategies and synthetic data branches stay verb-wired until #159 retires them. Converged 4:0 on cited ground: #159's own body gates that retirement separately ("Droppable / no commitment until those land"), C16 forbids project signals in the engine repo, and extending the closed campaign vocabulary with a synthetic-source form would build vocabulary for constructs already earmarked for deletion. This also matches the honest extent of the "once it carries" gate evidence: the green milestone fieldtest (0106-0109) exercised the real-archive path only — the dissolved scope and the validated scope coincide.
Fork 3 — the seed-resweep Monte-Carlo semantics (synthetic seed-resweep, blueprint
--seeds N) stay verb-only, an un-dissolved residue whose fate follows the built-in/synthetic branches (#159). Consequence of decision 2, not a preference: without a synthetic source form in the document vocabulary, a seeds axis has nothing to range over — the campaign member runner executes stored blueprints over real archive windows only. The P1 licence ("an MC run IS a loop over seeds", issues/188 power-axis comment) keeps the door structurally open should synthetic sources ever enter; recorded alongside: if such a form ever lands, it must not share themonte_carlostage name — resampling the data-generating seed and bootstrapping the realized R series are different objects and a shared name would conflate them. Retiring the semantics now (the C10-precedent argument) was rejected: C10's retirement carried an epistemic argument (historical friction unknowable) that does not transfer here.Fork 4 — plateau selection becomes permitted in the campaign walk-forward stage when no gate precedes it (structural, preflight-checkable); it stays refused when a gate precedes. The shipped refusal's own recorded reason is "a gated survivor subset has no grid lattice"; in a gate-free document the wf stage re-sweeps the full declared grid, the lattice exists, and the refusal would be a rule outliving its reason. Unconditional allowance (silent degradation on non-lattice survivor sets) was rejected by every lens that addressed it. This is the enabling change for dissolving the walkforward verb's
--select plateau:*branch.Fork 5 — full behaviour parity: byte-identical pinned stdout via a presentation adapter deriving the verbs' summary lines from campaign records, plus registry-family parity (dissolved verbs keep persisting the
FamilyKindfamilies the verbs persist today). "Reproduces its behaviour" is the ratified gate wording (fork A, 2026-07-03) and the verbs' integration tests pin stdout shapes and families; re-pinning the tests would redefine the behaviour instead of reproducing it. Registry families are data, not presentation:aura runs family <id>/rankare live consumers that would silently stop finding families otherwise. The C25 stateless-projection analogy offered for re-pinning is an admitted analogical stretch (C25's scope is visual surfaces). Whether family parity is implemented by the campaign annotators persisting families or by the sugar layer is an implementation-spec detail.Fork 6 — the generated campaign document and its generated process document are auto-registered into the content-addressed stores on every sugar run. Converged 4:0: the #188 fork-C resolution already names auto-registration as the designated tooling pattern; a computed-but-unstored content id would make the run record's reproduction pointer dangle (the exact thing C18 forbids); an opt-in flag reinstates intent-evaporation as the forgotten-flag default. With this, every ad-hoc verb invocation becomes durable, diffable, reproducible intent — the #188 diagnosis's cure applied to the verbs themselves.
Fork 7 — the sweep re-cut ships first; the remaining verbs follow by coverage distance (generalize, walkforward, mc's R-bootstrap path). Structural ground: the v2 process shape makes
std::sweepthe mandatory first stage of every generated document, so the sweep sugar's translator (axes mapping, window mapping, blueprint registration, presentation adapter, auto-registration) is the shared skeleton all four dissolutions ride on. Beyond that structural constraint, ordering is sequencing judgement, not design.Surfaced concerns and their dispositions
--tracevs. the closed tap vocabulary (composability lens): resolved by decision 2's scope — the dissolved real-data blueprint branches carry no live per-member trace surface today (run_blueprint_sweep's persist slot is a reserved no-op, tracked as #168; the blueprint walkforward branch rejects--trace; mc's trace belongs to the synthetic branch). Whether the campaign path ever grows per-member trace persistence remains #168's question, out of this milestone.Status
All forks of the dissolution design are decided; the design basis is this issue's body (coverage map) plus the decisions above. The first cycle — the sweep re-cut: translator skeleton, optional-selection vocabulary change, auto-registration, presentation adapter, inline-path deletion for the dissolved branch — is settled and ready for spec production.
Spec-level derived decisions (sweep dissolution, cycle 1)
Recorded per the decision-log discipline; each is a derived orchestrator decision with its rationale, refining the fork-triage comment on this issue (issues/210, comment "Fork triage: verb dissolution", 2026-07-04). Veto by replying here.
Option<SweepSelection>). Derived: a half-populated triple is unrepresentable by construction — the same make-invalid-unrepresentable move the axis-kind decision took (#189); wire form of existing documents is unchanged, no stored content id moves.seed: 0. Derived: no stage of a selection-free single-sweep pipeline consumes the seed; a fixed value keeps generated bytes deterministic so identical invocations dedupe onto identical content ids.--namemaps to the campaign document name; family names follow the campaign convention ({campaign8}-…-w0-s0). Derived: the pinned member stdout lines never carried the family name, so byte parity is unaffected; the user's name handle lands on the durable artifact (campaign document + run record) rather than the derived family row.selection_reportand finalcampaign_runstdout lines for the sugar invocation; the run record is still appended — presentation changes, the record does not.Spec: docs/specs/sweep-dissolution.md (working file, in-tree while the cycle is live).
Derived decision: the instrument manifest stamp survives the sweep re-cut
The spec's grounding check (2026-07-04) surfaced a real divergence the coverage map had missed: the campaign member runner stamps
manifest.instrument = Some(<SYM>)into every member report (crates/aura-cli/src/campaign_run.rs:256) while the inlineaura sweep --realpath leaves the fieldNone(serde omits it) — so the dissolved form's member stdout lines gain an additive"instrument":"<SYM>"key and are not byte-identical to the pre-change verb.Decision (derived): keep the stamp; correct the spec's byte-identity claim to "identical modulo the additive instrument field". Rationale: the stamp is provenance the substrate deliberately records (the audit direction C18 pulls in), the verbs' existing integration tests nowhere pin the field's absence on the real-data blueprint branch, and stripping it in sugar mode would fork the member runner's behaviour for presentation cosmetics — the same dual-path drift the dissolution exists to remove. The alternative (a sugar-mode flag suppressing the stamp) was rejected on that ground. The characterization test takes an instrument-field-tolerant form, with the stamp's presence asserted separately after the re-cut.
This refines decision 5 of the fork-triage comment on this issue ("full behaviour parity"): parity of the pinned contract, plus one named, recorded, additive provenance field. Veto by replying here.
Spec auto-signed (grounding-check PASS, no human signature)
The cycle-1 spec (docs/specs/sweep-dissolution.md — the real-data blueprint sweep re-cut as sugar over a generated, auto-registered campaign document; optional selection group in the sweep stage vocabulary; terminal-only preflight rule; inline-path deletion) was signed autonomously. The signature is the grounding-check PASS of 2026-07-04: every load-bearing current-behaviour assumption is ratified by a named, currently-green test. Two earlier grounding dispatches BLOCKed on the instrument-manifest-stamp claim — first as factually wrong (the spec claimed byte-identical member lines; corrected to identity modulo the additive stamp, see the derived-decision comment on this issue), then as code-true-but-unpinned; the second BLOCK was closed by two green characterization pins (commit
8235cee) before any re-cut work, and the third dispatch PASSed with the gated pins confirmed executing against the local real archive. No human signed; the auto-sign notify was sent with veto standing.Spec reconciliation note: after the auto-sign, plan recon surfaced three spec/tree mismatches — the preflight fault type (the variant lands on ExecFault; no PreflightFault type exists), the sugar presentation seam (a mode parameter on the campaign-run entry, not a Presentation variant), and the Testing-1 characterization description (the shipped pin test asserts instrument-absence today; the re-cut task flips exactly that one assertion to stamp-equality). The spec was corrected accordingly — no behaviour claim changed — and a fresh grounding check PASSed on the corrected bytes (all ten load-bearing assumptions ratified by named green tests, gated pins confirmed executing against the local archive). The signature stands on that fresh PASS.
Cycle 1 (sweep dissolution) shipped: commits
c37abb2(optional selection group + terminal-only preflight + selection-free executor arm), fd0b21d/verb_sugar translator,b7aaa0b(dispatch re-cut). The real-data blueprint sweep now runs as sugar over a generated, auto-registered campaign document through the one executor; the inline real-data arm is gone; synthetic/built-in branches and --list-axes untouched per decision 2. Three implementation-level findings recorded for later verb cycles: (1) generated documents speak the RAW campaign-axis namespace — the dispatch strips the CLI probe's wrapper segment (#203 convention); (2) the probed archive window is epoch-ns at the seam and converts once through aura-ingest's epoch_ns_to_unix_ms (now pub) into the document's ms currency; (3) family ids carry a per-execution run-suffix by design, so cross-invocation determinism is pinned at the member-report level, not stdout bytes. Characterization pin survived the re-cut with exactly the one sanctioned assertion flip; suite 1055/0; gated real-data paths executed locally. Remaining in the milestone: generalize, walkforward, mc (R-bootstrap path) per decision 7.Cycle 0110 closed end-to-end. Audit: drift-clean after two fixes (docs aligned with the optional selection group; the #203 wrapped/raw axis convention single-sourced as a cross-documented inverse pair in campaign_run — ratified semantics tightening: the old loose suffix-match's undocumented sub-segment shorthands now refuse loudly instead of matching). Fieldtest (fieldtests/cycle-0110-sweep-dissolution/, spec in docs/specs/): GREEN-core, 0 bugs, all four cycle axes working. Findings disposition: 5 fixed same-session — axis-name preflight echoing the typed name with a --list-axes pointer; validate-before-register in the sugar (refusals leave no store litter); house-style blueprint-slot prose shared by sweep/walkforward/mc incl. an op-script build-first hint; authoring-guide §1 bind inconsistency + the bind-closes-the-axis rule; the sweep-axis wrapped-namespace documentation. 1 deferred as #211 (in-project intrinsic validate hits the cdylib wall — pre-existing eager-load coupling, resolves in the role-homes design pass, not per-verb). Suite 1060/0. Next per decision 7: the generalize re-cut.
Fork triage: risk-harness parameters across the campaign seam (grounded stance review, 2026-07-04)
Cycle-2 recon surfaced the milestone's central remaining design question, and a five-lens grounded stance review (minimalist / composability / maintainer / skeptic / off-ledger wildcard) ran on it. One fork is decided on cited ground; the load-bearing one is a genuine direction fork and awaits a decision here.
The finding
The verbs' r-sma machinery treats fast/slow/stop-length/stop-k as ONE search space (
r_sma_sweep_familybuilds stop-open and grids all four). The campaign member runner bakes the stop to fixed defaults (3 / 2.0) inside non-serialized scaffolding; stored strategy blueprints are the bias leg only, and the referential tier requires every open param to be covered by an axis ("there is no default"). Consequences: the campaign path cannot vary stop parameters at all; a non-default-stop candidate is not reproducible through it (generalize's own E2E pin uses --stop-length 14); and — the audit gap — the executed stop regime appears in no content-addressed artifact, so "same candidate, different stop" would leave identical topology hashes (the C18 concern). This blocks the behaviour-preserving dissolution of generalize, walkforward, and mc's R-path equally.Decided (cited ground)
Cycle sequencing: the stop question is resolved first, as its own design pass; no further verb is dissolved against the unresolved substrate. Ground: the ratified per-verb gate ("a verb is re-cut only when the campaign substrate reproduces its behaviour" — fork A, 2026-07-03) is not satisfied by reproducing only default-stop invocations; a backend split keyed on flag VALUES (default stop → campaign path, other → inline) would produce silently different registry artifacts for look-alike invocations — the dual-path drift this milestone exists to remove — and was rejected by three lenses on that gate wording. "Dissolve a stop-free branch first" is foreclosed by fact: all three remaining verbs share the same four knobs. (The one lens preferring the partial split undermined it in its own surfaced concern: the non-default-stop E2E pin would become a permanent carve-out, not a dissolution.)
Open — the direction fork (user's call)
Where do risk-harness parameters (stop-length, stop-k) live in the artifact model? The lenses split 2:2 with citable ground on both sides, plus two off-menu directions; no source settles it:
Cross-cutting note (maintainer): whichever direction wins, the resolved stop parameters must land in the per-run manifests / addressable record — resolving where they live without stamping what ran would repeat the instrument-stamp near-miss the cycle-1 grounding check caught.
Status
Milestone paused at this fork after cycle 1 (sweep dissolved, shipped, closed end-to-end). The direction decision above gates cycles 2-4 (generalize, walkforward, mc's R-path). Decision requested; the fork's options and grounds are reproduced in full above so the choice can be made from this thread alone.
Direction decided: risk regime as a structural campaign axis
The fork "where do risk-harness parameters (stop-length, stop-k) live in the artifact model" — options and grounds in the fork-triage comment — is resolved by user decision, 2026-07-06: deferral is ruled out (the question is decided now, not parked), and the structural axis wins over ordinary param axes; the user's stated ground, translated: the structural axis is clearly the more coherent shape.
The decided shape:
risksection enters the campaign document as a peer ofdata.instruments/data.windows: a finite list of risk regimes in a closed vocabulary, initially{ stop_length, stop_k }(the vol-stop's two scalars). The realization matrix gains one structural dimension: strategy x instrument x window x regime.param_space(): strategy axes keep binding only the referenced blueprint's real open knobs, and the campaign runner feeds the regime to the member scaffolding the way it feeds instrument and window.Grounds, one sentence each:
Ratified consequences:
aura walkforward/aura mcaccept--stop-length <csv>/--stop-k <csv>and rank per fold across the joint grid including stops (usage strings at crates/aura-cli/src/main.rs:4651 and :4730); the campaign path will NOT reproduce that: cross-stop argmax compares candidates with mixed R units and is retired as a methodology. The equivalent research question is expressed as a regime list — compared at presentation level the way instruments are, never selected or deflated across. This is a design decision for the ledger at cycle close, not silent drift; the per-verb dissolution gate ("the substrate reproduces the verb's behaviour") is amended accordingly for these flags.risksection runs the current baked constants (stop_length 3, k 2.0 — crates/aura-cli/src/main.rs:2491/2495) as its single regime — the optional-group pattern already established forstd::sweep's selection triple in cycle 0110, not an open-param default.Status: direction settled — ready for spec production. This design pass precedes the remaining dissolutions (order per the sequencing decision in the verb-dissolution triage: generalize, then walkforward, then mc's R-path).
Derived design decisions (specify): risk-regime structural axis
Spec:
risk-regime-axis. The parent fork — risk parameters live as a structural campaign axis — is the user decision recorded in the direction comment. The sub-forks below are derived by the orchestrator (rationale, not user provenance); each was decidable from the code, the ledger, or existing-design consistency, so none bounced.Fork: document placement → a new top-level
CampaignDoc.riskfield (peer ofdata,strategies,process), NOT a third member insideDataSection.Derived:
DataSectionis defined as "structural data axes: which instruments over which windows" (crates/aura-research/src/lib.rs:438-444) — a data selection. A risk regime is a risk-execution choice, which domain invariant 7 keeps decoupled from both the strategy signal and the data. Housing it inDataSectionwould conflate the risk-execution axis with the data axes; its own top-level section keeps the three concerns (data / signal / risk-execution) as visibly separate in the document as the architecture keeps them.Fork: regime vocabulary shape → a new serializable
RiskRegimetype inaura-research, a tagged closed vocabulary whose sole implemented variant is the vol-stop{ length, k }, with the fixed-stop rule admitted as a future additive variant.Derived: it mirrors the existing shipped
StopRule { Fixed, Vol }(crates/aura-composites/src/lib.rs:73-77), which the ledger (C10, INDEX.md:928-930) already frames as the structural stop axis (C20) — so a tagged form models a proven two-variant axis, not speculative vocabulary (the fixed-stop rule exists and runs as a composite today, it is merely not campaign-reachable). Stored campaign documents are content-addressed, so the wire shape is a durable contract: a tagged form makes adding the fixed-stop rule purely additive (no content-id churn on existing vol regimes), where a flat{ stop_length, stop_k }bakes "vol is the only stop" into the wire and forces a breaking restructure later.aura-researchstays dependency-pure (aura-core + serde + sha2); the CLI member-runner mapsRiskRegime→StopRuleat the runtime boundary, exactly as it maps campaign axes → runtime param-spaces.Fork: which matrix dimension the regime behaves like → a kept-separate structural axis (peer of
window), NOT an aggregated-over axis (peer ofinstrument). The campaign-scope generalize unit becomes per(strategy, window, regime); the nominee key(strategy_ordinal, window_ordinal)(crates/aura-campaign/src/exec.rs:124) gains the regime ordinal.Derived: generalize aggregates across instruments because a candidate must survive all instruments (worst-case R floor, exec.rs:157-195). Regimes are the opposite: they are alternatives compared to find the most robust one, never a set the candidate must all survive (that would demand a strategy work at every stop — neither necessary nor wanted). So the regime is compared at presentation, kept separate in the keying like
window, and each regime independently carries its own generalize-across-instruments result. (This is exactly what makes cross-regime argmax structurally unavailable — the parity break the direction comment ratified.)Fork: manifest stamping of the resolved regime → each member manifest records the resolved stop params (
stop_length/stop_kpairs, the single-run manifest shape at crates/aura-cli/src/main.rs:3608-3609), including the default regime.Derived: ledger law, not really a fork — C10/C18 state "the stop defines 1R … each swept member's manifest records the fixed R-defining params beside the floated knobs (reproducible from its own manifest)" (INDEX.md:949-952). Campaign members today stamp no stop (the baked constants at main.rs:2694 are invisible); this closes that C18 gap. It stamps even the default regime, since reproducibility-from-manifest is unconditional — the instrument-stamp precedent (campaign_run.rs:282) applied to the stop.
Fork: absent-
riskdefault and parity →#[serde(default, skip_serializing_if = "…")]so an absentriskserializes to zero bytes; absent or empty means one implicit regime equal to the current baked constants (stop length 3, k 2.0, main.rs:2491/2495). The sweep sugar (verb_sugar.rs:57-100) emits norisk, so its generated document is byte-unchanged.Derived: the cycle-0110 optional-group precedent (the
SweepSelectionflattened optional). It keeps every stored campaign document byte-stable (content-id parity) and valid without migration, and keepsaura sweepsugar parity by construction — the same discipline that kept the selection triple absent-serializing.Status: design settled — ready for spec production. No open load-bearing fork remains on this design pass.
Spec auto-signed (grounding-check PASS, no human signature)
The risk-regime-axis design pass — enabling a campaign document to enumerate protective-stop regimes as a structural axis and run every (strategy, instrument, window, regime) cell, with each member's resolved stop stamped into its manifest — reached a spec whose load-bearing assumptions about current behaviour were all ratified by currently-green tests, verified by an independent fresh-context grounding-check. Under the autonomous run that PASS is the spec's signature; no human signed it.
One assumption was code-true but unpinned on the first pass: a campaign member manifest records no stop today (the absence the additive manifest stamp rests on). The older member-line pin locates bindings by
findand never asserted that absence. It was closed by a green characterization pin (sweep_real_blueprint_member_manifest_carries_no_stop_param_today, commit455916d) that runs a real dissolved sweep and asserts no manifest param names a stop; the resolved-regime stamp will flip it to require the stop key — the ratified additive contract extension (like the instrument stamp). The re-dispatched grounding-check then passed.Design settled and signed — ready for plan production. A user reply vetoes the signature; the spec is a working artifact and any downstream code is forward-correctable.
Derived design decisions (specify): generalize dissolution
Spec:
generalize-dissolution. The dissolution direction and its gates are the milestone decisions in the fork triage (Fork 5 full byte-parity via a presentation adapter over campaign records + registry-family parity; Fork 6 auto-registration; Fork 7 generalize rides the sweep translator skeleton; old-path deletion in the same cycle). The sub-forks below are derived by the orchestrator from those decisions, the sweep-dissolution precedent, and the code — none is a pure-preference fork.Fork: verb surface vs. execution → the
aura generalizecommand surface and its argument-layer refusals stay byte-identical (a thin front-end, today'sgeneralize_args_from); only the inline execution dissolves.dispatch_generalizevalidates args (refusals unchanged), then calls a newrun_generalize_sugarinstead ofrun_generalize; the inline execution path (run_generalize) is removed this cycle (old-path deletion), andgeneralize_jsonsurvives — reused by the presentation adapter to format the aggregate line.Derived: Fork 7 + old-path-deletion + the sweep precedent (
aura sweepthe command stayed; its execution dissolved). Keeping the arg front-end preserves the seven refusal pins (cli_run.rs:3547-3735) byte-identically; dissolving only execution is the ratified "reproduces its behaviour" gate.Fork: the generated document shape → a
translate_generalizesibling oftranslate_sweepbuilds a campaign document withdata.instruments = <symbols>, a single fixed candidate as single-value raw axes ({"fast.length":{"kind":"I64","values":[N]}, "slow.length":{...[N]}}), the stop as a single risk regime (risk: [{"vol":{"length":<stop-length>,"k":<stop-k>}}]), and a selection-bearing process[{std::sweep, metric, argmax}, {std::generalize, metric}].Derived: this is exactly the
GENERALIZE_PROCESS_DOCfixture shape (research_docs.rs:911); the just-shipped regime axis carries the stop (a single--stop-length/--stop-k= one regime — confirmed by theCliMemberRunnerregime→StopRule map); a fixed candidate is a one-element axisvalueslist; and generalize needs a nominee, so the sweep stage must be selection-bearing (the sweep precedent's selection-free terminal form does NOT carry — the one place generalize genuinely differs from the sweep translator).Fork: stdout + family parity (the presentation adapter) →
run_generalize_sugarruns the campaign, reads the recordedCampaignGeneralizationfrom the run outcome, extracts itsGeneralization, and prints today's exact{"generalize":{...}}line via the survivinggeneralize_json, then persists theFamilyKind::CrossInstrumentfamily from the sugar layer and prints its{"family_id":"generalize-<run>"}line.Derived: Fork 5 (byte-identical stdout derived from campaign records; families are data, persisted for
aura runs familyconsumers). The record already contains theGeneralization(CampaignGeneralization.generalization), so reusinggeneralize_jsongives byte-identity for free; persisting the family from the sugar layer (Fork 5's sanctioned "by the sugar layer") keeps the campaign executor unchanged and avoids a ripple through the existing campaign-generalize tests — the less-invasive of the two Fork-5 options.Fork: metric + refusals routing → the generalize metric defaults to
expectancy_r(today'sgeneralize_args_fromdefault) and is placed on BOTH the sweep and generalize stages of the generated process. The argument-layer refusals (duplicate instrument, non-r-sma strategy, missing knob, multi-value flag) stay in the keptgeneralize_args_fromfront-end (byte-identical); the campaign preflight's own generalize guards (≥2 instruments, R-metric) are a redundant second net behind them, never the surfaced message.Derived: keeping the front-end validation is what preserves the refusal pins; the campaign preflight guards already exist and agree in intent, so they are a defense-in-depth backstop, not a behaviour change.
Status: design settled — ready for spec production. No open load-bearing fork; the one genuine novelty vs. the sweep translator (a selection-bearing pipeline + an aggregate-line presentation adapter) is derived above, not a user-preference choice.
Generalize-dissolution spec — grounding BLOCK (awaiting direction)
The
generalize-dissolutionspec (design decisions in comment 2857) reached the grounding-check with one genuinely unratified load-bearing assumption; under an autonomous run this is the objective denial that routes to a human decision.The gap: the byte-identical-output claim rests on the resolved stop reaching the graph identically through two different mechanisms. Today
aura generalizebinds the stop as a grid axis (r_sma_sweep_familybuildsstop_open=trueand bindsstop_length/stop_kas axes). The dissolved path binds the stop through the risk-regime seam shipped by the risk-regime cycle (RiskRegime::Vol → StopRule::Vol, stamped byrun_blueprint_member). That the two stop-application mechanisms produce identical R is pinned by no test. The mechanism sub-parts (the[std::sweep(argmax), std::generalize]vocabulary, theCampaignGeneralization→Generalization→generalize_jsonshape, argmax over a 1-point grid,FamilyKind::CrossInstrument, the regime→stop stamp) are each individually pinned by non-gated tests; the equivalence of the two stop mechanisms for the R values is the one unpinned link.Reasoning supports parity (both build a
vol_stopnode with the samelength/k, and the ledger records that a bound vol-stop and an open-then-bound vol-stop share identical downstream wiring), but reasoning is not a green test — and the byte-identity is the whole "reproduces its behaviour" gate (Fork 5).(A secondary grounding note — that the real-data e2e "skip" on this host — is a false detection: the archive is present at
/mnt/tickdata/Pepperstoneand those tests run green here. But even running, none of them compares the axis-bound-stop R against the regime-bound-stop R, so the equivalence stays unpinned.)Options for direction:
aura generalizeoutput on real data and assert the dissolved path reproduces its R byte-for-byte (a characterization pin, the risk-regime-cycle pattern). If it matches → proceed; if not → the byte-identity claim is false and the approach needs rework.The spec sits on disk (uncommitted working file); no code was written. The substrate cycle it depends on is shipped and closed. Status: blocked at the sign gate — the parity-verification call is the user's.
Design decision (generalize dissolution): resolving the grounding BLOCK by characterization pin
The generalize-dissolution grounding-check blocked on one unpinned link:
the current
aura generalizebinds the protective stop as a grid axis(
r_sma_sweep_familybuildsstop_open=true,stop_length/stop_kasaxes), whereas the dissolved path binds it through the risk-regime seam
(
RiskRegime::Vol -> StopRule::Vol, stamped byrun_blueprint_member). Notest pinned that the two stop-application mechanisms produce identical R.
Decision (derived): resolve the block by the same characterization-pin
pattern the risk-regime cycle used for its own grounding block — not by
overriding it, and not by bouncing. Concretely:
(
generalize_real_e2e_pins_the_exact_current_grade,cli_run.rs, commitf3f32b8) that captures the currentaura generalize --real GER40,USDJPYoutput byte-for-byte (GER40
0.01056371324510624, USDJPY0.005795903617609842,worst_case0.005795903617609842,sign_agreement2). Green today against the inline axis-bound-stop path; itis the byte-identity anchor. The existing generalize e2e asserts shape
only, so it could not have caught a divergence.
gate (the new dissolution e2e asserts the sugar path reproduces exactly
those pinned bytes), not an unpinned assumption about current behaviour.
A stop-mechanism divergence then fails loudly at implement — the right
place to discover it — rather than shipping silently.
Basis: derived. The block flagged an unpinned current-behaviour
assumption (that current generalize produces stable, characterizable R
output); a characterization pin closes exactly that, which is the sound
response to a grounding gap (close it), not the escalation response
(override or discard). The future OLD==NEW parity is a deliverable of this
cycle, verified by its own acceptance e2e against the anchored bytes — not
a current-behaviour fact the grounding-check should gate on. This mirrors
the risk-regime cycle's ratified BLOCK-1 resolution.
Spec auto-signed (grounding-check PASS, no human signature)
The generalize-dissolution spec cleared the grounding-check on re-dispatch,
after the byte-identity anchor closed the prior block
(issues/210#issuecomment-2859). Under the autonomous run, the grounding-check
PASS is the sign-off (specify Step 6): an independent fresh-context agent
verified every load-bearing current-behaviour assumption against a
currently-green test. The one previously-unpinned link — that the current
grid-axis stop and the dissolved risk-regime seam yield identical R — is now
anchored by
generalize_real_e2e_pins_the_exact_current_grade, which firesnon-vacuously (its GER40/USDJPY archive is present) and would go red on any
stop-mechanism divergence after dissolution. New symbols
(
translate_generalize,run_generalize_sugar,run_campaign_returning) arecommitments of this cycle, correctly not treated as current-behaviour
assumptions.
Status: design settled and grounded — ready for plan production.
Derived design decisions (planner): generalize dissolution
Four implementation forks derived from the code + the sweep precedent while
planning the generalize dissolution. None is a user preference; each has a
groundable basis. Q4 is user-visible and flagged as such.
Q1 — built-in candidate blueprint source.
aura generalizehas no userblueprint file, so
dispatch_generalizesynthesizes the bare open SMA signalsma_signal(None, None)as the stored strategy blueprint (exactly what thesweep-dissolution fixture
sma_signal_open.jsonstores); fast/slow becomesingle-value campaign axes and the stop rides the risk regime.
Basis: the resulting downstream graph is
wrap_r(sma_signal(3,12), StopRule::Vol{14,2.0})— identical to today'sr_sma_graph(stop_open=true)path with 14/2.0 axes — so the exact-grade byte anchor holds.
Q2 — the run-outcome refactor shape.
run_campaign_returningreturns aCampaignRunbundle (outcome+ thecampaign/strategies/serverthepresentation tail needs), not a bare
CampaignOutcome;present_campaignconsumes the bundle, and
run_campaign_by_idbecomes their composition.Basis: a single source of truth for the resolve→execute body beats
duplicating ~78 lines that would drift; the tail is preserved verbatim so the
campaign e2e stays green.
Q3 — window resolution. Explicit
--from/--towhen both present(byte-identical, anchor-verified); otherwise the first symbol's
full_windowas the single shared campaign window.
Basis: the campaign model carries one
Windowfor all instruments; thetested path always passes an explicit window, and the absent case is an
untested edge with a sensible shared-window reading.
Q4 — the persisted family set changes (USER-VISIBLE). The campaign
executor persists one
FamilyKind::Sweepfamily per instrument cell(unconditional), so the dissolved
aura generalize GER40,USDJPYpersists2 Sweep families + 1 CrossInstrument family, where the inline path
persisted only the single CrossInstrument family (it suppressed
per-instrument families via
r_sma_sweep_family(None, …)). Option A: acceptthe campaign-path families (no change to the shared executor). Option B:
change the executor to suppress the intermediate 1-point Sweep families
(touches shared code + the sweep path). Chosen: Option A.
Basis: routing through the campaign path is the milestone thesis ("every
ad-hoc invocation becomes durable, diffable, reproducible intent"), and the
per-instrument Sweep families are the substance generalize's cross-instrument
grade is built on — now durably auditable rather than discarded.
aura generalize's own stdout stays byte-identical (Fork 5); only the auxiliaryaura runs familiesview gains the two Sweep lines. Consequence: theexisting
generalize_persists_a_discoverable_cross_instrument_familycount==1assertion (an inline-path artifact) relaxes to "exactly one CrossInstrument
family", which is the honest reflection of the campaign-path family set.
Cycle close: generalize dissolution — drift-clean (local, unpushed)
The second verb dissolution shipped and closed drift-clean.
aura generalize --strategy r-sma --real SYM1,SYM2 --fast --slow --stop-length --stop-know runsas thin sugar over the one campaign executor; the inline
run_generalizeisdeleted.
Commits over
d96af7e: anchorf3f32b8, spec/planfd9a732, T19ddea84(factor
run_campaign_returning+present_campaign+ theCampaignRunbundleout of
run_campaign_by_id), T2ed8b179(translate_generalize— theselection-bearing
[std::sweep(argmax), std::generalize]process, single-regimerisk), T3256ec73(run_generalize_sugar+dispatch_generalizesynthesizesthe bare
sma_signalblueprint; inline path deleted), T4f5bb38e(dissolution-proof e2e), audit
3a03617.The grounding block (whether the former grid-axis stop and the new risk-regime
seam yield identical R) was closed by the risk-regime characterization-pin
pattern: a committed exact-grade anchor
(
generalize_real_e2e_pins_the_exact_current_grade, real GER40/USDJPY floats)that stays green through the path shift IS the equivalence proof. Byte-parity
verified end-to-end; full workspace suite green; clippy
-D warningsclean.Derived decisions Q1-Q4 are recorded above. Drift resolution (architect: no
ledger drift): the no-window shared-window semantics (Q3) ratified as the
campaign model's natural expression, its reference-instrument reconsideration
filed as #213; the dissolved-verb translator arg-bundling debt filed as #214.
Status: generalize dissolution complete. The milestone's remaining dissolutions
are walkforward then mc's R-path (decision 7); the reusable seam
(
run_campaign_returning+validate_before_register) is in place. Push,formal milestone close, and the milestone-wide fieldtest remain user-reserved.
Walkforward dissolution — recon finding + a scope fork for direction
With generalize closed, walkforward is next (decision 7). Recon shows it is
not a clean sugar-wrap like generalize, and it hits a genuine scope
collision with decision 2 — recording it here before any spec is written.
The scope collision (the fork)
Decision 2 keeps the built-in demo-strategy and synthetic-data branches
verb-wired until #159; the dissolution covers real-data blueprint branches
only. But walkforward has no such branch:
--realpath is the built-in--strategybranch(
run_walkforward, main.rs:4713) — a built-in strategy, not a blueprint;DataSource::Synthetic(
run_blueprint_walkforward, main.rs:4679) — synthetic, not real.So the exact shape sweep + generalize dissolved (a real-data blueprint
invocation) does not exist for walkforward. What, if anything, is in scope to
dissolve now is itself the open question, and it turns on the milestone's own
sequencing — not something derivable from the code.
Options:
R-bootstrap path next instead. Honors decision 2 strictly.
--realbranch — dissolvethat branch now despite its built-in-strategy status (a scope expansion).
walkforward against a real-data-blueprint shape.
Even once scoped, this is not a thin sugar cycle
The campaign
std::walk_forwardstage covers the IS-sweep → OOS-run → deflationcore (and is more general — it also does Anchored), but three summary fields the
verb prints have no campaign equivalent, and one has no recorded data at all:
stitched_total_pips— the campaign runner discards the raw OOS pip curve(
oos_equity: vec![], exec.rs:794); it is neither computed nor recordable.Needs executor work to retain the curve or record the stitched total.
oos_r— on the campaign path only a followingstd::monte_carlostage produces it (
StageBootstrap::PooledOos, exec.rs:406); the generatedprocess would have to append that stage and reconstruct the line.
--select plateau:*— the decision-4 preflight relaxation (permitplateau in the wf stage when no gate precedes) is not yet shipped; the
campaign stage refuses plateau today (it re-sweeps post-gate survivors only,
so no lattice).
param_stabilityis reconstructable from the family reports; the window modelis settled (roller sizes from the process doc, span from the one campaign
window). So the genuine work is: a scope decision (above), plus enabling
executor/vocabulary changes (retain the OOS curve; ship the decision-4 plateau
relaxation), plus the usual presentation adapter.
Status: paused on the scope fork. generalize (the second dissolution) is closed
drift-clean; this is the direction call needed before walkforward can proceed.
Design reconciliation: decision-2 scope reading (Reading A)
The walkforward recon (2026-07-06) paused on a scope fork: decision 2 fences the
built-in demo-strategy and synthetic-data branches until #159, and walkforward's
only
--realpath is a built-in--strategybranch — so under a literal readingnothing of walkforward would be in scope to dissolve now.
Resolved (user decision, 2026-07-06): decision 2 governs the verb surfaces,
not real-archive executions. The five built-in demo strategies and the
synthetic data sources stay verb-wired — their vocabulary is not carried into
the campaign artifact model — until #159 retires them; but a real-archive run of
a built-in strategy MAY route through the one campaign executor now, exactly as
generalize already does. Restated: the surface stays fenced; the execution
dissolves.
Basis (why the literal reading cannot be the intended one). The literal fence
makes decision 7 vacuous. Decision 7 orders the remaining dissolutions as
generalize → walkforward → mc, yet none of the three has a real-data
blueprint-file invocation — each is a built-in
--strategyverb. Under theliteral fence the entire remaining sequence would block on #159, so decision 7's
own ordering could never execute. Decision 7 only coheres if real-archive
executions of built-in strategies are in scope; that is Reading A.
Consequences.
under this reading: it synthesizes a bare
sma_signalblueprint in-process andruns a real archive through the campaign path — an execution, not a new
synthetic vocabulary.
--realbranch dissolves the same way;the genuine remaining work is executor/presentation, not scope — retain the OOS
pip curve the campaign runner currently discards (
oos_equity: vec![],exec.rs:794) sostitched_total_pipsis recordable, reconstruct pooledoos_r(a followingstd::monte_carlostage on the campaign path), and shipthe decision-4 plateau preflight relaxation (decided, not yet implemented).
themselves; it is not a prerequisite for these executions.
Derived decisions — walkforward dissolution (specify, cycle 3 of #210)
Recorded before the spec is written (Step 1.5). All three are derived
orchestrator decisions (rationale, not user provenance), open to veto.
Finding: the dissolution is pure sugar (no executor change) for the argmax path
The recon feared two executor gaps; grounding disproves both. The campaign
std::walk_forwardstage already persists the per-window OOS reports as aWalkForwardfamily, andCellOutcome.familiessurfaces them in-memory to thesugar with
trade_rsintact. So the two verb summary fields reconstruct as puresugar from those reports:
stitched_total_pips= Σ (per-windowmetrics.total_pips), summedleft-to-right in window order. The engine
stitch(walkforward.rs) setsstitched_oos_equity.last() = Σ_i seg_i.last, and each window's OOS segment'slast cumulative value equals that window's
total_pips. Verified byte-exacton the anchor invocation: Σ =
-10398606.666650848== the summary'sstitched_total_pips. So the discardedoos_equity: vec isNOT needed — no executor change.
oos_r=r_metrics_from_rs(pool of the per-windowmetrics.r.trade_rs, window order). The reports reaching the sugar carrytrade_rsin-memory. Verified: Σ per-windown_trades= 20681 == the summary'spooled
n_trades. Nostd::monte_carlostage needs appending.So walkforward dissolves like generalize (register a generated campaign, run the
one executor, reprint the verb's summary from the recorded outcome), NOT with the
executor surgery the recon estimated.
Fork A — the stop maps to the risk regime; fast/slow are the IS-refit grid
The inline walkforward sweeps a 4-axis grid (fast × slow × stop_length × stop_k)
as the per-window IS-refit. The shipped risk-regime model makes the stop a
STRUCTURAL regime — compared, never argmax-swept (the stop defines 1R, so
sweeping it argmaxes across mixed R-units, the anti-pattern the model forbids).
Decision: the dissolved walkforward binds fast/slow as the IS-refit sweep
grid (multi-value campaign strategy axes — the anchor uses
--fast 3,5 --slow 12,20) and the stop as a single risk regime (RiskRegime::Vol{stop_length, stop_k}, mapped back toStopRule::Volby the member runner).--stop-length/--stop-kare therefore single-value (like generalize's knob restriction);a multi-value stop is refused with a message. For the anchor and the common case
this is byte-identical to the inline path.
Basis (derived): consistency with the just-shipped risk-regime model (the
canonical stop carrier); a signal grid is swept, the stop is a regime. Not an
effort argument — the substantive reason is R-unit integrity.
Fork B — this cycle dissolves the argmax path;
--select plateauis carved outThe committed equivalence anchor exercises
--select argmax(the default). Thecampaign
std::walk_forwardstage refuses--select plateau:*today (it selectsits per-window IS winner via
optimize_deflated/argmax only); the decision-4preflight relaxation that would permit plateau when no gate precedes is not yet
implemented.
Decision: this cycle dissolves the argmax path only (pure sugar,
anchor-gated). The dissolved
dispatch_walkforwardexplicitly refuses--select plateau:*(exit 2) with a forward pointer, rather than routing itinto a campaign stage that cannot serve it. The decision-4 wf-stage plateau
relaxation is filed as the immediate follow-up (its own tight cycle, gated by its
own plateau characterization anchor), picked up next.
Basis (derived): the anchor — the equivalence gate — proves the argmax path only;
the plateau relaxation is a separable executor change (branch the wf stage's IS
winner selection on
select, thread the survivor lattice) with its OWNcorrectness gate. Folding it in leaves half the cycle un-anchor-gated. An
explicit, documented, exit-2 refusal is honest (no silent breakage) and cheap to
lift when the relaxation lands next. The regression window is local/unpushed and
closes in the very next cycle.
Blueprint-file walkforward stays verb-wired (decision 2, Reading A)
Only the built-in
--strategy r-sma --realbranch dissolves. Therun_blueprint_walkforwardbranch is hardcodedDataSource::Synthetic—synthetic, fenced until #159 — and stays verb-wired, exactly as decision 2
(Reading A) governs.
Spec auto-signed (grounding-check PASS) — walkforward dissolution
The walkforward-dissolution spec was signed autonomously under
/boss: thesignature is the Step-5
grounding-checkPASS (an independent fresh-contextagent's verdict against currently-green tests), not orchestrator confidence. No
human signed it; this records that for after-the-fact audit and veto.
Capability signed:
aura walkforward --strategy r-sma --real …routes through theone campaign path as thin sugar (a generated, content-addressed campaign
document), reproducing the summary line byte-for-byte; the inline
run_walkforwardis deleted. Argmax path only —
--select plateau:*refused with a pointer to#215; multi-value stop refused (Fork A); blueprint-file/synthetic branches stay
verb-wired (decision 2 / Reading A).
Grounding ratified all ten load-bearing current-behaviour assumptions against
named green tests (the campaign walk_forward family +
CellOutcome.familiesin-memory
trade_rsviaexecute_mc_after_wf_pools_the_oos_series; one familyper regime; the generalize seam; the committed equivalence anchor). The two
reconstruction identities (
stitched_total_pips == Σ total_pips; pooledoos_rfrom the family reports) are gated by the committed anchor
96dc783.Status: design settled, spec produced and signed — ready for plan production.
Fork C —
run_walkforwardis retained, not deleted (the built-in arm splits)Plan recon surfaced a contradiction the spec initially carried: it said "delete
the inline
run_walkforward" (mirroring generalize) while also keeping thesynthetic path verb-wired (Reading A). But unlike generalize — which requires
--realand has no synthetic path —dispatch_walkforward's built-in arm todayroutes BOTH the synthetic built-in walkforward (
aura walkforwardwithout--real) AND the real built-in walkforward through the one inlinerun_walkforwardhandler. Deleting it would strand the synthetic invocation withno handler.
Decision (derived): the built-in arm splits on the dissolution predicate
--strategy r-sma && --real. That branch dissolves to the sugar/campaign path;every other built-in invocation — synthetic (any strategy), and the non-r-sma
real path (
--strategy sma --real, a different blueprint the r-sma sugar does notsynthesize) — keeps the existing inline body verbatim.
run_walkforwardand allits helpers are retained; this cycle deletes no inline code (it is additive).
Basis (derived): Reading A dissolves the real-archive built-in execution, and
the r-sma sugar synthesizes only the
sma_signalblueprint — so onlyr-sma --realhas a dissolved form this cycle. The synthetic path is fenceduntil #159 regardless. This mirrors
mc's own built-in split (--strategy r-smaselects the R-bootstrap path; else the synthetic seed-resweep), so the walkforward
arm gains the same shape rather than a delete-everything rewrite generalize could
afford only because it had no synthetic branch.
Consequence for the acceptance criteria: AC 2 is now "r-sma --real no longer flows
through
run_walkforward;run_walkforwardis retained for the fenced branches",not "run_walkforward is deleted". Recon's caller enumeration confirms every
walkforward helper keeps a live non-test caller (the synthetic path, the blueprint
walkforward path, and the
mcR-bootstrap path) — so nothing was deletableanyway.
Re-grounded after Fork C (the run_walkforward-retention split): fresh grounding-check PASS. The new current-behaviour claim (the built-in arm today routes both synthetic and real through the inline handler; the synthetic path runs e2e) is ratified by walkforward_bare_sma_summary_has_no_oos_r + walkforward_strategy_r_sma_reports_oos_r; all prior assumptions still hold. Spec re-signed on the PASS (no human); design settled, proceeding to plan production.
Derived decisions — mc R-bootstrap dissolution (specify, cycle 4 of #210, the LAST verb)
Recorded before the spec (Step 1.5). Derived orchestrator decisions (rationale),
open to veto. Forks A (stop→regime) and C (retention split) mirror the walkforward
dissolution verbatim; only the seed mapping is genuinely new.
The one new load-bearing decision — the campaign seed carries the mc
--seedThe inline mc R-bootstrap uses TWO seeds: a fixed
DEFLATION_SEEDfor theper-window walk-forward winner selection, and the mc
--seedfor the terminalr_bootstrap. The campaign path has only ONE seed (campaign.seed), used for BOTHthe wf-stage deflation and the monte_carlo-stage
r_bootstrap(exec.rs:400-446).Decision:
translate_mcsetscampaign.seed = the mc --seed.Basis (derived): the wf winners are argmax by the raw metric — the deflation
seed only annotates the recorded selection provenance (overfit_probability /
deflated_score), it does NOT change which point is picked. So the winners, and
therefore the pooled OOS trade-R series, are seed-independent. This is already
PROVEN by the shipped walkforward anchor: the campaign path at
campaign.seed = 0reproduced the inline path's
DEFLATION_SEED(3740379117) winners byte-for-byte(param_stability means intact through the dissolution). So remapping the campaign
seed to the mc
--seedleaves the pooled series unchanged (n_trades = 20681,matching the walkforward multi-grid anchor — mc pools the same series) and makes the
r_bootstrapat that seed reproduce the inline bootstrap exactly. The committedanchor
mc_r_bootstrap_real_e2e_pins_the_exact_current_grade(3fc491a) gates it.Fork A (stop→regime) and Fork C (retention split) — as walkforward
RiskRegime::Vol(compared, not swept);--stop-length/--stop-ksingle-value; fast/slow the multi-value IS-refit grid.--strategy r-sma --realdissolves.dispatch_mc'sbuilt-in arm splits on
strategy == r-sma && real.is_some()→run_mc_sugar;everything else (synthetic-r-sma via
McArgs::RealRwith a synthetic choice, thesynthetic seed-resweep
run_mc, and the blueprint mc path) keeps the existinginline logic verbatim.
run_mc_r_bootstrap/run_mc/mc_r_bootstrap_reportare RETAINED — this cycle deletes NO inline code.
Shape (pure sugar, no executor change)
Process
[std::sweep(argmax, "sqn_normalized"), std::walk_forward{roller sizes, Rolling, "sqn_normalized", argmax}, std::monte_carlo{resamples, block_len}]; campaign =one instrument, multi-value fast/slow axes, one Vol regime,
seed = mc_seed.run_mc_sugarreads the monte_carlo stage'sStageBootstrap::PooledOos(RBootstrap)from
run.outcome.record.cells[0].stages[].bootstrapand prints the existingmc_r_bootstrap_json(&RBootstrap)— ONE line, no per-window member lines (unlikewalkforward). The two walkforward plan-guess corrections carry over: resolve the
window via
source.full_window(env)unconditionally, and the record read musthandle wrapped axis names where relevant.
Spec auto-signed (grounding-check PASS) — mc R-bootstrap dissolution
The mc-dissolution spec was signed autonomously under /boss: the signature is the Step-5 grounding-check PASS (an independent fresh-context agent's verdict against currently-green tests), not orchestrator confidence. No human signed it.
Capability signed:
aura mc --strategy r-sma --real …routes through the one campaign path as thin sugar (a[std::sweep(argmax), std::walk_forward, std::monte_carlo]campaign document), reproducing the{"mc_r_bootstrap":…}line byte-for-byte; the fenced branches (synthetic-r-sma, the synthetic seed-resweep, blueprint mc) stay verb-wired (Reading A / Fork C). This is the FOURTH and LAST verb dissolution.Grounding ratified all load-bearing current-behaviour assumptions. The one new decision —
campaign.seed = the mc --seed— is ratified doubly: (a) execute_mc_after_wf_pools_the_oos_series asserts the campaign mc-stage bootstrap is seeded from the campaign seed; (b) the shipped walkforward anchor is genuine cross-seed evidence (campaign seed 0 reproduced the inline DEFLATION_SEED 0xDEF1A7ED winners byte-for-byte → the wf winners are argmax/seed-independent, so the pooled series is unchanged). The committed anchor3fc491agates it.Status: design settled, spec produced and signed — ready for plan production.
mc R-bootstrap dissolution — shipped + closed drift-clean
The fourth and last verb dissolution is complete.
aura mc --strategy r-sma --realnow 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 back from the runrecord and reprints the existing
mc_r_bootstrapgrade line, byte-identical tothe inline path. Pure sugar — the executor (aura-campaign / aura-engine /
aura-research) is byte-for-byte diff-free. This mirrors the walkforward
dissolution.
Load-bearing decision — the campaign seed carries the mc
--seed(derived).The inline mc path used two seeds (a fixed deflation seed for the per-window
walk-forward winner selection, and the mc
--seedfor the terminal bootstrap);the campaign path has one seed used for both. This reconciles because the
walk-forward winners are argmax-selected, hence deflation-seed-independent —
already proven by the shipped walkforward equivalence anchor — so remapping the
single campaign seed to the mc
--seedleaves the pooled OOS trade-R seriesunchanged while making the bootstrap at that seed reproduce the inline draw. A
dedicated end-to-end test pins exactly this: two
--seedvalues over the sameinvocation yield identical pooled trade counts but a moved resampled
E[R]mean.Fork A (stop) and Fork C (retention) — as walkforward. The stop binds as a
single
RiskRegime::Vol(a multi-value stop is refused); fast/slow are themulti-value in-sample-refit grid. Only
--strategy r-sma --realdissolves; theinline handlers (
run_mc_r_bootstrap/run_mc) are retained, not deleted, forthe fenced synthetic and non-real paths, until the verb surfaces are unfenced
(#159).
--name/--traceare rejected on the sugar path — this isequivalence-preservation, not a new choice: the inline mc R-bootstrap path
already rejects them (the mc R-path records without a family name), so the sugar
keeps that refusal. This is the one place mc diverges from the walkforward
template, which accepts those flags.
Equivalence gate. The committed byte-identity anchor for the exact current mc
grade ran the real GER40 archive and passed byte-for-byte through the new path;
full workspace suite green; lint clean.
State. The verb set — sweep, generalize, walkforward, mc — is now fully
dissolved into the one campaign path. The arg-bundle consolidation (#214) is
unblocked: it was deliberately deferred until all four verbs dissolved so the
shared invocation shape is cut once over the settled common form. The commits are
local and unpushed; a push, the formal milestone close, and the milestone-wide
fieldtest remain outstanding.
Milestone closed — "Verb dissolution: the campaign path as canonical orchestration"
All four orchestration verbs are dissolved into the one campaign executor, in the
decision-7 order: sweep (cycle 0110, its blueprint form), then generalize,
walkforward, and mc's R-bootstrap path — each now thin sugar over a generated,
content-addressed campaign document, behaviour-preserving (byte-identical summary
lines), plus the structural risk-regime axis (
CampaignDoc.risk: [RiskRegime], solevol{length,k}variant, compared as a matrix dimension).Close gate — milestone fieldtest (2026-07-07): green
Authored from the public interface only, over the real GER40/USDJPY 2025 archive.
Three end-to-end scenarios: (1) all four dissolved verbs on the r-sma real path,
correct + deterministic; (2) reach-through —
aura generalizeauto-registers acampaign + process document and
aura campaign run <id>reproduces itsworst_casebyte-for-byte; (3) a hand-authored 2-instrument × 2-window × 2-regime matrix (8 cells
→ 4 generalizations) that no verb can express, regimes compared and never argmax'd.
Result: 0 bugs; the core promise holds empirically — behaviour preservation,
campaign-substrate reach-through, and the risk-regime axis all confirmed.
Intended-scope clarification (ratified)
The dissolved form is per-verb, by intent: for sweep it is the blueprint file
(
<bp.json> --real), whileaura sweep --strategy r-sma --realstays the inlinebuilt-in path — the built-in
--strategydemo surface is the #159 hard-wired-harnessretirement target, not this milestone's; for generalize/walkforward/mc the dissolved
form is
--strategy r-sma --real. Recorded in the design ledger.Forward findings (discoverability/ergonomics, non-blocking)
runs/store into cwdThe ledger status line is refreshed to 4/4 and the glossary verb entries are marked as
campaign sugar. Those closing commits (fieldtest corpus + ledger/glossary) are local;
the push to origin/main is pending.