Files
momentum devandClaude Fable 5 82273d9d4e feat: image version identity + DB-config parity for nightly scripts
Version: CI now tags images YYYY.MM.DD-<shortsha> alongside :latest/:sha
and passes GIT_SHA/BUILD_TAG build-args; the running app reports them via
dashboard /api/health and admin /api/status (TrueNAS custom apps only show
'custom/1.0.0', so the app self-reports what it actually runs).

Config parity: every scheduler-run script (nightly_tune, eod_replay,
premarket_eval, regime, market_context, experiments cli) now loads
config_overrides at startup like the live services do — a bare env would
replay under DEFAULT exits (the documented 4.5x-baseline landmine).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 23:28:06 -05:00

79 lines
2.5 KiB
Python

"""CLI: ``python -m research.experiments {register|run|adjudicate} [args]``.
Thin wrapper. Builds ONE EventStore (its Postgres connection is shared for the
experiment tables) and dispatches. Deferred imports so each subcommand only
pulls what it needs. NEVER run against the production DB from a test — the
harness's real verdict lands when the owner first schedules it.
"""
from __future__ import annotations
import os
import sys
# config parity with the live loop: DB-backed knobs (config_overrides) beat
# compose env — a bare env would replay under DEFAULT exits (the 4.5x-baseline
# landmine, orderflow-shelf-verdicts 2026-07-17)
from runtime.env_overrides import load as _load_cfg_overrides # noqa: E402
_load_cfg_overrides()
def _store():
from storage.event_store import EventStore
return EventStore(os.environ.get("WATCHER_EVENT_DB_PATH", "momentum"))
def main(argv=None) -> int:
argv = list(sys.argv[1:] if argv is None else argv)
if not argv:
print("usage: python -m research.experiments {register <json>|run|adjudicate}")
return 2
cmd, rest = argv[0], argv[1:]
if cmd == "register":
if not rest:
print("usage: python -m research.experiments register <json_path>")
return 2
from research.experiments import registry
store = _store()
try:
registry.apply_schema(store.con)
exp_id = registry.register(store.con, rest[0])
print(f"registered {exp_id}")
finally:
store.close()
return 0
if cmd == "run":
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:
store.close()
return 0
if cmd == "adjudicate":
from research.experiments import adjudicate
store = _store()
try:
verdicts = adjudicate.adjudicate(store)
for v in verdicts:
print(f"{v['experiment_id']}: {v['verdict']} (n={v['n']})")
finally:
store.close()
return 0
print(f"unknown command: {cmd}")
return 2
if __name__ == "__main__":
raise SystemExit(main())