R7/RQ3: executor-wiring stone — offline selector DVs + calibration gate
Wire the RQ3 churn-resilience companion (severable follow-on; RQ3 frozen in its
own prereg) as SYNTHETIC/OFFLINE build + calibration only — no confirmatory
battery run.
- battery: enumerate_rq3_cells()/rq3_schedule() add selector in {static(control),
random, agent} at 1house/bridge-off under pinned churn kp=30/steps=20, kept
SEPARATE so the frozen 6-cell lead lattice stays byte-identical.
- executor: run_rq3_cell_run collects offline selector DVs (throughput-retention,
drops/rebuilds, rebuild-interval gaps); added-latency is live-only (offline path
records None, never fabricated); run_rq3_battery(live=False) hard-raises.
- analysis/rq3_calibration: DRY offline gate. Churn-bites (1589 drops/1576
rebuilds) + rebuild-classifier calibration green with real teeth — churned(kp30)
vs baseline(kp5) AUC 0.926 separable, baseline-vs-baseline null 0.518 blind.
Classifier scored on the PER-RUN mean inter-rebuild gap (the confirmatory
grouping unit); the frozen instrument (rebuild_interval_gaps,
rebuild_classifier_auc) is untouched, no fit to confirmatory data.
HARD HOLD: no RQ3 confirmatory battery (live added-latency is operator+grid-gated).
Lead prereg SHA f22331a72e… untouched; containment intact (synthetic/offline, $0
local Ollama, frontier arm inert); worktree-only.
Tests: tests/test_sor_rq3_wiring.py 9 passed; full SOR suite 194 passed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -126,6 +126,63 @@ def enumerate_rq2p3_cells() -> List[Cell]:
|
||||
return cells
|
||||
|
||||
|
||||
# --- RQ3 companion cells (frozen prereg §3/§4; run-brief params) ------------ #
|
||||
RQ3_KILL_PROB_PCT = 30 # pinned churn kill probability (run-brief §2(B))
|
||||
RQ3_CHURN_STEPS = 20 # pinned churn horizon (run-brief §2(B))
|
||||
|
||||
|
||||
def enumerate_rq3_cells() -> List[Cell]:
|
||||
"""The RQ3 companion cells (`sor-consent-prereg.md` §3/§4, params pinned in
|
||||
`rq3-companion-run-brief.md` §2), kept **separate** from the frozen lead lattice so
|
||||
`enumerate_cells()` stays byte-identical for every lead-pipeline caller.
|
||||
|
||||
The selector arm (`selector ∈ {static, random, agent}`) is the only manipulation,
|
||||
at the 1-house / bridge-off control, under the pinned churn schedule
|
||||
(`kill_prob_pct = 30`, `steps = 20`). ``static`` is the interleaved control baseline;
|
||||
``random`` and ``agent`` are treatments. Tagged ``rq = "RQ3"`` so they are distinct
|
||||
from the frozen RQ1/RQ2 cells and are skipped by the lead collectors.
|
||||
"""
|
||||
churn_tag = f"kp{RQ3_KILL_PROB_PCT}s{RQ3_CHURN_STEPS}"
|
||||
cells: List[Cell] = []
|
||||
for sel in ("static", "random", "agent"):
|
||||
cells.append(Cell(
|
||||
"RQ3",
|
||||
f"RQ3/topo=1house/bridge=off/selector={sel}/churn={churn_tag}",
|
||||
{"bridge": "off", "topology": "1house", "selector": sel,
|
||||
"churn_kill_prob_pct": str(RQ3_KILL_PROB_PCT),
|
||||
"churn_steps": str(RQ3_CHURN_STEPS)},
|
||||
is_control=(sel == "static"),
|
||||
))
|
||||
return cells
|
||||
|
||||
|
||||
def rq3_schedule(order_seed: int, r: int = R_RUNS) -> List[PlannedRun]:
|
||||
"""RQ3 executable run order, honouring §2's interleaving discipline: runs
|
||||
randomized **within** each cell, and the ``static`` control arm interleaved
|
||||
**before and after** the {random, agent} treatments so grid drift is bracketed.
|
||||
Deterministic from ``order_seed`` (a measurement-side ordering seed). Kept
|
||||
**separate** from the frozen lead ``battery_schedule`` (which is left byte-identical)."""
|
||||
rng = random.Random(order_seed)
|
||||
cells = enumerate_rq3_cells()
|
||||
control = next(c for c in cells if c.is_control)
|
||||
treatments = [c for c in cells if not c.is_control]
|
||||
|
||||
def runs_of(c: Cell) -> List[PlannedRun]:
|
||||
rs = [PlannedRun(c.cell_id, c.rq, i, derive_seed(c.cell_id, i), c.is_control)
|
||||
for i in range(r)]
|
||||
rng.shuffle(rs)
|
||||
return rs
|
||||
|
||||
control_runs = runs_of(control)
|
||||
half = len(control_runs) // 2
|
||||
ordered: List[PlannedRun] = list(control_runs[:half]) # control BEFORE
|
||||
treatment_runs = [pr for c in treatments for pr in runs_of(c)]
|
||||
rng.shuffle(treatment_runs)
|
||||
ordered.extend(treatment_runs)
|
||||
ordered.extend(control_runs[half:]) # control AFTER
|
||||
return ordered
|
||||
|
||||
|
||||
def declared_na_cells() -> List[Cell]:
|
||||
"""N/A cells declared at the design (not run, not dropped ad hoc) — padding is
|
||||
only defined for bridge-on, so bridge-off+padding is N/A (§2)."""
|
||||
|
||||
Reference in New Issue
Block a user