Implement ACTIVITY-WP-0026 ops_run claim queue (T01–T06).
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 52s

Add durable claimable ops_runs table, emit dual-write on TaskSpec, REST
claim/lease/complete/fail API, ops status visibility, and consumer docs
aligned with ACT-ADR-005. T07 railiance rollout remains deploy-side.
This commit is contained in:
tegwick 2026-08-03 19:22:50 +02:00
parent a145cc4027
commit 15eb3a2066
15 changed files with 1294 additions and 27 deletions

View file

@ -90,7 +90,7 @@ async def automations_status(
timezone_name: str = Query(default="Europe/Berlin", alias="timezone"),
activity_id: str | None = Query(default=None),
) -> dict[str, Any]:
return await ops_status(
report = await ops_status(
since=since,
until=until,
timezone_name=timezone_name,
@ -100,6 +100,37 @@ async def automations_status(
state_hub_url=os.environ.get("STATE_HUB_URL"),
activity_id=activity_id,
)
# ACTIVITY-WP-0026 T05: ops_run claim-queue visibility + SLA signals
try:
from datetime import timedelta
from sqlalchemy import and_, func, select
from activity_core.orm import OpsRun
from activity_core.ops_run_queue import ops_run_counts
sla_hours = float(os.environ.get("OPS_RUN_SLA_HOURS", "1") or "1")
now = datetime.now(timezone.utc)
sla_cutoff = now - timedelta(hours=max(0.1, sla_hours))
Session = _db()
async with Session() as session:
counts = await ops_run_counts(session)
stuck_stmt = select(func.count()).where(
and_(
OpsRun.state.in_(("open", "claimed")),
OpsRun.created_at < sla_cutoff,
)
)
stuck = int((await session.execute(stuck_stmt)).scalar_one() or 0)
report["ops_runs"] = {
"counts": counts,
"stuck_open_or_claimed": stuck,
"sla_hours": sla_hours,
"list_url": "/ops-runs?state=open",
}
except Exception as exc: # table may not exist pre-migration
report["ops_runs"] = {"error": str(exc), "counts": {}}
return report
@router.get("/automations/{definition_id}")