Skip to content

feat(queue): wake linked PRs promptly on an issue-side label/assignment change - #2371

Merged
JSONbored merged 7 commits into
mainfrom
claude/issue-link-hard-rule-wake
Jul 1, 2026
Merged

feat(queue): wake linked PRs promptly on an issue-side label/assignment change#2371
JSONbored merged 7 commits into
mainfrom
claude/issue-link-hard-rule-wake

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

What

processGitHubWebhook had no eventName === "issues" branch (confirmed absent despite the event being subscribed and delivered), so labeling/unlabeling maintainer-only (or another point-bearing label) on a linked ISSUE, or assigning/unassigning the repo owner on it, never re-triggered the linked-issue hard-rule re-evaluation for PRs that link it. That check only ran when the PR itself received a webhook, or via the periodic sweep — which caps at SWEEP_MAX_PRS = 3 per repo per ~2-minute tick with no priority signal for "a linked issue's label just changed."

Failure scenario: a maintainer adds maintainer-only to an issue after a contributor already opened a linking PR (or removes it to unblock a wrongly-flagged PR). The PR keeps whatever verdict it had until either a new push happens on it or the staleness-ordered sweep eventually reaches it — on a repo with more than a few open PRs, this can lag for many sweep cycles. A should-now-be-closeable PR can still auto-merge in the meantime, or a should-now-be-mergeable PR stays wrongly held/closed.

Fix

Add maybeReReviewOnLinkedIssueChange, mirroring the existing CI-completion re-review handler (maybeReReviewOnCiCompletion) that already solves the structurally identical problem for a different trigger: on a labeled/unlabeled/assigned/unassigned issues event, find every OPEN PR that links the issue (pr.linkedIssues.includes(issueNumber)) and re-review it promptly via the existing reReviewStoredPullRequest.

  • Reuses the same per-PR coalesce window CI-completion re-review already uses (ciReReviewCoalesced) — its purpose applies identically here: bound re-review frequency, never correctness, since the re-review always re-fetches live state regardless of trigger.
  • Gated on the same GITTENSORY_REVIEW_REPOS convergence allowlist the CI-completion handler uses, so this activates on exactly the same repo footprint as the analogous existing mechanism — no new rollout surface.
  • Wired in processGitHubWebhook alongside the other non-PR wake triggers (CI-completion, deployment_status), since an issues event carries no payload.pull_request either.

Tests

  • An issue label change wakes the linked PR's re-evaluation promptly (webhook-driven, not sweep-only) — the explicit deliverable from the issue.
  • Does NOT wake an open PR that links a different issue (proves the filter, not "any issue event wakes all PRs").
  • Dormant on a repo outside the convergence allowlist (still marks the webhook processed; only the re-review work is skipped).
  • No-ops safely on a malformed payload missing the issue number.
  • Respects the coalesce window shared with CI-completion re-review (no redundant re-review within it).

Full unsharded test:coverage green (5605 passed); typecheck green; npm audit clean.

Advances #1936. Closes #2259.

…nt change

processGitHubWebhook had no eventName === "issues" branch, so
labeling/unlabeling maintainer-only (or another point-bearing label)
on a linked ISSUE, or assigning/unassigning the repo owner on it,
never re-triggered the linked-issue hard-rule re-evaluation for PRs
that link it. That check only ran when the PR itself received a
webhook, or via the staleness-ordered sweep, which caps at 3 PRs per
repo per ~2-minute tick with no priority signal for "a linked issue
just changed" — on a busy repo this can lag for many cycles, letting
a should-now-be-closeable PR auto-merge, or a should-now-be-mergeable
PR stay wrongly held.

Add maybeReReviewOnLinkedIssueChange, mirroring the existing
CI-completion re-review handler (maybeReReviewOnCiCompletion): on a
labeled/unlabeled/assigned/unassigned issues event, find every OPEN
PR that links the issue and re-review it promptly. Reuses the same
per-PR coalesce window CI-completion re-review already uses, and the
same GITTENSORY_REVIEW_REPOS convergence allowlist gate, so this
activates on exactly the same repo footprint as the analogous
existing mechanism.
@dosubot dosubot Bot added the size:M label Jul 1, 2026
@loopover-orb

loopover-orb Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-01 22:41:51 UTC

2 files · 1 AI reviewer · no blockers · readiness 93/100 · CI pending · blocked

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review

Review summary
The change wires `issues` label/assignment events into the non-PR webhook path, finds stored open PRs that link the changed issue, and re-runs the existing live PR review path. The dedicated issue-side coalesce key and trailing delayed `agent-regate-pr` job address the real stale-state risk from bursty add/remove sequences without sharing the CI-completion window. The tests cover the linked/unlinked paths, allowlist gating, coalesce separation, trailing scheduling, and enqueue-failure retry behavior, so the visible diff is coherent and safe enough to proceed.

Nits — 7 non-blocking
  • nit: `src/queue/processors.ts:2370` and `src/queue/processors.ts:2672` add very long explanatory comments that repeat the same rationale several times; I would trim them to the invariant and the failure mode so this hot-path file stays readable.
  • nit: `test/unit/queue.test.ts:1541` and the following new cases repeat a large repo/install/settings/PR setup block; extracting a small helper for the issue-wake fixture would make the regression cases easier to audit.
  • nit: `test/unit/queue.test.ts:1541` uses dense one-line object literals for most setup calls, which makes the changed tests harder to scan than the surrounding suite.
  • In `src/queue/processors.ts:2370`, keep the dedicated namespace and trailing-job invariant, but collapse the duplicate comment text into one short note near `issueLinkedPrReReviewCoalesced` and one near `scheduleTrailingIssueLinkedReReview`.
  • In `test/unit/queue.test.ts:1541`, add a local helper that seeds `owner/agent-repo` with PR fix(docs): add explicit favicon metadata #7 linked to a supplied issue number and returns the env, then reuse it across the new issue-wake tests.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #2259
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (size label size:L; 1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 65 registered-repo PR(s), 55 merged, 572 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 65 PR(s), 572 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: not available
  • Official Gittensor activity: 65 PR(s), 572 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Triage stale or unlinked PRs.
  • No action.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.86%. Comparing base (cbf1909) to head (c1ebbca).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2371   +/-   ##
=======================================
  Coverage   95.85%   95.86%           
=======================================
  Files         224      224           
  Lines       25104    25134   +30     
  Branches     9129     9137    +8     
=======================================
+ Hits        24064    24094   +30     
  Misses        427      427           
  Partials      613      613           
Files with missing lines Coverage Δ
src/queue/processors.ts 91.52% <100.00%> (+0.13%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb loopover-orb Bot added gittensor gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. labels Jul 1, 2026
@JSONbored JSONbored self-assigned this Jul 1, 2026
JSONbored added 2 commits July 1, 2026 14:01
maybeReReviewOnLinkedIssueChange reused ciReReviewCoalesced's shared
`ci-coalesce:{repo}#{pr}` key, but a CI-completion webhook and an
issue-side label/assignment change answer different questions -- unlike
concurrent CI-completion events (interchangeable: whichever wins the
coalesce race re-fetches the same already-settled CI state), a completely
unrelated CI re-review claiming the shared window could silently suppress
a genuine issue-side signal, leaving the linked-issue verdict stale until
the window expired or the sweep eventually reached the PR.

Add a dedicated issueLinkedPrReReviewCoalesced using its own
`issue-link-coalesce:` key namespace. Within that namespace the window
still bounds frequency for a burst of same-PR issue-side churn -- its
legitimate purpose, matching the CI window's own philosophy -- but it can
no longer be stolen by (or steal from) an unrelated CI-completion event.

Rewrite the test that asserted the CI window suppressing the issue-side
wake as correct behavior into a regression test asserting the opposite,
and add a dedicated test proving same-domain (issue-side) bursts still
coalesce correctly.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
gittensory-ui 15c00a5 Commit Preview URL

Branch Preview URL
Jul 01 2026, 09:06 PM

…ents

The prior fix gave issue-side wakes their own coalesce window (separate
from CI-completion's), but same-PR issue-side events are ALSO not
interchangeable with each other: an add-then-remove label or
assign-then-unassign sequence within the 60s window carries genuinely
different states. The plain throttle (ciCompletionCoalesced) silently
drops every event after the first, so the second event's state was lost
entirely -- the PR stayed on the FIRST (now-stale) state until another
webhook or the sweep eventually reached it, defeating the "wake promptly"
purpose this whole trigger exists for.

Add scheduleTrailingIssueLinkedReReview: when an issue-side event is
coalesced, schedule exactly one deduped trailing agent-regate-pr job
(delaySeconds: 60, the window's length) so the LATEST state is always
eventually captured shortly after the window closes. Reuses the existing
rate-limit-aware, retried sweep-unit job rather than inventing a new job
type; deduped via its own claim key so a burst of N coalesced events
schedules ONE trailing job, not N. A failed enqueue is swallowed
(best-effort -- the sweep remains the ultimate backstop).

Add regression tests: an add-then-remove sequence schedules exactly one
correctly-shaped trailing job, a third coalesced event does not schedule
a second, and a failed enqueue never propagates into the webhook handler.
@dosubot dosubot Bot added size:L and removed size:M labels Jul 1, 2026
JSONbored added 3 commits July 1, 2026 14:36
…e succeeds

scheduleTrailingIssueLinkedReReview used ciCompletionCoalesced, which
writes its dedup marker unconditionally as soon as it is called -- BEFORE
the subsequent env.JOBS.send even runs. A transient queue failure would
therefore leave the "trailing re-review scheduled" marker held with
nothing actually queued: every later coalesced issue-side event for the
same PR within the window would see the marker already claimed and skip
retrying, permanently forfeiting the guarantee this function exists to
provide (capturing the latest linked-issue state after a burst).

Reorder to check-then-send-then-claim: read the marker first (read-only,
via getTransientKey), attempt the enqueue, and write the marker (via
putTransientKey) ONLY when the send actually succeeds. A failed attempt
leaves the marker unclaimed, so the next coalesced event in the same
window retries the enqueue instead of silently giving up.

Add a regression test simulating a transient failure (first send throws,
second succeeds) that proves the retry works and that a fourth coalesced
event correctly dedupes against the now-successful claim.
@JSONbored
JSONbored merged commit 6fa4c2b into main Jul 1, 2026
12 checks passed
@JSONbored
JSONbored deleted the claude/issue-link-hard-rule-wake branch July 1, 2026 22:45
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

fix(queue): issue-side label edits never wake the linked PR's hard-rule re-evaluation

1 participant