Files
paperclip/server
dmndbrp-oss 0186e66c7c test(SAG-4328): de-flake checkout-lock race in orphan-recovery test (#8315)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server heartbeat subsystem tracks agent runs and recovers
orphaned processes via `reapOrphanedRuns`
> - When a local process dies mid-run, the recovery path queues a retry
run and releases the checkout lock asynchronously
> - The test `queues exactly one retry when the recorded local pid is
dead` verifies both the retry state and that the checkout lock is
released
> - The `checkoutRunId` null-check was a bare synchronous assertion, but
the checkout-lock release is async — so the test could read
`checkoutRunId` before the write committed
> - This PR wraps the predicate already gating on `executionRunId` to
also wait for `checkoutRunId === null`, so both writes are committed
before the assertions fire
> - The benefit is a deterministic test that does not race the async
checkout-lock release

## Linked Issues or Issue Description

-

## What Changed

- `server/src/__tests__/heartbeat-process-recovery.test.ts`: Extended
the `waitForValue` predicate (line ~1024) from `executionRunId ===
retryRun?.id` to also require `checkoutRunId === null` before returning
the row. This eliminates the race between the assertion and the async
terminal-run checkout-lock release.

## Verification

- No production code changed — test file only.
- Ran the test file 10× locally with `pnpm exec vitest run
server/src/__tests__/heartbeat-process-recovery.test.ts
--no-file-parallelism --maxWorkers=1`:

```
Run  1: Test Files 1 passed (1) | Tests 57 passed (57)
Run  2: Test Files 1 passed (1) | Tests 57 passed (57)
Run  3: Test Files 1 passed (1) | Tests 57 passed (57)
Run  4: Test Files 1 passed (1) | Tests 57 passed (57)
Run  5: Test Files 1 passed (1) | Tests 57 passed (57)
Run  6: Test Files 1 passed (1) | Tests 57 passed (57)
Run  7: Test Files 1 passed (1) | Tests 57 passed (57)
Run  8: Test Files 1 passed (1) | Tests 57 passed (57)
Run  9: Test Files 1 passed (1) | Tests 57 passed (57)
Run 10: Test Files 1 passed (1) | Tests 57 passed (57)
```

10/10 green streak — race eliminated.

## Risks

Low risk — test-only change. No production code modified. The predicate
change only narrows the waitForValue poll to require both writes
committed before asserting, matching the intent already documented in
the comment on line 1031.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

Claude Sonnet 4.6 (claude-sonnet-4-6) — Anthropic, 200K context, tool
use enabled.

## 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] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Director of Engineering <cto@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Devin Foley <devin@paperclip.ing>
2026-06-19 08:59:23 -07:00
..
2026-03-12 13:09:22 -05:00