Skip to content

fix(queue): elect the duplicate-winner from live sibling state, not the stale cache#1406

Merged
JSONbored merged 1 commit into
mainfrom
claude/beautiful-margulis-cd0070
Jun 26, 2026
Merged

fix(queue): elect the duplicate-winner from live sibling state, not the stale cache#1406
JSONbored merged 1 commit into
mainfrom
claude/beautiful-margulis-cd0070

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

The duplicate-cluster winner (#dup-winner) was elected from the stored open-PR cache (listOtherOpenPullRequests), not live GitHub state. That cache lags GitHub, so a sibling that was closed/merged on GitHub but still cached open kept "winning" the cluster — the real lowest-OPEN PR was treated as a loser, given the duplicate_pr_risk blocker (default block), and one-shot auto-closed. This is a destructive race (audit #15).

The fix reconciles the cluster's open siblings against live GitHub state before the winner is elected, in reconcileLiveDuplicateSiblings:

  • Re-fetch the LIVE state of just the lower-numbered overlapping siblings (the only ones that can demote this PR from winner) via the new fetchLivePullRequestState (GET /pulls/{n}), and drop any no longer open, so isDuplicateClusterWinner sees ground truth.
  • Applied at both review entry points (reReviewStoredPullRequest and the pull_request webhook path) before buildPullRequestAdvisory, so the advisory's duplicate_pr_risk finding and the disposition agree.
  • Fail-open: a sibling is dropped only on a positive non-open confirmation — an unreadable live fetch keeps it, so a transient GitHub hiccup never newly spares a real loser.
  • Entirely gated behind GITTENSORY_DUPLICATE_WINNER; flag-off (default) is byte-identical with no extra API calls.

No GitHub issue — internal review-subsystem audit finding (#15). Self-contained behavioral fix to the live-merge / one-shot-close path.

Scope

  • Backend (src/) only — src/queue/processors.ts, src/github/backfill.ts
  • No API/schema change (no ui:openapi regen needed)
  • No DB/migration, no wrangler.jsonc binding/var, no UI change
  • Narrow, one coherent change

Validation

  • npm run test:ci — green (4454 passed | 4 skipped)
  • npm run test:coverage — every changed line and branch covered (verified against coverage/lcov.info), incl. the previously-uncovered linkedIssueDuplicatePullRequestsForGate non-overlap arm
  • npm audit --audit-level=moderate — 0 vulnerabilities
  • npm run typecheck / ui:typecheck — clean
  • New tests: reconcile-live-duplicate-siblings.test.ts (flag-off / no-issue / no-lower-sibling / cached-closed / live-closed-drop / live-open-keep / fail-open / multi-sibling / token-mint throws+succeeds), fetch-live-pr-state.test.ts, and added linkedIssueDuplicatePullRequestsForGate cases.

Safety

  • No secrets / wallets / hotkeys / coldkeys / trust scores / reward values added
  • Fail-open (drops a sibling only on positive non-open confirmation); flag-gated; flag-off byte-identical
  • No public-surface term leakage (no comment/check-output text changed)

…he stale cache

The duplicate-cluster winner (#dup-winner) was elected from the stored open-PR
cache (listOtherOpenPullRequests). That cache lags GitHub, so a sibling closed
or merged on GitHub but still cached `open` kept "winning" the cluster — the
real lowest-OPEN PR was treated as a loser, given the duplicate_pr_risk blocker,
and one-shot auto-closed.

Reconcile the cluster's open siblings against live GitHub state before the
winner is elected: re-fetch the LIVE state of just the lower-numbered
overlapping siblings (the only ones that can demote this PR) and drop any no
longer open, so isDuplicateClusterWinner sees ground truth. Fail-open — a
sibling is dropped only on a positive non-open confirmation, and the whole path
is gated behind GITTENSORY_DUPLICATE_WINNER, so flag-off is byte-identical with
no extra API calls.

Adds fetchLivePullRequestState; covers both reconcile arms, the new helper's
branches, and the previously-uncovered linkedIssueDuplicatePullRequestsForGate
non-overlap arm.
@dosubot dosubot Bot added the size:M label Jun 26, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

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

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.34%. Comparing base (c1db53a) to head (9ce90c2).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1406   +/-   ##
=======================================
  Coverage   95.34%   95.34%           
=======================================
  Files         192      192           
  Lines       20752    20770   +18     
  Branches     7501     7508    +7     
=======================================
+ Hits        19785    19804   +19     
  Misses        383      383           
+ Partials      584      583    -1     
Files with missing lines Coverage Δ
src/github/backfill.ts 92.93% <100.00%> (+0.01%) ⬆️
src/queue/processors.ts 87.87% <100.00%> (+0.24%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JSONbored
JSONbored merged commit fba397a into main Jun 26, 2026
18 checks passed
@JSONbored
JSONbored deleted the claude/beautiful-margulis-cd0070 branch June 26, 2026 00:52
@JSONbored JSONbored self-assigned this Jun 26, 2026
galuis116 pushed a commit to galuis116/gittensory that referenced this pull request Jul 18, 2026
Root cause traced as far as possible: release-please's own createReleases()
(the step that actually tags a merged Release PR) sometimes finds nothing
to tag, and createPullRequests() then aborts every subsequent run without
ever completing the tag -- confirmed live across two separate
linked-versions release cycles this session (JSONbored#7127, JSONbored#7133), both requiring
a manual dispatch of each publish-*.yml workflow plus manually flipping
the merged PR's autorelease:pending label. Matches multiple long-open,
unresolved upstream issues (googleapis/release-please#1946/JSONbored#1444/JSONbored#1406),
so it isn't something fixable from this repo's config alone.

Adds a reconcile-stale-releases job that runs after every release-please
attempt, independent of its outputs: compares each package's committed
package.json version against what's actually live on npm, dispatches
that package's publish workflow whenever they disagree (the same
documented human-override path, automated), and flips any merged PR's
autorelease label once every package is confirmed live -- unsticking
release-please's own tracking for the next run. Also cleaned up one
ancient, silently-stuck instance of the exact same bug (engine v1.0.0,
autorelease:pending since long before this session).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant