generated from coulomb/repo-seed
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>
192 lines
5.7 KiB
Python
192 lines
5.7 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
|
|
from pathlib import Path
|
|
|
|
from reuse_surface.reports import (
|
|
cohort_filters_from_args,
|
|
collect_gap_report,
|
|
format_cohort_json,
|
|
format_cohort_markdown,
|
|
format_gap_markdown,
|
|
select_cohort,
|
|
)
|
|
|
|
|
|
SAMPLE_INDEX = {
|
|
"capabilities": [
|
|
{
|
|
"id": "capability.planning.only",
|
|
"vector": "D5 / A0 / C2 / R1",
|
|
"domain": "helix_forge",
|
|
"consumption_modes": ["planning"],
|
|
},
|
|
{
|
|
"id": "capability.implementation.ready",
|
|
"vector": "D5 / A4 / C3 / R3",
|
|
"domain": "helix_forge",
|
|
"consumption_modes": ["cli", "service API"],
|
|
},
|
|
{
|
|
"id": "capability.other.domain",
|
|
"vector": "D4 / A3 / C2 / R2",
|
|
"domain": "other",
|
|
"consumption_modes": ["cli"],
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
def test_planning_min_filter():
|
|
filters = cohort_filters_from_args(
|
|
argparse.Namespace(
|
|
planning_min="D5",
|
|
implementation_min=None,
|
|
discovery_min=None,
|
|
availability_min=None,
|
|
availability_max=None,
|
|
domain=None,
|
|
)
|
|
)
|
|
matches = select_cohort(filters, SAMPLE_INDEX)
|
|
assert [item["id"] for item in matches] == ["capability.planning.only"]
|
|
|
|
|
|
def test_implementation_min_filter():
|
|
filters = cohort_filters_from_args(
|
|
argparse.Namespace(
|
|
planning_min=None,
|
|
implementation_min="A4",
|
|
discovery_min=None,
|
|
availability_min=None,
|
|
availability_max=None,
|
|
domain=None,
|
|
)
|
|
)
|
|
matches = select_cohort(filters, SAMPLE_INDEX)
|
|
assert [item["id"] for item in matches] == ["capability.implementation.ready"]
|
|
|
|
|
|
def test_domain_filter():
|
|
filters = {"discovery_min": None, "availability_min": None, "availability_max": None, "domain": "helix_forge"}
|
|
matches = select_cohort(filters, SAMPLE_INDEX)
|
|
assert len(matches) == 2
|
|
|
|
|
|
def test_format_cohort_markdown_includes_filters():
|
|
filters = {"discovery_min": "D5", "availability_min": None, "availability_max": "A1", "domain": None}
|
|
text = format_cohort_markdown([SAMPLE_INDEX["capabilities"][0]], filters)
|
|
assert "planning-min" not in text
|
|
assert "discovery_min" in text
|
|
assert "capability.planning.only" in text
|
|
|
|
|
|
def test_format_cohort_json_payload():
|
|
filters = {"discovery_min": "D5", "availability_min": None, "availability_max": "A1", "domain": None}
|
|
payload = json.loads(
|
|
format_cohort_json([SAMPLE_INDEX["capabilities"][0]], filters)
|
|
)
|
|
assert payload["count"] == 1
|
|
assert payload["filters"]["discovery_min"] == "D5"
|
|
|
|
|
|
def test_collect_gap_report_from_roster():
|
|
root = Path(__file__).resolve().parent.parent
|
|
roster = root / "registry/federation/local-repo-roster.yaml"
|
|
report = collect_gap_report(roster)
|
|
assert report["summary"]["total"] == 61
|
|
assert len(report["publish_fail"]) == 0
|
|
assert report["unclassified_count"] + report["explicit_none_count"] == report["empty_scaffold_count"]
|
|
assert "/" in report["coverage_ratio"]
|
|
|
|
|
|
def test_collect_gap_report_splits_unclassified_and_explicit_none(tmp_path):
|
|
roster_path = tmp_path / "roster.yaml"
|
|
roster_path.write_text(
|
|
"""
|
|
summary:
|
|
total: 3
|
|
repos:
|
|
- slug: has-repo
|
|
status: established
|
|
capability_count: 1
|
|
capability_status: has
|
|
- slug: none-repo
|
|
status: established
|
|
capability_count: 0
|
|
capability_status: none
|
|
- slug: pending-repo
|
|
status: established
|
|
capability_count: 0
|
|
capability_status: pending
|
|
"""
|
|
)
|
|
report = collect_gap_report(roster_path, index={"capabilities": []})
|
|
assert report["unclassified"] == ["pending-repo"]
|
|
assert report["explicit_none"] == ["none-repo"]
|
|
assert report["empty_scaffold_count"] == 2
|
|
assert report["coverage_ratio"] == "2/3"
|
|
|
|
|
|
def test_format_gap_markdown_lists_publish_fail():
|
|
report = {
|
|
"roster_path": "/tmp/roster.yaml",
|
|
"summary": {"total": 60, "established": 60, "publish_pass": 48},
|
|
"publish_fail": [{"slug": "inter-hub", "publish_note": "missing repo"}],
|
|
"empty_scaffold_count": 1,
|
|
"empty_scaffolds": ["ops-bridge"],
|
|
"unclassified_count": 1,
|
|
"unclassified": ["ops-bridge"],
|
|
"explicit_none_count": 0,
|
|
"explicit_none": [],
|
|
"coverage_ratio": "59/60",
|
|
"seeded_repos": [],
|
|
"dedup_pending_local_owners": [],
|
|
"local_capability_count": 2,
|
|
}
|
|
text = format_gap_markdown(report)
|
|
assert "inter-hub" in text
|
|
assert "ops-bridge" in text
|
|
|
|
|
|
def test_cmd_report_gaps_json(monkeypatch):
|
|
from reuse_surface.cli import main
|
|
|
|
exit_code = main(["report", "gaps", "--format", "json"])
|
|
assert exit_code == 0
|
|
|
|
|
|
def test_cmd_report_gaps_check_capability_requests_unreachable(monkeypatch):
|
|
from reuse_surface.cli import main
|
|
|
|
monkeypatch.setattr(
|
|
"reuse_surface.cli.list_open_capability_requests", lambda: None
|
|
)
|
|
exit_code = main(["report", "gaps", "--check-capability-requests"])
|
|
assert exit_code == 0
|
|
|
|
|
|
def test_cmd_report_gaps_check_capability_requests_json(monkeypatch):
|
|
from reuse_surface.cli import main
|
|
|
|
monkeypatch.setattr(
|
|
"reuse_surface.cli.list_open_capability_requests",
|
|
lambda: [{"id": "r1", "title": "Need X", "requesting_domain_slug": "infotech"}],
|
|
)
|
|
exit_code = main(
|
|
["report", "gaps", "--check-capability-requests", "--format", "json"]
|
|
)
|
|
assert exit_code == 0
|
|
|
|
|
|
def test_cmd_report_cohorts_markdown(monkeypatch):
|
|
from reuse_surface.cli import main
|
|
|
|
monkeypatch.setattr(
|
|
"reuse_surface.reports.load_index",
|
|
lambda: SAMPLE_INDEX,
|
|
)
|
|
exit_code = main(["report", "cohorts", "--planning-min", "D5"])
|
|
assert exit_code == 0 |