REUSE-WP-0019-T04: reuse telemetry store and recording
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 10s
ci / validate-registry (push) Has been cancelled
Build and Publish Container Image / build-and-push (push) Successful in 1m16s

Implements the hub side of the shared reuse-event schema (already drafted
in WP-0018-T01, schemas/reuse-event.schema.json): a SQLite reuse_events
table, POST /v1/reuse-events (token-auth), GET /v1/reuse-events?capability_id=
(read-only).

reuse_surface/plan_check.py: refactored record_outcome around a new
shared post_or_fallback_reuse_event() helper -- tries the hub first, falls
back to the local JSONL only on failure/unreachability, never both. New
record_manual_reuse_event() backs a new CLI command, reuse-surface
record-reuse, for retroactive facts recorded outside plan-check.

Privacy/scope (repo slugs and capability ids only, no code, no secrets) is
enforced structurally via the schema's additionalProperties: false, not
just by convention.

21 new pytest cases, 145 total pass. Live-verified against a real running
hub instance: POST/GET /v1/reuse-events directly, record-reuse and
plan-check --record-outcome both posting successfully to the hub, and --
after actually killing the hub process -- confirmed the fallback path
writes correctly to the local JSONL instead of erroring.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-07-07 22:32:19 +02:00
parent 2fcc91f2aa
commit d181043717
12 changed files with 562 additions and 32 deletions

View file

@ -28,6 +28,7 @@ from reuse_surface.plan_check import (
load_query_from_intent,
load_query_from_workplan,
maybe_file_capability_request,
record_manual_reuse_event,
record_outcome,
run_plan_check,
)
@ -663,8 +664,9 @@ def cmd_plan_check(args: argparse.Namespace) -> int:
llm_url=args.llm_url,
)
recorded = None
if args.record_outcome:
record_outcome(result, args.record_outcome, consumer_repo=args.consumer_repo)
recorded = record_outcome(result, args.record_outcome, consumer_repo=args.consumer_repo)
filed = None
if args.file_request and result["verdict"] == "new":
@ -677,6 +679,8 @@ def cmd_plan_check(args: argparse.Namespace) -> int:
if args.format == "json":
if filed is not None:
result["filed_capability_request"] = filed
if recorded is not None:
result["recorded_reuse_event"] = recorded
print(format_plan_check_json(result))
else:
print(format_plan_check_markdown(result), end="")
@ -685,6 +689,32 @@ def cmd_plan_check(args: argparse.Namespace) -> int:
print(f"\nFiled State Hub capability request: {filed.get('id')}")
else:
print("\nState Hub unreachable — capability request not filed.")
if recorded is not None:
if recorded["recorded_to"] == "hub":
print("\nReuse event recorded to hub.")
else:
print("\nReuse hub unreachable — recorded to local JSONL fallback.")
return 0
def cmd_record_reuse(args: argparse.Namespace) -> int:
try:
recorded = record_manual_reuse_event(
consumer_repo=args.consumer_repo,
capability_id=args.capability_id,
verdict=args.verdict,
outcome=args.outcome,
hub_url=args.hub_url,
)
except ValueError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
if args.format == "json":
print(json.dumps(recorded, indent=2, sort_keys=True))
else:
destination = "hub" if recorded["recorded_to"] == "hub" else f"local ({recorded['path']})"
print(f"ok: recorded reuse event to {destination}")
return 0
@ -880,7 +910,8 @@ def main(argv: list[str] | None = None) -> int:
plan_check.add_argument(
"--record-outcome",
choices=["reused", "extended", "new", "skipped"],
help="append this outcome to registry/telemetry/plan-check-events.jsonl",
help="record this outcome as a reuse event: POST /v1/reuse-events if the "
"hub is reachable, else append to registry/telemetry/plan-check-events.jsonl",
)
plan_check.add_argument(
"--consumer-repo", default="reuse-surface",
@ -903,6 +934,26 @@ def main(argv: list[str] | None = None) -> int:
)
plan_check.set_defaults(func=cmd_plan_check)
record_reuse = subparsers.add_parser(
"record-reuse",
help="manually record a retroactive reuse fact (REUSE-WP-0019-T04)",
)
record_reuse.add_argument("--consumer-repo", required=True, help="repo slug that reused/extended/built new")
record_reuse.add_argument(
"--capability-id", help="capability id involved, if any (omit for a pure 'new' fact)"
)
record_reuse.add_argument(
"--verdict", required=True, choices=["reuse", "extend", "new"],
)
record_reuse.add_argument(
"--outcome", choices=["reused", "extended", "new", "skipped"],
)
record_reuse.add_argument(
"--hub-url", help="hub base URL (or REUSE_SURFACE_URL)",
)
record_reuse.add_argument("--format", choices=["markdown", "json"], default="markdown")
record_reuse.set_defaults(func=cmd_record_reuse)
catalog = subparsers.add_parser(
"catalog", help="generate human-readable capability catalog"
)