REUSE-WP-0018 T01/T02/T04/T06: plan-check deterministic matching + State Hub bridge
Some checks failed
ci / validate-registry (push) Has been cancelled

T01: specs/PlanCheck.md design doc, plan-check-result.schema.json and
reuse-event.schema.json (the latter shared with WP-0019's reuse telemetry).

T02: reuse_surface/plan_check.py + 'reuse-surface plan-check' CLI command.
Deterministic token-Jaccard matching against registry/indexes/federated.yaml
(reuses overlaps.py's TOKEN_RE rather than a second scoring method), with
reuse/extend/new verdicts, markdown and --format json output, staleness
warning, and --record-outcome JSONL telemetry.

T04: reuse_surface/statehub_bridge.py bridges plan-check 'new' verdicts to
State Hub capability requests (--file-request) and surfaces open requests
with no matching capability (report gaps --check-capability-requests, opt-in
to stay offline-safe). Verified against the live local State Hub API;
status field (not catalog_entry_id presence) is the correct open/closed
signal, and the list endpoint needs a longer timeout than the health check
(~7s observed with 5 rows).

T06: docs (tools/README.md, RegistryFederation.md, SCOPE.md,
IntentScopeGapAnalysis.md priority 29) and an informational CI smoke step.

T03 (LLM rerank) not started -- llm-connect isn't running on this
workstation. T05 (ecosystem rollout) remains blocked: WP-0017 has drafted
entries for all 61 repos but they're still local-only pending its own T05
push/publish pass, so the federated index isn't yet worth rolling out
plan-check as ecosystem convention.

16 new tests, all mocked -- no network calls in the default test run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 00:57:18 +02:00
parent 2a6818d4fe
commit e72966a658
15 changed files with 1179 additions and 29 deletions

View File

@@ -0,0 +1,103 @@
from __future__ import annotations
import json
import urllib.error
import urllib.parse
import urllib.request
from typing import Any
DEFAULT_BASE_URL = "http://127.0.0.1:8000"
TIMEOUT_SECONDS = 5
LIST_TIMEOUT_SECONDS = 20
# Observed status vocabulary (2026-07): "requested", "completed". No fixed
# enum is published by the API (free-form string field) -- treat anything
# not in this terminal set as still-open rather than assuming a closed list.
TERMINAL_STATUSES = {"completed", "cancelled", "rejected", "resolved", "closed"}
def _request(
method: str,
path: str,
base_url: str,
*,
body: dict[str, Any] | None = None,
params: dict[str, str] | None = None,
timeout: int = TIMEOUT_SECONDS,
) -> dict[str, Any] | list[Any]:
url = f"{base_url.rstrip('/')}{path}"
if params:
query = "&".join(
f"{k}={urllib.parse.quote(v)}" for k, v in params.items() if v
)
if query:
url = f"{url}?{query}"
data = json.dumps(body).encode("utf-8") if body is not None else None
request = urllib.request.Request(
url,
data=data,
method=method,
headers={"Content-Type": "application/json", "User-Agent": "reuse-surface/0.1"},
)
with urllib.request.urlopen(request, timeout=timeout) as response:
return json.loads(response.read().decode("utf-8"))
def state_hub_reachable(base_url: str = DEFAULT_BASE_URL) -> bool:
try:
_request("GET", "/state/health", base_url)
return True
except (urllib.error.URLError, TimeoutError, OSError):
return False
def file_capability_request(
*,
title: str,
description: str,
requesting_domain: str,
requesting_agent: str,
capability_type: str = "reuse-surface-new-verdict",
requesting_workplan_id: str | None = None,
base_url: str = DEFAULT_BASE_URL,
) -> dict[str, Any] | None:
"""Files a State Hub capability request for a plan-check 'new' verdict.
Returns the created request record, or None if the hub is unreachable
(degrades gracefully -- plan-check never blocks on this)."""
if not state_hub_reachable(base_url):
return None
body = {
"title": title,
"description": description,
"capability_type": capability_type,
"requesting_domain": requesting_domain,
"requesting_agent": requesting_agent,
"requesting_workplan_id": requesting_workplan_id,
}
try:
return _request("POST", "/capability-requests/", base_url, body=body)
except (urllib.error.URLError, TimeoutError, OSError):
return None
def list_open_capability_requests(
base_url: str = DEFAULT_BASE_URL,
) -> list[dict[str, Any]] | None:
"""Returns capability requests whose status is not terminal (see
TERMINAL_STATUSES), or None if the hub is unreachable. A request can
carry a catalog_entry_id while still 'requested' (routed, not
fulfilled), so status -- not catalog_entry_id presence -- is the open
signal. The list endpoint can be slow (observed ~7s with a handful of
rows); a longer timeout than the health check is used deliberately."""
if not state_hub_reachable(base_url):
return None
try:
requests_ = _request(
"GET", "/capability-requests/", base_url, timeout=LIST_TIMEOUT_SECONDS
)
except (urllib.error.URLError, TimeoutError, OSError):
return None
if not isinstance(requests_, list):
return []
return [r for r in requests_ if r.get("status") not in TERMINAL_STATUSES]