Skip to content

fix(review): draft fork PRs republish the same review comment every few minutes (missing skip-guard) #6685

Description

@JSONbored

Severity: medium (wasted GitHub API calls, comment noise, and it's the direct cause of the ongoing review burst ops_anomaly false alarms across the fleet)

Failure scenario

A draft PR opened from a fork (e.g. JSONbored/loopover#6592, author claytonlin1110) republishes its review surface every 2-3 minutes for as long as its CI keeps running, even though nothing about the PR's content, review, or labels has changed since the first publish.

Confirmed live via the self-host Postgres audit_events table for #6592 between 16:20-16:52 UTC on 2026-07-16 — 12 github_app.pr_public_surface_published events in ~29 minutes, each with a different webhook deliveryId but identical reviewEffortMinutes: 290, interleaved with a github_app.ci_completion_fork_resume event every cycle:

16:20:52  github_app.review_deferred_ci_pending        CI still running — review deferred until all checks finish
16:22:20  github_app.ci_completion_fork_resume         resumed fork PR via head-SHA fallback (empty check pull_requests[])
16:22:25  github_app.type_label_decision                applied labels: gittensor:feature
16:22:25  github_app.ai_slop_cache_miss                 running a fresh advisory (first pass)
16:23:01  github_app.linked_issue_satisfaction_cache_miss
16:23:14  github_app.ai_review_auto_review_skipped      review skipped (draft)
16:23:49  github_app.pr_public_surface_published        <- publish #1

16:25:20  github_app.ci_completion_fork_resume         resumed fork PR via head-SHA fallback (empty check pull_requests[])
16:25:23  github_app.type_label_decision                applied labels: gittensor:feature  (same labels, reapplied)
16:25:23  github_app.ai_slop_one_shot_skip               already had its slop advisory pass; not spending a fresh call
16:25:23  github_app.linked_issue_satisfaction_one_shot_skip
16:25:23  github_app.ai_review_auto_review_skipped      review skipped (draft)
16:26:23  github_app.pr_public_surface_published        <- publish #2 (nothing changed since #1)

... repeats every 2-5 minutes, 12 times total in this window ...

Root cause (two parts)

1. Re-trigger source: #6592 is a fork PR. Per a well-known GitHub API quirk, check_run/check_suite webhook payloads for fork-originated checks carry an empty pull_requests[] array, so the webhook handler falls back to ci_completion_fork_resume ("resumed fork PR via head-SHA fallback") on every individual check completion. A CI run with N jobs produces N separate completion webhooks, each independently re-running the full per-PR evaluation pipeline — there's no coalescing across these fork-resume events for the same head SHA within a short window.

2. Missing skip-guard: Each re-run correctly avoids wasting AI spend — ai_slop_one_shot_skip, linked_issue_satisfaction_one_shot_skip, and ai_review_auto_review_skipped ("review skipped (draft)", from src/signals/focus-manifest.ts:156) all fire as expected. But nothing stops the pipeline from proceeding to republish the surface anyway. The only existing "skip republish if nothing changed" guard is in src/queue/processors.ts around line 9679-9695, and it's explicitly scoped to settings.publicSurface === "off" (check-run-only repos) — by design, per that guard's own comment, because a lastPublishedSurfaceSha match is considered insufficient proof of a complete comment+label publish (unlike an atomic completed check-run). loopover, awesome-claude, and metagraphed all use publicSurface: comment_and_label, so none of them have any equivalent guard — every fork-resume cycle on a draft PR unconditionally republishes.

Suggested fix direction

Add a narrower, safer skip specifically for the case this issue documents — not a blanket extension of the existing publicSurface === "off" guard (that guard's narrow scope is intentional and documented). When ALL of the following hold for the current pass:

  • autoReviewSkipReason === "review skipped (draft)" (i.e., we know deterministically why no review ran, not just that one was reused)
  • slop advisory and linked-issue satisfaction both hit their one-shot-skip path (not freshly computed)
  • resolved labels are unchanged from the PR's current label set
  • advisory.headSha === pr.lastPublishedSurfaceSha

... skip the republish. This is a stronger proof-of-no-op than the existing check-run-only guard already accepts (it doesn't rely solely on the SHA marker — it also confirms every substantive dimension explicitly took the "nothing to do" path this pass), so it should be safe to apply to comment_and_label surfaces too, without the partial-publish risk the existing guard's scoping comment warns about.

Separately worth a look: whether fork-PR check-run completions for the same head SHA within a short window (e.g. a few seconds/minutes) can be coalesced into one ci_completion_fork_resume evaluation instead of one per completing check job — would reduce the trigger frequency at the source, independent of the publish-skip fix above.

Impact

This is the actual root cause of the review burst (stuck-CI finalize loop or retry storm) ops_anomaly that's been firing every ~1-2h across loopover, awesome-claude, and metagraphed continuously for at least 24h+ prior to this issue being filed — rotating across whichever draft fork PR happens to have CI running at the time, not a single stuck PR.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

Status
Done

Relationships

None yet

Development

No branches or pull requests

Issue actions