diff --git a/storage/experiments_schema.sql b/storage/experiments_schema.sql new file mode 100644 index 0000000..ed25e05 --- /dev/null +++ b/storage/experiments_schema.sql @@ -0,0 +1,46 @@ +-- Experiment OS schema (plan 006) — pre-registered, auto-adjudicated +-- counterfactual experiments over logged fires. +-- +-- Applied by research/experiments/registry.apply_schema on first run (same +-- idiom as pg_schema.sql; also handed to the owner to run manually if preferred). +-- Idempotent: safe to run repeatedly (CREATE ... IF NOT EXISTS throughout). + +-- An experiment is a pre-registered, immutable contract. Rows are never +-- updated after registration except status/verdict fields. +CREATE TABLE IF NOT EXISTS experiments ( + id TEXT PRIMARY KEY, -- slug, e.g. 'time_stop_10m_v1' + registered_at TIMESTAMP NOT NULL, + hypothesis TEXT NOT NULL, -- one sentence, human-readable + population JSONB NOT NULL, -- PopulationConfig dump + extra filters + intervention JSONB NOT NULL, -- typed params, e.g. {"kind":"time_stop","minutes":10,"confirm":"new_high_or_0.5R"} + bars JSONB NOT NULL, -- pre-registered pass/fail criteria, e.g. {"expR_delta_min":0.05,"min_n":300,"p_max":0.05} + futility JSONB, -- early-kill rule, e.g. {"min_n":500,"expR_delta_max":-0.02} + fidelity JSONB NOT NULL, -- slippage floors: {"atr_frac":0.25,"spread_mult":3,"adverse_ticks":1} + status TEXT NOT NULL DEFAULT 'running', -- running|passed|failed|inconclusive|killed + verdict_at TIMESTAMP, + verdict_note TEXT +); + +-- One row per (experiment, replayed fire): the counterfactual outcome. +CREATE TABLE IF NOT EXISTS experiment_runs ( + experiment_id TEXT NOT NULL REFERENCES experiments(id), + fire_id TEXT NOT NULL, -- order_id / receipt correlation id + symbol TEXT NOT NULL, + session_date DATE NOT NULL, + baseline_r DOUBLE PRECISION, -- what actually happened (R units) + variant_r DOUBLE PRECISION, -- counterfactual outcome (R units) + detail JSONB, -- entry/exit stamps, fills, floor hits + computed_at TIMESTAMP NOT NULL, + PRIMARY KEY (experiment_id, fire_id) +); +CREATE INDEX IF NOT EXISTS idx_exp_runs_exp_date ON experiment_runs (experiment_id, session_date); + +-- Verdict history (append-only; the dashboard ledger reads this). +CREATE TABLE IF NOT EXISTS experiment_verdicts ( + experiment_id TEXT NOT NULL REFERENCES experiments(id), + as_of TIMESTAMP NOT NULL, + n INTEGER NOT NULL, + metrics JSONB NOT NULL, -- observed values vs each bar + verdict TEXT NOT NULL, -- PASS|FAIL|INCONCLUSIVE|KILLED + PRIMARY KEY (experiment_id, as_of) +);