fix(review): serialize public-surface publish under the per-PR actuation lock (#9013) - #9181
Merged
Merged
Conversation
Contributor
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
10 tasks
…ion lock (#9013) maybePublishPrPublicSurface ran with no per-PR mutex; only the later maybeRunAgentMaintenance claimed one. Two concurrent passes for the same PR (a webhook delivery racing a sweep re-review) could both publish, producing duplicate gate check-runs and letting a lock-losing pass's placeholder verdict overwrite a real one. Claim the actuation lock once before the publish call and thread it through both maybePublishPrPublicSurface (as preAcquiredActuationLock, covering its internal type-label section) and maybeRunAgentMaintenance, mirroring the existing preAcquiredAiReviewLock contract. A lock-losing pass now defers the whole publish-and-maintain unit instead of racing ahead. Also promotes the type-label block's own lock contention from "skip the label and keep publishing" to deferring the whole pass. Closes #9013
JSONbored
force-pushed
the
fix/9013-pr-actuation-lock-spans-publish
branch
from
July 27, 2026 06:26
c00f480 to
301ea18
Compare
❌ 5 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
maybePublishPrPublicSurfaceran with no per-PR mutex; only the latermaybeRunAgentMaintenanceclaimed the actuation lock. Two concurrent passes for the same PR (a webhook delivery racing a sweep re-review) could both publish — producing duplicate gate check-runs and letting a lock-losing pass's placeholder verdict overwrite a real one, whichever PATCHed last.reReviewStoredPullRequest,handlePullRequestWebhookEvent), and threads it through aspreAcquiredActuationLockinto bothmaybePublishPrPublicSurface(which also covers its internal type-label section, the only other place inside publish that claims this same lock) andmaybeRunAgentMaintenance— mirroring the existingpreAcquiredAiReviewLockcontract. A lock-losing pass now defers the whole publish-and-maintain unit (throws, retried by the queue) instead of racing ahead.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #9013).Validation
git diff --checknpm run actionlint(not run — no workflow files changed)npm run typechecknpm run test:coveragelocally (attempted; the full unsharded run in this environment did not finish producing a report within a practical time budget, so coverage is instead verified via targeted tests covering every new branch: the pre-acquired vs. self-claimed lock path on bothmaybeRunAgentMaintenanceand the type-label block, and the contention-throw path for both the new outer lock and the promoted type-label lock — see the 5 updated test files below)npm run test:workers(not run — no Workers-pool-specific code touched)npm run build:mcp(not run — no MCP package touched)npm run test:mcp-pack(not run — no MCP package touched)npm run ui:openapi:check(not run — no API/schema changes)npm run ui:lint/ui:typecheck/ui:build(not run — noapps/loopover-uichanges)npm audit --audit-level=moderate(pre-existing transitivebrace-expansion/eslintfindings, unrelated to this change, unchanged by it)test/unit/queue.test.ts,test/unit/queue-2.test.ts,test/unit/queue-3.test.ts,test/unit/queue-4.test.ts,test/unit/queue-5.test.tsupdated/addedtest/unitsuite (unsharded) and confirmed every failure is pre-existing onmain(verified viagit stashA/B comparison):test/unit/backfill.test.ts,test/unit/queue-4.test.ts("debounces noisy PR events"),test/unit/setup-wizard-docs-parity.test.ts,test/integration/api.test.ts("serves installation repair diagnostics") — none touch the code paths changed hereIf any required check was skipped, explain why:
actionlint,test:workers,build:mcp/test:mcp-pack,ui:*steps were skipped because this PR only touchessrc/queue/processors.tsand its Node-pool unit tests — no workflow, MCP package, or UI files changed.test:coveragerun did not finish emitting its coverage report within this environment's practical time budget (v8 coverage collection over the ~22k-test suite is expensive here); coverage on the diff was instead confirmed by exercising every new branch directly with targeted tests (see below).Safety
UI Evidencesection below with screenshots. (N/A — no visible UI changes.)Notes
maybePublishPrPublicSurface(the panel comment + gate check-run publish pass) andmaybeRunAgentMaintenance(the merge/close disposition pass) always ran back-to-back for a given webhook/sweep pass, but only the second one claimed the per-PR actuation lock. Two independently-triggered passes for the same PR (different coalesce keys, so never deduped against each other) could both reachmaybePublishPrPublicSurfaceconcurrently: both wouldGET-latest/POST-if-absent a gate check-run (no dedup exists for check-runs, unlike panel comments'deleteDuplicateMarkerCommentsself-heal), and whichever PATCHed last would win — including a lock-losing AI-review pass publishing anai_review_inconclusiveplaceholder over a genuine verdict.finallyspanning both), so the two are treated as a single atomic unit — a losing pass defers entirely rather than partially completing. This also transitively closes the AI-review-lock placeholder-overwrite scenario described in the issue, since two publish passes for the same PR can no longer run concurrently at all.maybeProcessPrPanelRetrigger, the manual "Re-run LoopOver review" checkbox) also callsmaybePublishPrPublicSurfacebut was out of scope for the issue (not mentioned in its root-cause analysis) and has no trailing maintenance call to pair with; it still self-claims the lock inside the type-label block exactly as before, and is now covered by a new regression test (test/unit/queue-4.test.ts) for the promoted contention-throw behavior.Closes #9013