fix(webhook): re-read PR labels at review start so a late AI-USAGE counts
Labelling a PR is two webhook events. AI-REVIEW arrives first, the review claims (repo, index, sha) and starts; the AI-USAGE event that follows a moment later hits the in-flight dedupe and is dropped. `report_usage` was snapshotted from the first payload, which had not seen AI-USAGE yet, so the review posted without its usage block even though the label was on the PR by the time it finished — observed on gitea_admin/pragent#9 (`usage=False` in the pod log, no <details> section in the posted review). Re-read the labels from the API at review start and upgrade the flag. The read is best-effort: any failure logs and returns [], leaving the payload's verdict intact, because a usage section is not worth failing a review over. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B11e8TZZxJyzHW7jj7KWUN
This commit is contained in:
@@ -39,6 +39,7 @@ import hmac
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import urllib.request
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
|
||||
from ai_review import review_pr
|
||||
@@ -176,10 +177,46 @@ def _release(key: tuple[str, str, str]) -> None:
|
||||
_inflight.discard(key)
|
||||
|
||||
|
||||
def _fetch_current_labels(repo: str, index: str) -> list:
|
||||
"""Re-read the PR's labels from the API. [] on any failure.
|
||||
|
||||
The webhook payload is a snapshot from the instant the event fired. When a
|
||||
PR is labelled AI-REVIEW first and AI-USAGE a moment later, the review is
|
||||
already claimed and running, so the second event is deduped and the usage
|
||||
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"
|
||||
req = urllib.request.Request(url, headers={
|
||||
"Authorization": f"token {BOT_TOKEN}",
|
||||
"Accept": "application/json",
|
||||
})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
return json.loads(resp.read().decode() or "[]")
|
||||
except Exception as e:
|
||||
print(f"pragent-webhook: could not re-read labels for {repo}#{index}: {e}", flush=True)
|
||||
return []
|
||||
|
||||
|
||||
def _run_review(
|
||||
key: tuple[str, str, str], title: str, body: str, report_usage: bool, base_ref: str
|
||||
) -> None:
|
||||
repo, index, sha = key
|
||||
# The AI-USAGE opt-in may have been applied *after* the label event that
|
||||
# triggered this review (labelling is two events, the review claims on the
|
||||
# first). Re-read the labels now so the later opt-in still counts.
|
||||
if not report_usage:
|
||||
report_usage = _labels_have(
|
||||
_fetch_current_labels(repo, index), AI_USAGE_LABEL
|
||||
)
|
||||
if report_usage:
|
||||
print(
|
||||
f"pragent-webhook: {repo}#{index} AI-USAGE found on re-read "
|
||||
f"(added after the trigger event)",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
try:
|
||||
with _review_slots:
|
||||
ok = review_pr(
|
||||
|
||||
Reference in New Issue
Block a user