Add Core Hub production stabilization scheduled checks
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 35s

Introduce the core-hub context resolver stabilization_check query, State Hub
progress summaries, activity-definition projections, and unit tests for the
CORE-WP-0007 post-cutover window.
This commit is contained in:
tegwick 2026-07-08 00:27:12 +02:00
parent c253139ee3
commit 151512a8fa
5 changed files with 383 additions and 1 deletions

View file

@ -1,4 +1,5 @@
from activity_core.context_resolvers import ( # noqa: F401
core_hub,
event_payload,
kaizen,
ops_inventory,

View file

@ -0,0 +1,193 @@
"""Core Hub production stabilization context adapter.
Registered as source type ``core-hub``.
Supported queries:
- stabilization_check: public production surface gates for the post-cutover
window before CORE-WP-0007-T02 operator sign-off.
"""
from __future__ import annotations
import os
from datetime import datetime, timezone
from typing import Any
import httpx
from activity_core.context_resolvers.base import CONTEXT_RESOLVER_REGISTRY, ContextResolver
from activity_core.context_resolvers.state_hub import _parse_iso_datetime, _utc_now
_DEFAULT_BASE_URL = "https://hub.coulomb.social"
_DEFAULT_TIMEOUT_SECONDS = 15.0
_PUBLIC_CHECKS: tuple[tuple[str, int], ...] = (
("/healthz", 200),
("/readyz", 200),
("/api/v2/widget-types", 200),
("/api/v2/hubs", 401),
)
class CoreHubContextResolver(ContextResolver):
"""Resolve lightweight Core Hub stabilization checks over public HTTP."""
def resolve(self, query: str, event: Any, params: dict[str, Any]) -> dict[str, Any]:
if query == "stabilization_check":
return _core_hub_stabilization_check(params)
return {}
def _bounded_int(
value: Any,
*,
default: int,
minimum: int,
maximum: int,
) -> int:
try:
parsed = int(value)
except (TypeError, ValueError):
return default
return max(minimum, min(maximum, parsed))
def _probe_status(
client: httpx.Client,
path: str,
expected_status: int,
) -> dict[str, Any]:
try:
response = client.get(path)
except Exception as exc: # pragma: no cover - network dependent
return {
"pass": False,
"path": path,
"expected_status": expected_status,
"error": str(exc),
}
return {
"pass": response.status_code == expected_status,
"path": path,
"expected_status": expected_status,
"status_code": response.status_code,
}
def _widget_type_count(client: httpx.Client, minimum: int) -> dict[str, Any]:
try:
response = client.get("/api/v2/widget-types")
except Exception as exc: # pragma: no cover - network dependent
return {
"pass": False,
"minimum": minimum,
"count": None,
"error": str(exc),
}
if response.status_code != 200:
return {
"pass": False,
"minimum": minimum,
"count": None,
"status_code": response.status_code,
}
try:
payload = response.json()
except ValueError as exc:
return {
"pass": False,
"minimum": minimum,
"count": None,
"error": f"invalid JSON: {exc}",
}
if isinstance(payload, list):
count = len(payload)
elif isinstance(payload, dict) and isinstance(payload.get("data"), list):
count = len(payload["data"])
else:
count = 0
return {
"pass": count >= minimum,
"minimum": minimum,
"count": count,
}
def _core_hub_stabilization_check(params: dict[str, Any]) -> dict[str, Any]:
closeout = bool(params.get("closeout"))
now = _utc_now()
window_start = _parse_iso_datetime(
params.get("window_start", "2026-07-03T00:00:00+00:00")
)
window_end = _parse_iso_datetime(
params.get("window_end", "2026-07-10T17:35:00+00:00")
)
within_window = bool(
window_start and window_end and window_start <= now <= window_end
)
if not within_window and not closeout:
return {
"kind": "core_hub_stabilization",
"skipped": True,
"reason": "outside_stabilization_window",
"closeout": closeout,
"overall_pass": True,
"operator_signoff_needed": False,
"base_url": str(params.get("base_url") or os.environ.get("CORE_HUB_BASE_URL") or _DEFAULT_BASE_URL),
"window": {
"start": window_start.isoformat() if window_start else None,
"end": window_end.isoformat() if window_end else None,
"within": within_window,
},
"checks": {},
}
base_url = str(
params.get("base_url")
or os.environ.get("CORE_HUB_BASE_URL")
or _DEFAULT_BASE_URL
).rstrip("/")
timeout_seconds = float(
params.get("timeout_seconds", _DEFAULT_TIMEOUT_SECONDS)
)
min_widget_types = _bounded_int(
params.get("min_widget_types", 26),
default=26,
minimum=1,
maximum=200,
)
checks: dict[str, Any] = {}
with httpx.Client(
base_url=base_url,
timeout=timeout_seconds,
headers={"Accept": "application/json", "User-Agent": "activity-core/core-hub-stabilization/0.1"},
follow_redirects=True,
) as client:
for path, expected_status in _PUBLIC_CHECKS:
key = path.strip("/").replace("/", "_").replace("-", "_") or "root"
checks[key] = _probe_status(client, path, expected_status)
checks["widget_types_count"] = _widget_type_count(client, min_widget_types)
overall_pass = all(section.get("pass") for section in checks.values())
result = {
"kind": "core_hub_stabilization",
"skipped": False,
"closeout": closeout,
"overall_pass": overall_pass,
"operator_signoff_needed": closeout and overall_pass,
"base_url": base_url,
"window": {
"start": window_start.isoformat() if window_start else None,
"end": window_end.isoformat() if window_end else None,
"within": within_window,
},
"checks": checks,
"source": str(params.get("source") or "activity-core"),
}
if closeout and not overall_pass:
raise RuntimeError(f"core hub stabilization closeout failed: {result}")
return result
CONTEXT_RESOLVER_REGISTRY["core-hub"] = CoreHubContextResolver

View file

@ -123,7 +123,11 @@ def _post_state_hub_progress(
"context_key": context_key,
}
if probe_result.get("checks") is not None:
if probe_result.get("kind") == "core_hub_stabilization":
compact = probe_result
summary = _core_hub_stabilization_summary_text(probe_result)
source_type = "core-hub"
elif probe_result.get("checks") is not None:
compact = probe_result
summary = _phase5_summary_text(probe_result)
source_type = "state-hub"
@ -503,6 +507,19 @@ def _compact_access_path(access_path: dict[str, Any]) -> dict[str, Any]:
}
def _core_hub_stabilization_summary_text(result: dict[str, Any]) -> str:
checks = result.get("checks") or {}
widget_types = checks.get("widget_types_count") or {}
status = "pass" if result.get("overall_pass") else "fail"
mode = "closeout" if result.get("closeout") else "daily"
return (
f"Core Hub stabilization {mode}: {status}; "
f"base={result.get('base_url', '?')}; "
f"widget_types={widget_types.get('count', '?')}/"
f"{widget_types.get('minimum', '?')}"
)
def _phase5_summary_text(result: dict[str, Any]) -> str:
checks = result.get("checks") or {}
totals = checks.get("totals") or {}