test(server): give the description-edit admission poll the same retry budget as its siblings (#17487)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - One subsystem is the chat channel control plane. It admits inbound
GitHub mentions and then creates a chat conversation.
> - Admission is durable. A transient database error defers the wakeup
and retries it later.
> - The retry delay has a floor of 1000 ms (`1000 * 2 ** attemptCount`
in `server/src/services/chat-channels/inbound/wakeups.ts`).
> - One integration test waits 1000 ms for the conversation. Any
deferred retry therefore fails the test, although the product works.
> - This pull request gives that one wait the same 10 s budget that the
neighbouring tests already use.
> - The benefit is a stable `chat (2/3)` CI shard. No production code
changes.

## Linked Issues or Issue Description

No public GitHub issue exists for this test defect. This is a bug
report.

**What happened?**

The test "authorizes description edits as the editor, never inheriting
the original author's access" failed in CI at
`server/src/__tests__/chat-channels.integration.test.ts:5636`.

The assertion was `expected +0 to be 1`. It is an `expect.poll` call
without a timeout, so the budget is the 1000 ms default.

The server log from the same job shows that the application did not drop
the conversation. It shows a deferred admission instead:

```
WARN: accepted inbound chat wakeup will retry {"deliveryId":"..."}
caused by: PostgresError: could not obtain lock on row in relation "chat_endpoints"
```

`acceptInboundWakeup` sets `retryAt = now + Math.min(30_000, 1000 * 2 **
Math.min(attemptCount, 5))` on such errors. The floor is 1000 ms. One
deferred retry always exceeds the poll budget.

**Expected behavior**

The test waits long enough for one deferred retry. It then passes,
because the conversation is created.

**Steps to reproduce**

1. Check out `master`.
2. Run `cd server && ../node_modules/.bin/vitest run
src/__tests__/chat-channels.integration.test.ts -t "authorizes
description edits as the editor" --config vitest.config.ts`.
3. Add database contention on the `chat_endpoints` row during the second
`send()`, so the first admission attempt fails with `could not obtain
lock`.
4. The wakeup is deferred with a 1000 ms delay. The poll stops at 1000
ms and fails.

**Paperclip version or commit**

`master` at `991c47b`. The failure was observed on a pull request branch
that does not change the chat code.

**Deployment mode**

Local trusted, embedded Postgres test runner.

## What Changed

- Add `{ timeout: 10_000 }` to the `expect.poll` call at
`server/src/__tests__/chat-channels.integration.test.ts:5636`.
- This matches the three neighbouring tests that poll the same
conversation count (lines 5577, 5658, 5721).

## Verification

- Before the change, one deferred admission fails the assertion, because
the default budget is 1000 ms and the retry floor is 1000 ms.
- After the change, on `master` at `991c47b`:

```
cd server && ../node_modules/.bin/vitest run src/__tests__/chat-channels.integration.test.ts \
  -t "authorizes description edits as the editor" --config vitest.config.ts
→ Test Files 1 passed (1); Tests 1 passed | 1421 skipped (1422)
```

- The complete file was not re-run locally. The targeted test covers the
changed assertion. All other lines of the test stay the same.

## Risks

- Low risk. The change touches test code only.
- A longer budget can hide a slow admission, but only up to 10 s. The
neighbouring tests already accept that budget for the same behaviour.
- A real loss of the conversation still fails the test.

## Model Used

- Provider: OpenCode. Model: `deepseek-v4.1-flash-free`. Context window:
200K. Modes: tool use, code execution, file access. No 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
- [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
- [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
- [ ] 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: ARTeMobAgents <11891309+ARTeMobAgents@users.noreply.github.com>
This commit is contained in:
ARTeMobAgentsandARTeMobAgents authored and GitHub committed 2026-10-29 18:33:15 -05:00
1 parent 1843d27e09
commit 4de82bc12e
1 file changed
+4 -1
@@ -6972,7 +6972,10 @@ describeEmbeddedPostgres("chat channel control-plane integration", () => {
expect(f.wakeup).not.toHaveBeenCalled();
const allowed = { ...payload, sender: { id: 42, login: "octocat", type: "User" }, issue: { ...payload.issue, user: payload.sender } };
await send(allowed);
await expect.poll(async () => (await readChatConversationsByEndpointId(f.endpoint.id)).length).toBe(1);
// A transient `chat_endpoints` row lock defers admission with a retry
// floor of 1000 ms (`chat-channels/inbound/wakeups.ts`), so allow one
// deferred retry instead of the 1000 ms poll default.
await expect.poll(async () => (await readChatConversationsByEndpointId(f.endpoint.id)).length, { timeout: 10_000 }).toBe(1);
const [conversation] = await readChatConversationsByEndpointId(f.endpoint.id);
const comments = await readIssueCommentsByIssueId(conversation.issueId!);
expect(comments).toHaveLength(1);