Campaign executor: aura campaign run drives the existing family machinery from persisted intent #198
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?
The executor-need re-test the cycle-0106 triage deliberately gated on (#189 decided item 6) has run: the cycle-0106 fieldtest (finding F7, fieldtests/cycle-0106-research-artifacts/FINDINGS.md) confirms the diagnosed intent-persistence gap is closed — a campaign+process pair is now a complete, content-addressed declarative spec of a sweep→gate→walk-forward the engine already runs — and that nothing consumes it: the trail ends at runs/campaigns/.json. The user's standing decision on the existing verbs ('dissolve the existing into the new — but only once the new carries', 2026-07-03, #188 resolution comment) requires the campaign path to carry, i.e. to execute.
Scope sketch (design pass to settle the forks): resolve refs via the referential tier, enumerate the campaign's axes into the existing family machinery (sweep_family/optimize/optimize_deflated/optimize_plateau, walkforward_family, r-bootstrap MC, generalization) stage by stage per the process pipeline, evaluate gates over the declared metrics between stages, record a campaign-level realization in the registry (doc = intent, realized run-set = manifest layer — fork-triage decided item 4 on #189), and honour data-level presentation (persist_taps, emit). Open design forks include: verb shape and id/name addressing, stage→machinery binding semantics (esp. walk_forward fold policy vs the shipped windowing), gate evaluation over families (per-member vs winner), campaign-level registry record shape, and v1 stage coverage.
Fork triage: campaign-executor design (grounded stance review, 2026-07-03)
The executor's design forks were populated by five adversarial design lenses grounded in the ledger, the shipped cycle-0106 code, the fieldtest corpus, and #188/#189. Decisions below are made where the lenses converged on citable ground (veto by replying here); one fork stays open for a decision.
Decided (with grounds)
Addressing: both file and content id; the content id is canonical.
aura campaign run <file|content-id>— a file is sugar that resolves (and registers) to the same content id before anything runs, since the stores are self-keying and the id is always computable from the bytes; id-addressing matches the shippedaura reproduce <family-id>pattern, file-addressing matches the verb family's existing grammar.The walk_forward stage vocabulary is corrected to machinery-true fields. "Map folds directly" is not an available option:
WindowRoller::newtakesis_len/oos_len/step/modeand no fold count (folds are a derived output of rolling a span). Thefoldsslot is replaced by the fields the shipped primitive actually accepts; inventing a folds→step heuristic inside executor glue would be an undocumented private mini-DSL. Schema change is cheap now (v1 shipped unpushed this session); a roller extension stays a separate future feature (the ledger itself calls current windowing calibration debt).Gates filter per-member survivors — and the shipped
selectfield is not in conflict. The ratified P1 text is explicit and user-approved ("stage2 runs on stage1 survivors where net_e_r > 0 …, a filter, not an if" — #188 addendum, ratified "ja, entspricht meiner Intuition" 2026-07-03). Reconciliation with the shipped schema: a sweep stage's mandatoryselectnames the stage's recorded selection (winner annotation + the deflation anchor), while the member population flows onward; a gate filters members over their per-member family records; a following walk_forward takes the survivor set as its in-sample search space. No schema surgery — winner-only flow was a mis-reading ofselect's role, not a second legitimate semantics.Realization record: a new campaign-level record type referencing per-stage family ids.
FamilyRunRecord/FamilyKindare doc-scoped to ONE orchestration-axis kind (homogeneity is load-bearing); a campaign is a sequence of heterogeneous stage kinds — a thin linking record over untouched family records follows the registry's own growth pattern (new siblings beside runs.jsonl/blueprints, never overloaded fields). [Pre-review enumeration had held this open; closed on the structural citations — veto rides on the notify.]v1 stage coverage: sweep + gate + walk_forward; monte_carlo/generalize follow in-milestone. The vocabulary itself already separates forwarding stages from "terminal annotator in v1" blocks (their own doc strings), and the fieldtest's diagnosed pipeline is exactly sweep→gate→walk_forward. Continuation semantics for the two annotator kinds is a follow-up, not a silent rejection: v1 refuses a campaign whose process uses them, with prose naming the v1 boundary.
Presentation v1 splits on hard code facts:
emitis honored,persist_tapsis deferred loudly.emitis data-level by its own contract and the JSON-dump pattern exists (walkforward_summary_jsonprecedent) — family_table/selection_report ship as structured dumps of already-computed structs (exact shapes are spec work).persist_tapshas no possible consumer today: every shipped Recorder is hand-wired, and a name→tap wiring mechanism does not exist — v1 prints an explicit "persist_taps not yet honored" line per run (never a silent drop; the F7 lesson), and the wiring lands as its own item. [Held open pre-review; closed on the grep/code facts — veto rides.]Member unit across data: one pipeline execution per (instrument, window) structural cell. Instruments and windows are C20 structural axes (different harness instances — the experiment matrix), the tuning sweep is the inner loop; gates filter members within a cell; cross-cell aggregation remains the generalize stage's later concern.
Zero survivors at runtime is a clean early termination, not an error. A well-formed gate can legitimately filter out every member for a given cell; the campaign records the realization up to and including the empty-survivor stage and exits 0 — a null result is a valid research result (the inferential-honesty ethos).
Failure/resume: v1 re-runs whole. C1 determinism makes a re-run bit-identical; no checkpoint/resume machinery in v1 (recorded as an explicit non-goal; revisit when campaigns grow long enough to hurt).
Harness binding reuses the shipped loaded-blueprint convention. A strategy ref alone is not a harness (C20); the binding recipe that
aura run/sweep <blueprint.json> --real SYMalready ships (single price-role binding to the instrument's data window) is the v1 convention — a blueprint whose role set does not fit it is refused with precise prose. The per-strategyenum Strategymatch-arms in the current drivers are NOT extended (they remain the condemned pre-C24 scaffolding).Delivery is sliced into shippable increments in the spec (the registry's own realization history is incremental — never one multi-axis drop).
Open — awaiting a decision (direction fork)
Executor home. Three positions, each with real ground: (i) aura-cli — the existing family drivers and
project::Envlive there, and the executor's charter is "drive existing machinery"; (ii) a new engine-repo library crate (recommendation) — the cli drivers are the pre-C24 scaffolding the ledger already schedules for retirement (extending a condemned home cements it), aura-registry's own module doc scopes it as memory ("storage…; display is the caller's concern") and C21 distinguishes the World's memory from its orchestration, and a library crate is reachable by more than one consumer (CLI today, playground/tests later) — note this crate is not "the World" (C21's World is a project-side program; this is the reusable campaign-execution library such programs and the CLI drive); (iii) aura-registry — it already owns every selection primitive the executor sequences, and a new crate ahead of the World design could preempt its shape. Put up rather than derived: the pre-review enumeration held it open and the lenses split three ways.Status: decisions 1–11 fix the executor design; the home fork gates the spec.
Design decision (user): executor home
The one open fork from the fork-triage comment (2026-07-03, "Fork triage: campaign-executor design") is resolved.
Basis: user, 2026-07-03: "ich denke 2 ist korrekt, vor allem wegen der zukünftigen konsumenten" — option (ii), decisive ground the multi-consumer reach (CLI today, playground/tests later; the crate is the reusable campaign-execution library, not C21's project-side World).
Decisions 1–11 of the triage stand un-vetoed. Design settled — ready for spec production.
Post-ship decision (derived): register-then-run stores before the gate — ratified as-is (fieldtest 0107, F10)
The cycle-0107 fieldtest observed that
aura campaign run <file>registers the campaign intoruns/campaigns/before the referential gate and preflight, so documents that then refuse (bad refs, v1-boundary processes) accumulate in the store, and asked whether the sugar should gate before storing.Basis: derived — the standalone
aura campaign registerverb already stores on intrinsic validity alone (the referential tier is advisory at register time by design: a ref can become resolvable later when its target is registered), so gating the run-sugar's store write would make the same document storable via one verb and refused via another; the content-addressed store is an idempotent intent archive keyed by bytes, and runnability is time-varying state, not a property of the bytes. Never-runnable documents in the store are inert (nothing consumes them unless addressed).Veto by replying here.