Campaign executor: execute std::monte_carlo / std::generalize stages (v1 refuses them) #200
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?
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.
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)
std::monte_carlobootstraps the stage's incoming R-evidence — one semantics, input-shaped by position. The block's fields (resamples,block_len) are byte-for-byter_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 OTHERmonte_carlo, the seed axis, needs synthetic sources and is inapplicable to real-data campaigns). Following sweep/gates: oner_bootstrapper surviving member over its in-memorytrade_rs, annotating every member (what flows onward is the population — #198 decision 3). Following a walk_forward: oner_bootstrapover the wf stage's pooled per-window OOStrade_rsseries (the shippedpooled_oos_trade_rspattern), never the stale pre-wf survivor vector (the skeptic's refutation of naive per-survivor reuse). Guards: fresh in-executor reports only (trade_rsis#[serde(skip)]— empty after any registry round-trip); zero-trade members record the defined all-zero degenerate explicitly; deterministic from the doc'sseed(C1, the deflation convention).std::generalizeexecutes at campaign scope. The shippedgeneralization(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.]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'sterminal_seen/GateAfterTerminalStageand 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.Realization shape: extend the existing records, no new siblings.
StageRealizationgains a sparsebootstrapOption (per-survivor(ordinal, RBootstrap)entries after sweep/gates; the single pooled-OOSRBootstrapafter 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 onCampaignRunRecordkeyed (strategy, window), carrying the shippedGeneralizationplus per-instrument winner params.RBootstrap(aura-engine) andGeneralization(aura-registry) gain serde derives; existingcampaign_runs.jsonllines stay parseable (serde-default widening, the C14/C23 convention).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 acceptstotal_pips, the shippedgeneralization()would refuse it only at runtime), and mc withresamples == 0orblock_len == 0(static, instead of the engine's defined all-zero degenerate).Status: decisions 1–5 settle #200's design; ready for spec production.