feat(ACTIVITY-WP-0029): inventory callers, retarget sweep, bound execution
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 32s

Map every State Hub/core-hub caller to a post-retirement owner. Keep the
15-minute sweep schedule here and point the engine at repo-manager
(State Hub dual-run by default, REPO_MANAGER_URL when present). Publish
GET /execution/semantics and 410 workplan launch routes so State Hub
/execution/* is not re-homed as a task database. T03 still waits on
HUB-WP-0004.
This commit is contained in:
tegwick 2026-08-18 10:52:56 +02:00
parent df3330f165
commit f6cfc28c33
12 changed files with 359 additions and 12 deletions

View file

@ -14,7 +14,10 @@ Supported queries:
- coding_retro: latest /progress/ item with event_type=coding_retro
- daily_triage_digest: curated scalar JSON digest for daily WSJF triage
- recently_on_scope_hourly: POST {STATE_HUB_URL}/recently-on-scope/hourly
- consistency_sweep_remote_all: POST {STATE_HUB_URL}/consistency/sweep/remote-all
- consistency_sweep_remote_all: POST {sweep_base}/consistency/sweep/remote-all
sweep_base = CONSISTENCY_SWEEP_URL or REPO_MANAGER_URL or STATE_HUB_URL
(ACTIVITY-WP-0029: engine is repo-manager; State Hub is the dual-run
adapter until REPO_MANAGER_URL is set)
- phase5_stabilization_check: hub-visible Phase 5 stabilization gates
- legacy_meter_weekly_review: GET {STATE_HUB_URL}/legacy-meter/weekly-review
- binky_rhythm_status: due-items for the Binky operating-rhythm definitions,
@ -68,6 +71,33 @@ def _base_url() -> str:
return os.environ.get("STATE_HUB_URL", _DEFAULT_STATE_HUB_URL).rstrip("/")
def _sweep_target() -> tuple[str, str, str]:
"""Return (base_url, path, adapter) for the consistency sweep POST.
Engine owner is always repo-manager. Default adapter is State Hub's
dual-run ``POST /consistency/sweep/remote-all``. Point
``CONSISTENCY_SWEEP_URL`` or ``REPO_MANAGER_URL`` at repo-manager when
that HTTP surface exists.
"""
path = (os.environ.get("CONSISTENCY_SWEEP_PATH") or "/consistency/sweep/remote-all").strip()
if not path.startswith("/"):
path = f"/{path}"
explicit = (os.environ.get("CONSISTENCY_SWEEP_URL") or "").strip().rstrip("/")
repo_mgr = (os.environ.get("REPO_MANAGER_URL") or "").strip().rstrip("/")
state_hub = _base_url()
if explicit:
if repo_mgr and explicit == repo_mgr:
adapter = "repo-manager"
elif explicit == state_hub:
adapter = "state-hub-dual-run"
else:
adapter = "explicit"
return explicit, path, adapter
if repo_mgr:
return repo_mgr, path, "repo-manager"
return state_hub, path, "state-hub-dual-run"
def _fetch_json(path: str, params: dict[str, Any] | None = None) -> Any:
url = f"{_base_url()}{path}"
try:
@ -104,9 +134,10 @@ def _post_json(
*,
timeout: float = _TIMEOUT_SECONDS,
retries: int | None = None,
base_url: str | None = None,
) -> Any:
"""POST JSON with retries on transient edge/hub failures (ACTIVITY-WP-0027)."""
url = f"{_base_url()}{path}"
url = f"{(base_url or _base_url()).rstrip('/')}{path}"
attempts = _post_retry_attempts() if retries is None else max(1, retries)
backoff = _post_retry_backoff_seconds()
last_exc: Exception | None = None
@ -261,11 +292,13 @@ class StateHubContextResolver(ContextResolver):
for key, value in params.items()
if key not in {"required", "degrade_on_unavailable"}
}
sweep_base, sweep_path, adapter = _sweep_target()
try:
result = _post_json(
"/consistency/sweep/remote-all",
sweep_path,
payload,
timeout=_SWEEP_TIMEOUT_SECONDS,
base_url=sweep_base,
)
except (httpx.HTTPError, ValueError) as exc:
if not _want_degrade(params):
@ -283,8 +316,15 @@ class StateHubContextResolver(ContextResolver):
"skipped_budget": [],
"degraded": True,
"degraded_reason": str(exc)[:300],
"engine_owner": "repo-manager",
"adapter": adapter,
"sweep_url": f"{sweep_base}{sweep_path}",
}
return _validate_consistency_sweep_remote_all(result)
validated = _validate_consistency_sweep_remote_all(result)
validated.setdefault("engine_owner", "repo-manager")
validated.setdefault("adapter", adapter)
validated.setdefault("sweep_url", f"{sweep_base}{sweep_path}")
return validated
if query == "phase5_stabilization_check":
return _phase5_stabilization_check(params)
if query == "legacy_meter_weekly_review":