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

@ -10,6 +10,7 @@ from reuse_surface.plan_check import (
load_query_from_workplan,
maybe_file_capability_request,
match_query,
record_manual_reuse_event,
record_outcome,
request_rerank,
run_plan_check,
@ -133,6 +134,83 @@ def test_record_outcome_appends_jsonl(tmp_path, monkeypatch):
assert event["source"] == "plan-check"
def test_record_outcome_posts_to_hub_when_reachable(tmp_path, monkeypatch):
telemetry_path = tmp_path / "plan-check-events.jsonl"
monkeypatch.setattr("reuse_surface.plan_check.TELEMETRY_PATH", telemetry_path)
monkeypatch.setattr(
"reuse_surface.hub_client.hub_record_reuse_event",
lambda event, base_url=None: (201, event),
)
result = {"verdict": "reuse", "matches": [{"id": "capability.infotech.issue-tracking"}]}
recorded = record_outcome(result, "reused", consumer_repo="some-repo")
assert recorded["recorded_to"] == "hub"
assert not telemetry_path.exists()
def test_record_outcome_falls_back_when_hub_rejects(tmp_path, monkeypatch):
telemetry_path = tmp_path / "plan-check-events.jsonl"
monkeypatch.setattr("reuse_surface.plan_check.TELEMETRY_PATH", telemetry_path)
monkeypatch.setattr(
"reuse_surface.hub_client.hub_record_reuse_event",
lambda event, base_url=None: (500, {"error": "boom"}),
)
result = {"verdict": "reuse", "matches": [{"id": "capability.infotech.issue-tracking"}]}
recorded = record_outcome(result, "reused", consumer_repo="some-repo")
assert recorded["recorded_to"] == "local"
assert telemetry_path.exists()
def test_record_outcome_falls_back_when_hub_unconfigured(tmp_path, monkeypatch):
monkeypatch.delenv("REUSE_SURFACE_URL", raising=False)
telemetry_path = tmp_path / "plan-check-events.jsonl"
monkeypatch.setattr("reuse_surface.plan_check.TELEMETRY_PATH", telemetry_path)
result = {"verdict": "reuse", "matches": [{"id": "capability.infotech.issue-tracking"}]}
recorded = record_outcome(result, "reused", consumer_repo="some-repo")
assert recorded["recorded_to"] == "local"
def test_record_manual_reuse_event_local_fallback(tmp_path, monkeypatch):
monkeypatch.delenv("REUSE_SURFACE_URL", raising=False)
telemetry_path = tmp_path / "plan-check-events.jsonl"
monkeypatch.setattr("reuse_surface.plan_check.TELEMETRY_PATH", telemetry_path)
recorded = record_manual_reuse_event(
consumer_repo="some-repo",
capability_id="capability.infotech.issue-tracking",
verdict="reuse",
outcome="reused",
)
assert recorded["recorded_to"] == "local"
event = json.loads(telemetry_path.read_text().splitlines()[0])
assert event["source"] == "manual"
assert event["consumer_repo"] == "some-repo"
def test_record_manual_reuse_event_posts_to_hub(tmp_path, monkeypatch):
telemetry_path = tmp_path / "plan-check-events.jsonl"
monkeypatch.setattr("reuse_surface.plan_check.TELEMETRY_PATH", telemetry_path)
monkeypatch.setattr(
"reuse_surface.hub_client.hub_record_reuse_event",
lambda event, base_url=None: (201, event),
)
recorded = record_manual_reuse_event(
consumer_repo="some-repo", capability_id=None, verdict="new", outcome="new",
)
assert recorded["recorded_to"] == "hub"
assert recorded["event"]["source"] == "manual"
assert recorded["event"]["capability_id"] is None
def test_record_manual_reuse_event_rejects_invalid_verdict(monkeypatch):
monkeypatch.delenv("REUSE_SURFACE_URL", raising=False)
try:
record_manual_reuse_event(
consumer_repo="some-repo", capability_id=None, verdict="not-a-verdict",
)
assert False, "expected ValueError"
except ValueError as exc:
assert "schema validation failed" in str(exc)
def test_maybe_file_capability_request_only_on_new_verdict(monkeypatch):
called = []
monkeypatch.setattr(
@ -349,3 +427,59 @@ def test_run_plan_check_integrates_successful_rerank(monkeypatch):
assert llm_entries[0]["score"] == 0.77
# deterministic top match is still first and unaffected
assert result["matches"][0]["kind"] == "deterministic"
def test_cmd_record_reuse_cli_local_fallback(tmp_path, monkeypatch, capsys):
from reuse_surface.cli import main
monkeypatch.delenv("REUSE_SURFACE_URL", raising=False)
telemetry_path = tmp_path / "plan-check-events.jsonl"
monkeypatch.setattr("reuse_surface.plan_check.TELEMETRY_PATH", telemetry_path)
exit_code = main([
"record-reuse",
"--consumer-repo", "some-repo",
"--capability-id", "capability.infotech.issue-tracking",
"--verdict", "reuse",
"--outcome", "reused",
])
assert exit_code == 0
out = capsys.readouterr().out
assert "local" in out
event = json.loads(telemetry_path.read_text().splitlines()[0])
assert event["consumer_repo"] == "some-repo"
assert event["source"] == "manual"
def test_cmd_record_reuse_cli_rejects_invalid_verdict():
import pytest
from reuse_surface.cli import main
with pytest.raises(SystemExit) as exc_info:
main([
"record-reuse",
"--consumer-repo", "some-repo",
"--verdict", "not-a-verdict",
])
assert exc_info.value.code == 2 # argparse choices rejection
def test_cmd_record_reuse_cli_json_format(tmp_path, monkeypatch, capsys):
from reuse_surface.cli import main
monkeypatch.delenv("REUSE_SURFACE_URL", raising=False)
telemetry_path = tmp_path / "plan-check-events.jsonl"
monkeypatch.setattr("reuse_surface.plan_check.TELEMETRY_PATH", telemetry_path)
exit_code = main([
"record-reuse",
"--consumer-repo", "some-repo",
"--verdict", "new",
"--format", "json",
])
assert exit_code == 0
out = capsys.readouterr().out
payload = json.loads(out)
assert payload["recorded_to"] == "local"
assert payload["event"]["capability_id"] is None