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>
This commit is contained in:
momentum devandClaude Fable 5 committed 2026-07-22 23:28:06 -05:00
1 parent 537e0c7e12
commit 82273d9d4e
11 files changed
+57 -2

No files matched your search

+7
View File
@@ -64,12 +64,19 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- id: ver
run: echo "tag=$(date -u +%Y.%m.%d)-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
- uses: docker/build-push-action@v6
with:
context: .
push: true
build-args: |
GIT_SHA=${{ github.sha }}
BUILD_TAG=${{ steps.ver.outputs.tag }}
tags: |
ghcr.io/fez5587/momentum-app:latest
ghcr.io/fez5587/momentum-app:${{ github.sha }}
ghcr.io/fez5587/momentum-app:${{ steps.ver.outputs.tag }}
fez5587/momentum-app:latest
fez5587/momentum-app:${{ github.sha }}
fez5587/momentum-app:${{ steps.ver.outputs.tag }}
+7
View File
@@ -14,6 +14,13 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends libpq5 ffmpeg curl \
&& rm -rf /var/lib/apt/lists/*
# Image identity: CI passes the git sha + a date tag so the RUNNING app can
# report exactly what it is (TrueNAS custom apps show only 'custom/1.0.0').
ARG GIT_SHA=dev
ARG BUILD_TAG=dev
ENV APP_GIT_SHA=$GIT_SHA \
APP_BUILD_TAG=$BUILD_TAG
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=/app \
+3 -1
View File
@@ -321,7 +321,9 @@ class Handler(BaseHTTPRequestHandler):
return self._send(401, {"error": "bad or missing X-Admin-Token"})
if path == "/api/status":
return self._send(200, {"containers": list_containers(), "checks": checks(),
"overrides": _safe_overrides()})
"overrides": _safe_overrides(),
"version": os.environ.get("APP_BUILD_TAG", "dev"),
"git_sha": os.environ.get("APP_GIT_SHA", "dev")[:12]})
if path == "/api/config":
eff = {k: v for k, v in sorted(os.environ.items()) if _editable(k)}
secrets = {k: bool(os.environ.get(k)) for k in
+3 -1
View File
@@ -878,7 +878,9 @@ class DashboardHandler(BaseHTTPRequestHandler):
days = int((self._query().get("days") or ["7"])[0])
self._send_json(compute_attribution(days=days))
elif route == "/api/health":
self._send_json({"ok": True, "mode": self.state.execution_mode})
self._send_json({"ok": True, "mode": self.state.execution_mode,
"version": os.environ.get("APP_BUILD_TAG", "dev"),
"git_sha": os.environ.get("APP_GIT_SHA", "dev")[:12]})
else:
self._send_json({"error": "not found"}, 404)
except Exception as exc: # noqa: BLE001
+6
View File
@@ -23,6 +23,12 @@ from dotenv import load_dotenv
sys.path.insert(0, ".")
load_dotenv()
# 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()
os.environ.setdefault("STRATEGY_SETUPS", "opening_range_break")
from alpaca_paper.client import AlpacaPaperClient
+6
View File
@@ -26,6 +26,12 @@ from research.multi_schema import open_research_db
load_dotenv()
# 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 _write_heartbeat(outcome: str) -> None:
"""Record that the tuner RAN (regardless of whether it found a profitable
+6
View File
@@ -11,6 +11,12 @@ 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
+6
View File
@@ -26,6 +26,12 @@ from research.ingestion.market_data import ( # noqa: E402
upsert_daily_bars, upsert_minute_bars)
from research.multi_schema import open_research_db # noqa: E402
# 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()
INDEX_SYMBOLS = ("SPY", "QQQ")
+6
View File
@@ -18,6 +18,12 @@ from dotenv import load_dotenv
from research import labeler
from research.multi_schema import open_research_db
# 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()
load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))
HIST = os.path.join(os.path.dirname(__file__), "..", "data", "premarket_eval_history.tsv")
+6
View File
@@ -37,6 +37,12 @@ from datetime import date, datetime, timedelta, timezone
from pathlib import Path
from statistics import mean, median, pstdev
# 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()
_SCHEMA_SQL = Path(__file__).resolve().parent.parent / "storage" / "regime_schema.sql"
# Pre-open / prior-close-lagged features only. See module docstring + plan 011
+1
View File
@@ -29,6 +29,7 @@ def test_read_missing_table_is_empty(con):
def test_load_db_applies_onto_environ(con, monkeypatch):
monkeypatch.setenv("ENV_OVERRIDES_DB", "1") # force on (harness may disable)
env_overrides.write_db_overrides({"TRADING_TEST_KNOB_XYZ": "42"}, con=con)
monkeypatch.setattr(env_overrides, "_db_con", lambda: con)
monkeypatch.setenv("TRADING_TEST_KNOB_XYZ", "1") # compose value loses