From aedea973abcbc1bf3a237ad4a5b1ade084891088 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 20 Aug 2026 23:29:03 +0000 Subject: [PATCH] fix(webhook): label re-read must hit /api/v1, not the bare host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GITEA_API is the host with no version prefix — ai_review._get / _post append /api/v1 per call. The re-read helper didn't, so it 404'd on every review and silently fell back to the payload's verdict: "could not re-read labels for gitea_admin/pragent#10: HTTP Error 404" in the pod log, usage block still missing. Caught by labelling PR #10 AI-REVIEW then AI-USAGE, which is the exact sequence the fix exists to handle. Test asserts the composed URL, since a wrong path here fails silently by design (the helper swallows errors so a review is never lost over a usage section). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01B11e8TZZxJyzHW7jj7KWUN --- pilot/webhook_server.py | 4 +++- tests/pilot/test_webhook_server.py | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/pilot/webhook_server.py b/pilot/webhook_server.py index 5a7ecfd..e756cd9 100644 --- a/pilot/webhook_server.py +++ b/pilot/webhook_server.py @@ -186,7 +186,9 @@ def _fetch_current_labels(repo: str, index: str) -> list: opt-in is lost — the review posts without its usage block. Reading the labels again at review start closes that window. """ - url = f"{GITEA_API}/repos/{repo}/issues/{index}/labels" + # GITEA_API is the bare host (no /api/v1) — every caller appends the + # version prefix itself; see ai_review._get / _post. + url = f"{GITEA_API}/api/v1/repos/{repo}/issues/{index}/labels" req = urllib.request.Request(url, headers={ "Authorization": f"token {BOT_TOKEN}", "Accept": "application/json", diff --git a/tests/pilot/test_webhook_server.py b/tests/pilot/test_webhook_server.py index 33d3461..fd0386a 100644 --- a/tests/pilot/test_webhook_server.py +++ b/tests/pilot/test_webhook_server.py @@ -162,6 +162,27 @@ def test_run_review_keeps_usage_off_when_label_absent(monkeypatch): assert seen["report_usage"] is False +def test_fetch_current_labels_hits_the_versioned_api_path(monkeypatch): + # GITEA_API is the bare host; the /api/v1 prefix is the caller's job. + # Getting this wrong 404s silently and the opt-in is lost — which is + # exactly what shipped the first time. + seen = {} + + class _Resp: + def read(self): return b'[{"name": "AI-USAGE"}]' + def __enter__(self): return self + def __exit__(self, *a): return False + + def _urlopen(req, timeout=None): + seen["url"] = req.full_url + return _Resp() + + monkeypatch.setattr(ws.urllib.request, "urlopen", _urlopen) + out = ws._fetch_current_labels("o/r", "9") + assert out == [{"name": "AI-USAGE"}] + assert seen["url"] == f"{ws.GITEA_API}/api/v1/repos/o/r/issues/9/labels" + + def test_label_reread_failure_is_not_fatal(monkeypatch): # A dead API must not take the review down with it — the re-read is a # best-effort upgrade of an opt-in flag, nothing more.