Skip to content

fix(github): exclude the panel's per-pass timestamp from the comment idempotency check (#9069) - #9073

Merged
JSONbored merged 1 commit into
mainfrom
fix/9069-comment-idempotency-timestamp
Jul 26, 2026
Merged

fix(github): exclude the panel's per-pass timestamp from the comment idempotency check (#9069)#9073
JSONbored merged 1 commit into
mainfrom
fix/9069-comment-idempotency-timestamp

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Problem

The PR panel embeds <sub>Review updated: <timestamp></sub> (src/review/unified-comment.ts), re-stamped from reviewedAt ?? new Date() on every render (unified-comment-bridge.ts) — and the processor never passes reviewedAt.

createOrUpdateIssueCommentWithMarker compared raw bodies:

if (canonical.body === body) { /* skip the PATCH */ }

So the panel could never match its own posted body, and the idempotency skip — whose stated purpose is "without this, every cycle PATCHes GitHub (a write + rate-limit cost) for no visible change" — was unreachable for the one comment it mattered most for.

Every re-gate tick therefore PATCHed GitHub purely to move a clock, and each PATCH generated an inbound issue_comment.edited delivery that ingress then classified as our own noise and discarded.

Measured impact (live ledger)

event count share of lifetime deliveries
check_suite.completed 106,943 35%
issue_comment.edited 79,612 26%
issue_comment.created 28,411 9%

~309,600 deliveries against ~12,500 reviews — roughly 25 webhooks per review, with a quarter of it this self-feeding loop, plus the matching outbound writes against the REST rate limit.

Change

Compare through a new comparableCommentBody, which normalizes only that one generated line.

Compare-only — the body actually posted keeps its real timestamp, and a body whose content genuinely changed still PATCHes carrying the fresh one. The surviving timestamp then reads as "the review last changed at X" rather than "we last looked at X", which is the more useful meaning and the one the wording already implies.

The regex is bounded to [^<]* and anchored per-line, so it can only ever match this exact generated line; every caller-supplied string reaching a comment body is angle-escaped upstream (escapePublicHtmlAngles), so contributor text cannot forge a <sub> wrapper. Comments without the line (close explanations, visual follow-ups) keep byte-exact comparison.

Secondary correctness win

changed: false now propagates correctly to the #6724 no-op accounting. pr_public_surface_published was previously recorded on every clock-only pass, inflating both the public "reviews completed" count and the review-burst anomaly counter. markPullRequestSurfacePublished deliberately stays outside that branch, so the surface stamp still advances every pass and the repair sweeps (surfaceRepairPriorityPullNumbers, backlog convergence) are unaffected.

Validation

  • npx vitest run test/unit/github-comments.test.ts24 passed, including 3 new cases.
  • Coverage on src/github/comments.ts: the only uncovered line is createOrUpdateAgentCommandComment, which is pre-existing and unrelated (it sits at line 93 on main and line 117 here, shifted by this patch's added lines). No new uncovered lines or branches.
  • tsc --noEmit --incremental false clean.
  • Prettier flags both files identically before and after this change (pre-existing on main), so they are deliberately left unreformatted rather than dragging in a large unrelated diff.

Tests added

  1. Skip on a clock-only delta — two renders differing only in the timestamp produce no PATCH and changed: false.
  2. Real changes still PATCH — content delta alongside a new timestamp PATCHes, and the asserted posted body carries the real fresh timestamp, proving the normalization is compare-only.
  3. INVARIANT/regression — normalization is confined to the generated line: bodies without it stay byte-exact, a forged inline <sub> sequence is untouched, and multi-occurrence /g behavior is pinned.

Closes #9069

…idempotency check (#9069)

The PR panel embeds `<sub>Review updated: <timestamp></sub>`, re-stamped from
`reviewedAt ?? new Date()` on every render. createOrUpdateIssueCommentWithMarker
compared raw bodies, so the panel could never match its own posted body and the
idempotency skip was unreachable for it: every re-gate tick PATCHed GitHub purely
to move a clock, and each PATCH generated an inbound issue_comment.edited delivery
that ingress then classified as our own noise and discarded.

That loop accounted for 79,612 of ~309,600 lifetime webhook deliveries (26%), plus
the matching outbound writes against the REST rate limit.

Compare through comparableCommentBody, which normalizes only that generated line.
Compare-only: the posted body keeps its real timestamp, and a body whose content
genuinely changed still PATCHes with the fresh one. The surviving timestamp then
means "the review last changed at X" rather than "we last looked at X".

Also makes the #6724 no-op accounting honest — pr_public_surface_published was
being recorded on every clock-only pass, inflating the public reviews-completed
count and the review-burst anomaly counter. markPullRequestSurfacePublished stays
outside that branch, so the surface stamp still advances and the repair sweeps are
unaffected.

Closes #9069
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored JSONbored self-assigned this Jul 26, 2026
@JSONbored
JSONbored merged commit e138090 into main Jul 26, 2026
4 checks passed
@JSONbored
JSONbored deleted the fix/9069-comment-idempotency-timestamp branch July 26, 2026 16:46
@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 26, 2026
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.65%. Comparing base (cce7f00) to head (9f2cb51).
⚠️ Report is 14 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9073      +/-   ##
==========================================
+ Coverage   90.56%   92.65%   +2.09%     
==========================================
  Files          96      807     +711     
  Lines       22490    80502   +58012     
  Branches     3884    24410   +20526     
==========================================
+ Hits        20367    74589   +54222     
- Misses       1945     4840    +2895     
- Partials      178     1073     +895     
Flag Coverage Δ
backend 93.46% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/github/comments.ts 100.00% <100.00%> (ø)

... and 710 files with indirect coverage changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

orb(webhook): ~25 webhooks per review — the comment idempotency guard never fires because the panel embeds a per-pass timestamp

1 participant