experiment-os: 'run' self-registers the declared cohort (fresh-DB bootstrap)

The scheduler ran 'experiments run/adjudicate' but nothing registered the five
experiments/*.json into the experiments table, so on a fresh DB the harness
replayed over an empty set and the ledger stayed empty forever (owner hit this
on first deploy). registry.register_cohort() registers every cohort JSON
idempotently (skips already-registered, tolerates a malformed file); 'run'
calls it at startup so a fresh deployment self-bootstraps with no manual
register step. +test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
momentum devandClaude Fable 5 committed 2026-07-14 10:14:51 -04:00
1 parent a12dc8a768
commit 16f880d372
3 files changed
+45 -1

No files matched your search

+6 -1
View File
@@ -39,9 +39,14 @@ def main(argv=None) -> int:
return 0
if cmd == "run":
from research.experiments import replay
from research.experiments import registry, replay
store = _store()
try:
# self-bootstrap: register the declared cohort (idempotent) so a fresh
# DB replays the experiments instead of silently running over nothing.
newly = registry.register_cohort(store.con)
if newly:
print(f"registered cohort: {', '.join(newly)}")
n = replay.run(store)
print(f"experiments run: {n} new run rows")
finally:
+23
View File
@@ -15,6 +15,7 @@ from datetime import datetime, timezone
from pathlib import Path
_SCHEMA_SQL = Path(__file__).resolve().parents[2] / "storage" / "experiments_schema.sql"
_COHORT_DIR = Path(__file__).resolve().parents[2] / "experiments"
# Every experiment JSON must carry these; fidelity must carry these floors.
_REQUIRED = ("id", "hypothesis", "population", "intervention", "bars", "fidelity")
@@ -75,6 +76,28 @@ def register(con, json_path: str | Path) -> str:
return exp_id
def register_cohort(con, cohort_dir: str | Path | None = None) -> list[str]:
"""Register every ``experiments/*.json`` not already registered (idempotent).
The declared cohort must be in the ``experiments`` table before ``run`` can
replay anything — otherwise the harness silently does nothing on a fresh DB.
``run`` calls this at startup so a fresh deployment self-bootstraps with no
manual ``register`` step. Already-registered ids are skipped (immutable);
a malformed file is logged and skipped, never fatal to the run."""
apply_schema(con)
d = Path(cohort_dir) if cohort_dir else _COHORT_DIR
newly: list[str] = []
for f in sorted(d.glob("*.json")):
try:
newly.append(register(con, f))
except ValueError as exc:
# already registered (the common nightly case) or malformed — skip it
if "already registered" not in str(exc):
import logging
logging.getLogger(__name__).warning("skip cohort file %s: %s", f.name, exc)
return newly
def _row_to_dict(row) -> dict:
return {
"id": row[0],
+16
View File
@@ -47,3 +47,19 @@ def test_list_running_only(store, tmp_path):
store.con.execute("UPDATE experiments SET status='failed' WHERE id='b_v1'")
running = registry.list_experiments(store.con, running_only=True)
assert [e["id"] for e in running] == ["a_v1"]
def test_register_cohort_bootstraps_and_is_idempotent(store):
"""run's self-bootstrap: register_cohort registers every experiments/*.json
from the real cohort dir, and a second call adds nothing (idempotent)."""
from research.experiments import registry
first = registry.register_cohort(store.con)
assert len(first) == 5 # the shipped first cohort
assert "pm1_v1" in first and "time_stop_10m_v1" in first
# every one landed as a running experiment
running = {e["id"] for e in registry.list_experiments(store.con, running_only=True)}
assert set(first) <= running
# idempotent: re-running registers nothing new, raises nothing
second = registry.register_cohort(store.con)
assert second == []
assert len(registry.list_experiments(store.con)) == 5