Files
twenty/packages
Félix Malfait 6ac8ebcd18 test(ai): pin StreamAgentChatJob stream lifecycle with a reusable spec harness (#22479)
## Rationale

`StreamAgentChatJob` is the most failure-sensitive path in the AI chat
stack — it coordinates the model stream, Redis event publishing, message
persistence, the thread's stream claim, and the queued-message flush —
and it had **zero unit coverage**. Both historical hangs lived here:

- a throw before the model stream merges bypassed `onFinish` entirely
and hung the job until the **10-minute BullMQ lock** expired (thread
stuck the whole time);
- a throw inside `onFinish` (persistence failure) left trailing chunks
published with **no terminal event** — the client spinner ran forever.

Production still shows this class is live: `Query read timeout` thrown
from inside `handleStreamFinish`
([TWENTY-SERVER-GV7](https://twenty-v7.sentry.io/issues/TWENTY-SERVER-GV7)).

## Why this shape, not something else

Tests-only PR, zero production risk. The fake chat stream mirrors the
one AI SDK contract the job's coordination depends on — verified against
the installed `ai@6.0.97` dist: `toUIMessageStream` converts mid-stream
errors into error parts and **always** fires `onFinish` when the stream
ends (`handleUIMessageStreamFinish` invokes it from both `flush()` and
`cancel()`). Pinning that contract in the fake means a future SDK
upgrade that breaks it fails these tests instead of production. Six
tests pin current behavior: chunk ordering with `message-persisted`
last, opaque error-chunk suppression, mid-stream failure persisting
`lastStreamError` + releasing the claim, the two hang regressions above,
and cancel skipping the queued flush.

Three sibling PRs extend this exact spec file (missing-workspace
routing, halted queue, and — later — auto-retry), which is why the
harness lands first.

## User impact

None directly; it makes the two worst historical user-facing hangs
(10-minute dead thread, infinite spinner) regression-proof before the
stuck-state fix series touches this code.

## Test plan

- [x] 6 unit tests, no production code changed
- [ ] CI green

https://claude.ai/code/session_01Lyi6zTema2FMVVh8MD6c38

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Lyi6zTema2FMVVh8MD6c38)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22479?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-02 21:01:32 +02:00
..