fix(test): await issue lock before retry race assertions (#13273)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Retry decisions must respect changes to an issue owner. > - Database tests verify this with two concurrent transactions. > - The test started the competing operation before its fixture held the lock. > - That race can fail a correct source verification run and block deployment. > - This PR waits for lock acquisition before starting the competing operation. ## Linked Issues or Issue Description Refs #13257 for the deployment verification work that exposed this test race. No duplicate fix was found. **What happened?** [Cloud verification job 103424158921](https://github.com/paperclipai/paperclip/actions/runs/34648268409/job/103424158921) failed because the test did not observe a concurrent issue-row lock waiter. The fixture and retry operation both started without an ordering guarantee. **Expected behavior** The fixture must hold the issue lock before the competing retry operation starts. The test must still prove that the retry waits for the lock and observes the reassignment. **Steps to reproduce** 1. Add a temporary 50 ms delay before the fixture acquires the issue lock. 2. Run the promoteOrCancelDueRetry issue-lock test. 3. The original fixture fails with the same missing-waiter error as CI. 4. The synchronized fixture passes with that delay. The delay is not part of this PR. ## What Changed - Separate fixture readiness from its transaction completion promise. - Wait for readiness at both callers before starting the retry decision. - Propagate transaction failure during setup through Promise.race. ## Verification - The original fixture fails under the temporary delayed-lock probe. The fixed fixture passes the same probe. - All 31 tests in server/src/modules/run-dispatch/adapters/postgres.test.ts pass after removing the probe. - The real concurrent waiter, lock-order, and reassignment assertions remain intact. No timeout was increased. - Full local typecheck and build pass (221s and 50s). The full local test command stopped in its server phase after 10,610 passes, 65 skips, and 14 failures: 13 existing macOS skill-cache rename/permission failures and one unchanged Telegram test assertion. The Telegram test passes in a focused rerun. Later local phases did not run after that failure. Current-head Greptile is 5/5 with no findings. All 32 current-head checks pass, including full Linux typecheck, build, native verification, server suites, browser suites, and canary packaging. The unchanged GitHub browser mock assertion passed its single failed-shard retry. - git diff --check passes. ## Risks - Only test synchronization changes. Production database behavior is unchanged. - Awaiting the transaction itself during setup would deadlock the test. Returning the completion promise inside an object avoids that problem. ## Model Used OpenAI GPT-6 through Codex, with reasoning, repository tools, and code execution. The exact serving model ID and context window are not exposed by this environment. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass — all 31 database adapter tests pass; full local verification limits are disclosed above - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
51b0e01ead
commit
1c4bcff2b1
|
|
@ -319,8 +319,10 @@ describeEmbeddedPostgres("run-dispatch postgres adapter", () => {
|
|||
.set({ assigneeAgentId: newAssigneeAgentId })
|
||||
.where(eq(issues.id, issueId));
|
||||
});
|
||||
await locked;
|
||||
return transaction;
|
||||
// Await lock acquisition before starting the competing operation. Keep
|
||||
// completion separate so setup does not wait for that operation to finish.
|
||||
await Promise.race([locked, transaction]);
|
||||
return { done: transaction };
|
||||
}
|
||||
|
||||
describe("evaluateScheduledRetryGate", () => {
|
||||
|
|
@ -572,7 +574,7 @@ describeEmbeddedPostgres("run-dispatch postgres adapter", () => {
|
|||
contextSnapshot: { issueId, wakeReason: "issue_assigned" },
|
||||
});
|
||||
|
||||
const holderDone = reassignIssueAndLockRunOnceAConcurrentWaiterBlocks(
|
||||
const { done: holderDone } = await reassignIssueAndLockRunOnceAConcurrentWaiterBlocks(
|
||||
issueId,
|
||||
runId,
|
||||
replacementAgentId,
|
||||
|
|
@ -733,7 +735,7 @@ describeEmbeddedPostgres("run-dispatch postgres adapter", () => {
|
|||
// Acquire the issue row lock first and hold it until it observes a
|
||||
// concurrent `for update` waiter — the promote call below — proving
|
||||
// this is a real block, not a race the assertion got lucky on.
|
||||
const holderDone = reassignIssueAndLockRunOnceAConcurrentWaiterBlocks(
|
||||
const { done: holderDone } = await reassignIssueAndLockRunOnceAConcurrentWaiterBlocks(
|
||||
issueId,
|
||||
runId,
|
||||
newAgentId,
|
||||
|
|
|
|||
Loading…
Reference in New Issue