#!/usr/bin/env python3 """Report stale runtime records and optionally reconcile them explicitly.""" from __future__ import annotations import argparse import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] BACKEND = ROOT / "backend" IMPORT_ROOT = ROOT if (ROOT / "app").is_dir() else BACKEND sys.path.insert(0, str(IMPORT_ROOT)) from app.db.session import SessionLocal # noqa: E402 from app.models import AnalysisRun, Job # noqa: E402 from app.services.runtime_reconciliation_service import RuntimeReconciliationService # noqa: E402 RECONCILE_CONFIRMATION = "reconcile-interrupted-runtime" def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--reconcile", action="store_true", help="Mark running jobs and analysis runs as failed with PROCESS_INTERRUPTED.", ) parser.add_argument( "--confirm", default="", help=f"Required with --reconcile: {RECONCILE_CONFIRMATION}", ) return parser.parse_args() def main() -> int: args = parse_args() if args.reconcile and args.confirm != RECONCILE_CONFIRMATION: print( json.dumps( { "status": "refused", "reason": "Explicit reconciliation confirmation is missing.", "required_confirmation": RECONCILE_CONFIRMATION, }, indent=2, ) ) return 2 with SessionLocal() as db: running_jobs = db.query(Job).filter(Job.status == "running").order_by(Job.created_at).all() running_runs = ( db.query(AnalysisRun) .filter(AnalysisRun.status == "running") .order_by(AnalysisRun.created_at) .all() ) result = { "status": "ok", "mode": "read_only", "running_job_count": len(running_jobs), "running_analysis_run_count": len(running_runs), "running_job_ids": [str(item.id) for item in running_jobs], "running_analysis_run_ids": [str(item.id) for item in running_runs], } if args.reconcile: reconciled = RuntimeReconciliationService.reconcile(db) result.update( { "mode": "reconciled", "interrupted_jobs": reconciled.interrupted_jobs, "interrupted_analysis_runs": reconciled.interrupted_analysis_runs, } ) print(json.dumps(result, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())