Campaign executor: execute std::monte_carlo / std::generalize stages (v1 refuses them) #200

Closed
opened 2026-07-03 22:06:50 +02:00 by Brummel · 1 comment
Owner

v1 (#198, cycle 0107) executes exactly std::sweep [std::gate]* [std::walk_forward]? and refuses the two annotator stages with prose naming the boundary (#198 triage, decided item 5: 'continuation semantics for the two annotator kinds is a follow-up, not a silent rejection'). This item defines and ships that continuation semantics: what population an mc/generalize stage consumes mid-pipeline, what its realization records, and how its results flow (or terminate). depends on: cycle-0107 executor (shipped). context: the process vocabulary already carries both blocks; only execution is missing.

v1 (#198, cycle 0107) executes exactly std::sweep [std::gate]* [std::walk_forward]? and refuses the two annotator stages with prose naming the boundary (#198 triage, decided item 5: 'continuation semantics for the two annotator kinds is a follow-up, not a silent rejection'). This item defines and ships that continuation semantics: what population an mc/generalize stage consumes mid-pipeline, what its realization records, and how its results flow (or terminate). depends on: cycle-0107 executor (shipped). context: the process vocabulary already carries both blocks; only execution is missing.
Brummel added the feature label 2026-07-03 22:06:50 +02:00
Brummel added this to the Research artifacts — process & campaign documents milestone 2026-07-03 22:07:03 +02:00
Author
Owner

Fork triage: mc/generalize stage execution (grounded stance review, 2026-07-03)

Five adversarial design lenses (minimalist / methodology-designer / maintainer / skeptic / wildcard), each grounded in the shipped 0106/0107 code, the ledger, and the #198 triage, answered five forks. All five resolve on cited ground (veto by replying here); none bounces.

Decided (with grounds)

  1. std::monte_carlo bootstraps the stage's incoming R-evidence — one semantics, input-shaped by position. The block's fields (resamples, block_len) are byte-for-byte r_bootstrap's arguments (the moving-block bootstrap of a per-trade R series — mc.rs's own doc string names the #139 OOS-series use; the engine's OTHER monte_carlo, the seed axis, needs synthetic sources and is inapplicable to real-data campaigns). Following sweep/gates: one r_bootstrap per surviving member over its in-memory trade_rs, annotating every member (what flows onward is the population — #198 decision 3). Following a walk_forward: one r_bootstrap over the wf stage's pooled per-window OOS trade_rs series (the shipped pooled_oos_trade_rs pattern), never the stale pre-wf survivor vector (the skeptic's refutation of naive per-survivor reuse). Guards: fresh in-executor reports only (trade_rs is #[serde(skip)] — empty after any registry round-trip); zero-trade members record the defined all-zero degenerate explicitly; deterministic from the doc's seed (C1, the deflation convention).

  2. std::generalize executes at campaign scope. The shipped generalization(per_instrument, metric) is cross-instrument by signature (refuses < 2 instruments), while the executor's unit is one (strategy, instrument, window) cell — #198 decision 7 parked exactly this. After all instrument cells of one (strategy, window) complete, the executor generalizes over the per-cell stage-selection winner reports; per-instrument winning params are recorded so divergent winners are exposed, never averaged away (the methodologist's honesty clause). Gate-truncated cells contribute no winner; if fewer than 2 winners remain, the annotation records the shortfall instead of computing nonsense. [The minimalist held generalize should stay refused this cycle — a slicing objection, not a semantic one; the other four lenses define the same semantics and the machinery exists verbatim, so it ships, as its own plan task.]

  3. Both stay terminal annotators (unanimous). Nothing flows out of std::monte_carlo / std::generalize; filtering stays the gate's monopoly (thresholding bootstrap fields would be new logic → a new closed-vocab block per C25, never a second semantics smuggled onto an existing one). The intrinsic tier's terminal_seen/GateAfterTerminalStage and the 0106 'terminal annotator in v1' doc strings already encode this shape. No document-schema bytes change in this cycle — existing content ids are untouched.

  4. Realization shape: extend the existing records, no new siblings. StageRealization gains a sparse bootstrap Option (per-survivor (ordinal, RBootstrap) entries after sweep/gates; the single pooled-OOS RBootstrap after wf — the payload mirrors decision 1's input shape), the established Option-per-stage-kind pattern. The generalize aggregate is recorded at the scope it is computed: a campaign-level field on CampaignRunRecord keyed (strategy, window), carrying the shipped Generalization plus per-instrument winner params. RBootstrap (aura-engine) and Generalization (aura-registry) gain serde derives; existing campaign_runs.jsonl lines stay parseable (serde-default widening, the C14/C23 convention).

  5. v2 executable shape: std::sweep [std::gate]* [std::walk_forward]? [std::monte_carlo]? [std::generalize]? — an ordered optional annotator suffix, each at most once, generalize strictly last (campaign scope). The executor preflight refuses what the intrinsic tier still admits (its MonteCarlo arm is empty, so [sweep, mc, walk_forward] validates intrinsically today): any population-transforming stage after an annotator, duplicate annotators, mc before wf, generalize anywhere but last, generalize over a campaign with < 2 instruments (static), generalize with a non-R metric (static — intrinsic accepts total_pips, the shipped generalization() would refuse it only at runtime), and mc with resamples == 0 or block_len == 0 (static, instead of the engine's defined all-zero degenerate).

Status: decisions 1–5 settle #200's design; ready for spec production.

## Fork triage: mc/generalize stage execution (grounded stance review, 2026-07-03) Five adversarial design lenses (minimalist / methodology-designer / maintainer / skeptic / wildcard), each grounded in the shipped 0106/0107 code, the ledger, and the #198 triage, answered five forks. All five resolve on cited ground (veto by replying here); none bounces. ### Decided (with grounds) 1. **`std::monte_carlo` bootstraps the stage's incoming R-evidence — one semantics, input-shaped by position.** The block's fields (`resamples`, `block_len`) are byte-for-byte `r_bootstrap`'s arguments (the moving-block bootstrap of a per-trade R series — `mc.rs`'s own doc string names the #139 OOS-series use; the engine's OTHER `monte_carlo`, the seed axis, needs synthetic sources and is inapplicable to real-data campaigns). Following sweep/gates: **one `r_bootstrap` per surviving member over its in-memory `trade_rs`**, annotating every member (what flows onward is the population — #198 decision 3). Following a walk_forward: **one `r_bootstrap` over the wf stage's pooled per-window OOS `trade_rs` series** (the shipped `pooled_oos_trade_rs` pattern), never the stale pre-wf survivor vector (the skeptic's refutation of naive per-survivor reuse). Guards: fresh in-executor reports only (`trade_rs` is `#[serde(skip)]` — empty after any registry round-trip); zero-trade members record the defined all-zero degenerate explicitly; deterministic from the doc's `seed` (C1, the deflation convention). 2. **`std::generalize` executes at campaign scope.** The shipped `generalization(per_instrument, metric)` is cross-instrument by signature (refuses < 2 instruments), while the executor's unit is one (strategy, instrument, window) cell — #198 decision 7 parked exactly this. After all instrument cells of one (strategy, window) complete, the executor generalizes over the per-cell stage-selection winner reports; per-instrument winning params are recorded so divergent winners are exposed, never averaged away (the methodologist's honesty clause). Gate-truncated cells contribute no winner; if fewer than 2 winners remain, the annotation records the shortfall instead of computing nonsense. [The minimalist held generalize should stay refused this cycle — a slicing objection, not a semantic one; the other four lenses define the same semantics and the machinery exists verbatim, so it ships, as its own plan task.] 3. **Both stay terminal annotators (unanimous).** Nothing flows out of `std::monte_carlo` / `std::generalize`; filtering stays the gate's monopoly (thresholding bootstrap fields would be new logic → a new closed-vocab block per C25, never a second semantics smuggled onto an existing one). The intrinsic tier's `terminal_seen`/`GateAfterTerminalStage` and the 0106 'terminal annotator in v1' doc strings already encode this shape. No document-schema bytes change in this cycle — existing content ids are untouched. 4. **Realization shape: extend the existing records, no new siblings.** `StageRealization` gains a sparse `bootstrap` Option (per-survivor `(ordinal, RBootstrap)` entries after sweep/gates; the single pooled-OOS `RBootstrap` after wf — the payload mirrors decision 1's input shape), the established Option-per-stage-kind pattern. The generalize aggregate is recorded at the scope it is computed: a campaign-level field on `CampaignRunRecord` keyed (strategy, window), carrying the shipped `Generalization` plus per-instrument winner params. `RBootstrap` (aura-engine) and `Generalization` (aura-registry) gain serde derives; existing `campaign_runs.jsonl` lines stay parseable (serde-default widening, the C14/C23 convention). 5. **v2 executable shape: `std::sweep [std::gate]* [std::walk_forward]? [std::monte_carlo]? [std::generalize]?`** — an ordered optional annotator suffix, each at most once, generalize strictly last (campaign scope). The executor preflight refuses what the intrinsic tier still admits (its MonteCarlo arm is empty, so `[sweep, mc, walk_forward]` validates intrinsically today): any population-transforming stage after an annotator, duplicate annotators, mc before wf, generalize anywhere but last, generalize over a campaign with < 2 instruments (static), generalize with a non-R metric (static — intrinsic accepts `total_pips`, the shipped `generalization()` would refuse it only at runtime), and mc with `resamples == 0` or `block_len == 0` (static, instead of the engine's defined all-zero degenerate). Status: decisions 1–5 settle #200's design; ready for spec production.
Brummel referenced this issue from a commit 2026-07-04 10:53:54 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#200