generated from coulomb/repo-seed
REUSE-WP-0018 T01/T02/T04/T06: plan-check deterministic matching + State Hub bridge
Some checks failed
ci / validate-registry (push) Has been cancelled
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:
@@ -20,6 +20,16 @@ from reuse_surface.hub_sync import (
|
||||
write_sources_manifest,
|
||||
)
|
||||
from reuse_surface.overlaps import find_overlaps
|
||||
from reuse_surface.statehub_bridge import list_open_capability_requests
|
||||
from reuse_surface.plan_check import (
|
||||
format_plan_check_json,
|
||||
format_plan_check_markdown,
|
||||
load_query_from_intent,
|
||||
load_query_from_workplan,
|
||||
maybe_file_capability_request,
|
||||
record_outcome,
|
||||
run_plan_check,
|
||||
)
|
||||
from reuse_surface.reports import (
|
||||
cohort_filters_from_args,
|
||||
collect_gap_report,
|
||||
@@ -578,16 +588,85 @@ def cmd_report_cohorts(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_plan_check(args: argparse.Namespace) -> int:
|
||||
if args.workplan and args.intent:
|
||||
print("error: pass either a workplan file or --intent, not both", file=sys.stderr)
|
||||
return 1
|
||||
if args.workplan:
|
||||
path = Path(args.workplan).resolve()
|
||||
if not path.exists():
|
||||
print(f"error: workplan not found: {path}", file=sys.stderr)
|
||||
return 1
|
||||
query = load_query_from_workplan(path)
|
||||
elif args.intent:
|
||||
query = load_query_from_intent(args.intent)
|
||||
else:
|
||||
print("error: provide a workplan file or --intent", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
result = run_plan_check(
|
||||
query,
|
||||
reuse_threshold=args.reuse_threshold,
|
||||
extend_threshold=args.extend_threshold,
|
||||
)
|
||||
|
||||
if args.record_outcome:
|
||||
record_outcome(result, args.record_outcome, consumer_repo=args.consumer_repo)
|
||||
|
||||
filed = None
|
||||
if args.file_request and result["verdict"] == "new":
|
||||
filed = maybe_file_capability_request(
|
||||
result,
|
||||
requesting_domain=args.requesting_domain,
|
||||
requesting_agent=args.consumer_repo,
|
||||
)
|
||||
|
||||
if args.format == "json":
|
||||
if filed is not None:
|
||||
result["filed_capability_request"] = filed
|
||||
print(format_plan_check_json(result))
|
||||
else:
|
||||
print(format_plan_check_markdown(result), end="")
|
||||
if args.file_request and result["verdict"] == "new":
|
||||
if filed:
|
||||
print(f"\nFiled State Hub capability request: {filed.get('id')}")
|
||||
else:
|
||||
print("\nState Hub unreachable — capability request not filed.")
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_report_gaps(args: argparse.Namespace) -> int:
|
||||
roster_path = Path(args.roster).resolve() if args.roster else default_roster_path()
|
||||
if not roster_path.exists():
|
||||
print(f"error: roster not found: {roster_path}", file=sys.stderr)
|
||||
return 1
|
||||
report = collect_gap_report(roster_path)
|
||||
|
||||
if args.check_capability_requests:
|
||||
open_requests = list_open_capability_requests()
|
||||
report["open_capability_requests"] = (
|
||||
[
|
||||
{"id": r["id"], "title": r["title"], "requesting_domain": r.get("requesting_domain_slug")}
|
||||
for r in open_requests
|
||||
]
|
||||
if open_requests is not None
|
||||
else None
|
||||
)
|
||||
|
||||
if args.format == "json":
|
||||
print(format_gap_json(report))
|
||||
else:
|
||||
print(format_gap_markdown(report), end="")
|
||||
if args.check_capability_requests:
|
||||
requests_ = report["open_capability_requests"]
|
||||
print("\n## Open State Hub capability requests (no matching federated capability)\n")
|
||||
if requests_ is None:
|
||||
print("_State Hub unreachable — skipped._\n")
|
||||
elif not requests_:
|
||||
print("- none\n")
|
||||
else:
|
||||
for r in requests_:
|
||||
print(f"- `{r['id']}` ({r['requesting_domain']}): {r['title']}")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -697,6 +776,44 @@ def main(argv: list[str] | None = None) -> int:
|
||||
)
|
||||
overlaps.set_defaults(func=cmd_overlaps)
|
||||
|
||||
plan_check = subparsers.add_parser(
|
||||
"plan-check",
|
||||
help="match a draft workplan or intent against the federated capability index",
|
||||
)
|
||||
plan_check.add_argument(
|
||||
"workplan", nargs="?", help="path to a workplan file (frontmatter + body)"
|
||||
)
|
||||
plan_check.add_argument("--intent", help="free-text intent instead of a workplan file")
|
||||
plan_check.add_argument(
|
||||
"--format", choices=["markdown", "json"], default="markdown"
|
||||
)
|
||||
plan_check.add_argument(
|
||||
"--reuse-threshold", type=float, default=0.45,
|
||||
help="score at or above which the verdict is 'reuse'",
|
||||
)
|
||||
plan_check.add_argument(
|
||||
"--extend-threshold", type=float, default=0.22,
|
||||
help="score at or above which the verdict is 'extend'",
|
||||
)
|
||||
plan_check.add_argument(
|
||||
"--record-outcome",
|
||||
choices=["reused", "extended", "new", "skipped"],
|
||||
help="append this outcome to registry/telemetry/plan-check-events.jsonl",
|
||||
)
|
||||
plan_check.add_argument(
|
||||
"--consumer-repo", default="reuse-surface",
|
||||
help="repo slug recorded with --record-outcome and --file-request",
|
||||
)
|
||||
plan_check.add_argument(
|
||||
"--file-request", action="store_true",
|
||||
help="on a 'new' verdict, file a State Hub capability request for the gap",
|
||||
)
|
||||
plan_check.add_argument(
|
||||
"--requesting-domain", default="infotech",
|
||||
help="domain slug recorded on a filed capability request",
|
||||
)
|
||||
plan_check.set_defaults(func=cmd_plan_check)
|
||||
|
||||
catalog = subparsers.add_parser(
|
||||
"catalog", help="generate human-readable capability catalog"
|
||||
)
|
||||
@@ -808,6 +925,12 @@ def main(argv: list[str] | None = None) -> int:
|
||||
choices=["markdown", "json"],
|
||||
default="markdown",
|
||||
)
|
||||
gaps.add_argument(
|
||||
"--check-capability-requests",
|
||||
action="store_true",
|
||||
help="also list open State Hub capability requests with no matching "
|
||||
"federated capability (requires State Hub reachable at 127.0.0.1:8000)",
|
||||
)
|
||||
gaps.set_defaults(func=cmd_report_gaps)
|
||||
|
||||
stats = subparsers.add_parser("stats", help="registry maturity and federation stats")
|
||||
|
||||
Reference in New Issue
Block a user