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:
1 parent
a12dc8a768
commit
16f880d372
3 files changed
+45
-1
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user