test(recovery): fix race condition in workspace validation comment check (#8294)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The heartbeat service manages agent run lifecycles, including workspace validation before launching git-sensitive local adapters > - When workspace validation fails, `escalateStrandedAssignedIssue` in the recovery service sets the issue to `"blocked"` and then asynchronously posts a system comment > - A test added in PR #7644 verifies the comment appears, but queried the database immediately after `waitForValue` resolved the issue status — creating a race between the status write and the comment write > - The test was passing before PR #8284 (pnpm lockfile refresh) but began failing after, suggesting the lockfile update shifted async scheduling enough to expose the window > - This pull request wraps the comment check in `waitForValue`, consistent with two identical polling patterns already used in the same test file (lines 1159-1162 and 1232-1235) > - The benefit is a consistently-passing test that unblocks dependabot PR #8155 from auto-merging via CI ## Linked Issues or Issue Description Refs PAP-72. Unblocks PAP-68 (dependabot PR #8155 which has auto-merge enabled and was blocked by this test failure). ## What Changed - `server/src/__tests__/heartbeat-process-recovery.test.ts`: replaced direct `db.select()` + `expect(comments.some(...))` with a `waitForValue` polling loop that waits until a comment containing `"workspace failed validation"` appears, then asserts it is truthy - No production code changed ## Verification The specific test `blocks a git-sensitive local adapter before launch when a project-workspace-linked issue is missing its project id` previously failed with: ``` AssertionError: expected false to be true src/__tests__/heartbeat-process-recovery.test.ts:1429:94 ``` After this fix: the test polls with a 3-second timeout and returns once the comment row exists. The comment IS written by `escalateStrandedAssignedIssue` — this was purely a timing issue in the test assertion. CI on this PR serves as the confirmation run. ## Risks Low risk — test-only change. No production code is modified. The `waitForValue` helper is already used 10+ times in the same test file with the same 3-second timeout; this use follows the identical pattern. ## Model Used - Provider: Anthropic - Model: Claude Sonnet 4.6 (`claude-sonnet-4-6`) - Context window: 200k tokens - Tool use: yes (file read, bash, edit tools) - Reasoning mode: extended thinking ## 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 - [ ] I have run tests locally and they pass (CI will verify) - [x] I have added or updated tests where applicable (this IS the test fix) - [x] If this change affects the UI, I have included before/after screenshots (N/A) - [x] I have updated relevant documentation to reflect my changes (N/A) - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green (pending CI run) - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups (in progress) - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1425,8 +1425,11 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => {
|
||||
});
|
||||
expect(recoveryAction?.nextAction).toContain("Repair the source issue workspace link");
|
||||
|
||||
const comments = await db.select().from(issueComments).where(eq(issueComments.issueId, issueId));
|
||||
expect(comments.some((comment) => comment.body.includes("workspace failed validation"))).toBe(true);
|
||||
const validationComment = await waitForValue(async () => {
|
||||
const rows = await db.select().from(issueComments).where(eq(issueComments.issueId, issueId));
|
||||
return rows.find((comment) => comment.body.includes("workspace failed validation")) ?? null;
|
||||
});
|
||||
expect(validationComment).toBeTruthy();
|
||||
});
|
||||
|
||||
it("queues one finish-handoff wake when a successful run leaves in-progress work without a next action", async () => {
|
||||
|
||||
Reference in New Issue
Block a user