Skip to content

feat(ai-client): client-side chat persistence adapter#661

Open
AlemTuzlak wants to merge 6 commits into
mainfrom
feat/chat-client-persistence
Open

feat(ai-client): client-side chat persistence adapter#661
AlemTuzlak wants to merge 6 commits into
mainfrom
feat/chat-client-persistence

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented May 30, 2026

Summary

Adds an optional, opt-in persistence adapter to ChatClient so client-side conversations survive reloads — no manual initialMessages + onFinish boilerplate. Implements #374.

The adapter uses the same getItem/setItem/removeItem shape as the rest of the SDK and may be sync or async:

interface ChatClientPersistence {
  getItem: (id) => Array<UIMessage> | null | undefined | Promise<...>
  setItem: (id, messages) => void | Promise<void>
  removeItem: (id) => void | Promise<void>
}

const client = new ChatClient({ id: 'conversation-123', connection, persistence })

When provided, ChatClient:

  • hydrates from getItem(id) on construction (async hydration is race-guarded against a newly-started conversation),
  • saves via setItem(id, messages) on every message change, through an ordered write queue so writes never overlap or land out of order,
  • clears via removeItem(id) on clear(), discarding any in-flight stream so late chunks can't repopulate cleared state.

When omitted, behavior is unchanged — fully backwards compatible. Persistence is best-effort: adapter errors are swallowed so storage problems never break the chat.

Changes

  • @tanstack/ai-client: ChatClientPersistence interface + persistence option on ChatClient; hydrate / save-queue / clear integration; mid-stream clear chunk suppression. Exported from the package index.
  • Framework wrappers (react, preact, solid, vue, svelte): thread the persistence option through and read hydrated messages from the client after construction.
  • Docs: new docs/chat/persistence.md (interface, behavior, localStorage + IndexedDB examples), registered under Chat & Streaming.
  • Changeset: minor bump for all six packages.

Testing

  • New unit tests in ai-client (hydration sync/async, save-on-change, clear, mid-stream clear suppression) and in every framework wrapper.
  • New E2E test: persists chat across a browser reload with a localStorage adapter.
  • Verified locally: ai-client 248 unit tests pass; all wrapper unit tests pass; test:types and test:eslint clean across all six packages; ai-client builds; test:docs passes; the persistence E2E test passes (full E2E suite green).

Summary by CodeRabbit

  • New Features

    • Chat messages can now be persisted and automatically restored across page reloads using configurable storage adapters.
  • Documentation

    • Added a comprehensive guide showing how to enable persistence with examples for localStorage and IndexedDB; navigation updated to include the new page.
  • Tests

    • Added e2e and extensive unit tests to verify persistence, hydration, clearing behavior, and race conditions for reliable chat state.

Review Change Stack

Add an optional `persistence` adapter to ChatClient with the same
getItem/setItem/removeItem shape used elsewhere in the SDK. When
provided, the client hydrates from getItem(id) on construction (sync or
async), saves to setItem(id, messages) on every message change via an
ordered write queue, and calls removeItem(id) on clear(). Late chunks
from a stream cleared mid-flight are suppressed so they can't repopulate
cleared state. When omitted, behavior is unchanged.

The persistence option is threaded through all framework wrappers
(react, preact, solid, vue, svelte), which now read hydrated messages
from the client after construction.

Implements #374.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds ChatClient persistence docs and sidebar, a localStorage E2E adapter and Playwright test, a changeset for minor package bumps, and extensive unit tests across ChatClient, ChatPersistor, and framework hooks validating hydration, write sequencing, clear semantics, and race conditions.

Changes

Persistence Documentation and E2E Testing

Layer / File(s) Summary
Persistence feature documentation
docs/chat/persistence.md, docs/config.json
Documents the three-method ChatClientPersistence adapter (sync/async), ChatClient hydration and queued save behavior, clear semantics and error swallowing, framework integration examples (React/Preact/Solid/Vue/Svelte), and runnable localStorage/IndexedDB examples.
E2E route, adapter, and test
testing/e2e/src/routes/$provider/$feature.tsx, testing/e2e/tests/chat.spec.ts
Parses persistence=localStorage, provides a localStoragePersistence adapter that revives createdAt dates, computes a deterministic chatId for tests, passes id and adapter into useChat, and adds a Playwright test verifying messages persist after reload.
Release metadata
.changeset/chat-client-persistence.md
Creates a changeset marking multiple @tanstack/ai-* packages for minor release bumps with the note "Add persistence support for chat messages."
Core ChatClient tests
packages/ai-client/tests/chat-client.test.ts
Extensive tests added for constructor hydration rules, async hydration races, subscribe/send adapter abort handling, and many clear() + persistence interaction edge cases (stale/chunk suppression, session generation, persistence races).
ChatPersistor unit tests
packages/ai-client/tests/client-persistor.test.ts
New test suite covering read/hydrate, notify/remove sequencing, snapshotting, shouldIgnoreChunk suppression rules, and run-lifecycle ignore logic.
Framework hook tests (React/Preact/Solid)
packages/ai-preact/tests/use-chat.test.ts, packages/ai-react/tests/use-chat.test.ts, packages/ai-solid/tests/use-chat.test.ts
Adds persistence-related init tests, empty-persisted-array precedence, async hydration/id-race protections, callback lifecycle checks across id changes and StrictMode, client recreation on id change, and live-toggle subscription tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • tombeckenham
  • schiller-manuel

Poem

🐰 Persistence hops into the fray,
Messages saved to greet the day.
localStorage hums a lullaby,
IndexedDB watches time drift by —
Conversations wake when pages say hooray.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a client-side chat persistence adapter to the ai-client package.
Description check ✅ Passed The PR description comprehensively covers changes, motivation, and testing, with clear sections on implementation details and verification.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/chat-client-persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
docs/chat/persistence.md (1)

24-38: 💤 Low value

Interface snippet both imports and redeclares ChatClientPersistence.

The snippet does import type { ChatClientPersistence } and then interface ChatClientPersistence { ... }, which is a duplicate-identifier conflict if copied as-is, and UIMessage is referenced without being imported. Since this block is purely illustrating the shape, dropping the import (or showing it as the canonical definition only) avoids confusion.

📝 Suggested snippet cleanup
-import type { ChatClientPersistence } from "`@tanstack/ai-client`";
-
-interface ChatClientPersistence {
+import type { UIMessage } from "`@tanstack/ai-client`";
+
+interface ChatClientPersistence {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/chat/persistence.md` around lines 24 - 38, The snippet both imports and
redeclares ChatClientPersistence which causes a duplicate identifier and also
references UIMessage without importing it; fix by removing the redundant import
line (drop "import type { ChatClientPersistence }"), keep the canonical
interface ChatClientPersistence declaration, and either add an import type {
UIMessage } or include a minimal UIMessage type/placeholder so UIMessage is
defined for the example.
packages/typescript/ai-react/tests/use-chat.test.ts (1)

938-976: 💤 Low value

Inconsistent indentation in test block.

The it block and its contents have inconsistent indentation (6 spaces for it, 8 spaces for body). While syntactically valid, this makes the test structure harder to follow.

The test logic is correct - it verifies that callbacks from stale clients are ignored after id changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/typescript/ai-react/tests/use-chat.test.ts` around lines 938 - 976,
The test block for "should ignore user callbacks from an old client after id
changes" has inconsistent indentation; fix by normalizing spacing for the `it`
block and its body to match the project's style (e.g., 2 spaces per indent) so
the `it(...)` line and its inner lines (variables like releaseOldStream,
oldOnChunk, newOnChunk, adapter, the renderHook call for useChat, sendPromise,
rerender, releaseOldStream.resolve, and the final expects) are consistently
indented; ensure nested blocks (async function, await/waitFor, and the rerender
call) follow the same indentation level used across other tests.
packages/typescript/ai-preact/src/use-chat.ts (1)

77-80: 💤 Low value

Minor inconsistency with React version's onResponse handling.

The Preact version uses return optionsRef.current.onResponse?.(response) while the React version uses void optionsRef.current.onResponse?.(response). Both are functionally equivalent since the return value isn't used, but the inconsistency could cause confusion during maintenance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/typescript/ai-preact/src/use-chat.ts` around lines 77 - 80, The
onResponse handler in use-chat.ts returns the result of
optionsRef.current.onResponse?.(response) which is inconsistent with the React
variant that uses void; update the onResponse implementation (the function that
checks activeClientRef.current against instance) to call
optionsRef.current.onResponse?.(response) with the void prefix (i.e., void
optionsRef.current.onResponse?.(response)) instead of returning it so the
behavior and style match React's use of onResponse.
packages/typescript/ai-preact/tests/use-chat.test.ts (1)

1370-1371: 💤 Low value

Preact tests include additional status assertion for error scenarios.

The Preact error tests assert expect(result.current.status).toBe('error') which the React tests don't include. This is actually improved coverage - consider adding the same assertions to the React tests for parity.

Also applies to: 1390-1391

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/typescript/ai-preact/tests/use-chat.test.ts` around lines 1370 -
1371, Add the same error-status assertion used in the Preact tests to the
corresponding React tests: after the existing error expectation(s) add
expect(result.current.status).toBe('error') in the React use-chat test cases
that mirror the Preact ones (the two locations corresponding to the Preact lines
around 1370 and 1390), so the React tests assert result.current.status ===
'error' for those error scenarios.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/typescript/ai-client/src/chat-client.ts`:
- Around line 45-48: The Sets clearedMessageIds and clearedToolCallIds (and
similarly clearedRunIds, ignoredActiveRunIds) grow unbounded because nothing
ever removes entries; fix by adding eviction/pruning: replace the raw Sets with
a small LRU or Map<string, number> that records insertion timestamps for
clearedMessageIds and clearedToolCallIds (and
clearedRunIds/ignoredActiveRunIds), and prune old entries on each
clear()/send()/persist cycle or enforce a max size and evict oldest entries;
update any code that checks membership to use the new Map/LRU (e.g., wherever
clearedMessageIds.has(...) or clearedToolCallIds.has(...) is used) and add a
pruneOldEntries() utility called from clear(), send(), and the persistence load
path so suppression state does not leak indefinitely.
- Around line 485-504: The bug is that currentRunlessRunId is a single slot so
stale runless native chunks can slip through; replace that single-slot tracking
with a Set (e.g., runlessRunIds: Set<string>) and update all places that
read/write currentRunlessRunId (handlers that process RUN_STARTED/RUN_ENDED,
clear(), and any assignment sites) to add/remove the run id to/from
runlessRunIds instead of overwriting a single value; then update
isRunlessChunkFromIgnoredRun to call getChunkRunId and, when a chunk has no
runId, check whether any id in runlessRunIds is present in ignoredActiveRunIds
or clearedRunIds (treat chunk as ignored if so) so multiple concurrent runless
runs are protected.

---

Nitpick comments:
In `@docs/chat/persistence.md`:
- Around line 24-38: The snippet both imports and redeclares
ChatClientPersistence which causes a duplicate identifier and also references
UIMessage without importing it; fix by removing the redundant import line (drop
"import type { ChatClientPersistence }"), keep the canonical interface
ChatClientPersistence declaration, and either add an import type { UIMessage }
or include a minimal UIMessage type/placeholder so UIMessage is defined for the
example.

In `@packages/typescript/ai-preact/src/use-chat.ts`:
- Around line 77-80: The onResponse handler in use-chat.ts returns the result of
optionsRef.current.onResponse?.(response) which is inconsistent with the React
variant that uses void; update the onResponse implementation (the function that
checks activeClientRef.current against instance) to call
optionsRef.current.onResponse?.(response) with the void prefix (i.e., void
optionsRef.current.onResponse?.(response)) instead of returning it so the
behavior and style match React's use of onResponse.

In `@packages/typescript/ai-preact/tests/use-chat.test.ts`:
- Around line 1370-1371: Add the same error-status assertion used in the Preact
tests to the corresponding React tests: after the existing error expectation(s)
add expect(result.current.status).toBe('error') in the React use-chat test cases
that mirror the Preact ones (the two locations corresponding to the Preact lines
around 1370 and 1390), so the React tests assert result.current.status ===
'error' for those error scenarios.

In `@packages/typescript/ai-react/tests/use-chat.test.ts`:
- Around line 938-976: The test block for "should ignore user callbacks from an
old client after id changes" has inconsistent indentation; fix by normalizing
spacing for the `it` block and its body to match the project's style (e.g., 2
spaces per indent) so the `it(...)` line and its inner lines (variables like
releaseOldStream, oldOnChunk, newOnChunk, adapter, the renderHook call for
useChat, sendPromise, rerender, releaseOldStream.resolve, and the final expects)
are consistently indented; ensure nested blocks (async function, await/waitFor,
and the rerender call) follow the same indentation level used across other
tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 04182668-4a8b-412c-9a75-4c205200e6e2

📥 Commits

Reviewing files that changed from the base of the PR and between 548e113 and df49575.

📒 Files selected for processing (20)
  • .changeset/chat-client-persistence.md
  • docs/chat/persistence.md
  • docs/config.json
  • packages/typescript/ai-client/src/chat-client.ts
  • packages/typescript/ai-client/src/connection-adapters.ts
  • packages/typescript/ai-client/src/index.ts
  • packages/typescript/ai-client/src/types.ts
  • packages/typescript/ai-client/tests/chat-client.test.ts
  • packages/typescript/ai-preact/src/use-chat.ts
  • packages/typescript/ai-preact/tests/use-chat.test.ts
  • packages/typescript/ai-react/src/use-chat.ts
  • packages/typescript/ai-react/tests/use-chat.test.ts
  • packages/typescript/ai-solid/src/use-chat.ts
  • packages/typescript/ai-solid/tests/use-chat.test.ts
  • packages/typescript/ai-svelte/src/create-chat.svelte.ts
  • packages/typescript/ai-svelte/tests/use-chat.test.ts
  • packages/typescript/ai-vue/src/use-chat.ts
  • packages/typescript/ai-vue/tests/use-chat.test.ts
  • testing/e2e/src/routes/$provider/$feature.tsx
  • testing/e2e/tests/chat.spec.ts

Comment thread packages/typescript/ai-client/src/chat-client.ts Outdated
Comment thread packages/typescript/ai-client/src/chat-client.ts Outdated
Move all persistence concerns out of ChatClient into a dedicated
ChatPersistor: storage orchestration (hydrate / save / remove via an
ordered write queue with generation guards) and clear-during-stream
chunk suppression (cleared/ignored id tracking). ChatClient now holds a
single optional `persistor` and delegates, keeping it focused on
streaming and message state.

The run-lifecycle methods keep their activeRunIds / session-generating /
processing side-effects and call persistor hooks (onRunStarted,
onRunSettled, onSessionRunError, resetIgnored, takeRunlessRunId) for the
cleared-set bookkeeping. A shared getChunkRunId helper is exported from
connection-adapters so both modules use one source.

Pure refactor: no public API or behavior change. All ai-client unit
tests, framework wrapper tests, and the persistence E2E test pass
unchanged; chat-client.js shrinks ~34kB -> ~28kB.
Add a dedicated isolation suite for the extracted ChatPersistor (31
tests) covering edge cases that are awkward to trigger through the full
ChatClient:

- readInitial: sync read, async promise passthrough, throwing adapter
- hydrateAsync: array / null / undefined / non-promise / rejection, plus
  the generation guard that drops a stale async hydration after a local
  change
- notifyMessagesChanged: persists a snapshot (not the live array), skips
  exactly one write after beginClear, swallows sync errors, runs async
  writes FIFO with no overlap, and isolates rejections
- remove: removeItem, swallowed errors, and generation invalidation of a
  queued write a removal supersedes
- shouldIgnoreChunk: every branch (cleared message id, cleared run id,
  in-flight currentRunId, runless content/snapshot, parentMessageId with
  toolCallId memoization)
- run lifecycle hooks: onRunSettled forget + runless-pointer advance,
  onSessionRunError, takeRunlessRunId, resetIgnored (partial reset), and
  the guard that a non-cleared run is never suppressed

Adds shared createUIMessage / createMockPersistence helpers to
test-utils. Full ai-client suite: 279 passing.
…istence

# Conflicts:
#	packages/ai-client/src/chat-client.ts
#	packages/ai-client/src/client-persistor.ts
#	packages/ai-client/src/types.ts
#	packages/ai-client/tests/chat-client.test.ts
#	packages/ai-client/tests/client-persistor.test.ts
#	packages/ai-client/tests/test-utils.ts
#	packages/ai-preact/src/use-chat.ts
#	packages/ai-react/src/use-chat.ts
#	packages/typescript/ai-client/src/connection-adapters.ts
#	testing/e2e/src/routes/$provider/$feature.tsx
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 30, 2026

🚀 Changeset Version Preview

6 package(s) bumped directly, 5 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-elevenlabs 0.2.14 → 1.0.0 Dependent
@tanstack/ai-openai 0.10.4 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.4 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.4 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai-client 0.14.0 → 0.15.0 Changeset
@tanstack/ai-preact 0.7.0 → 0.8.0 Changeset
@tanstack/ai-react 0.13.0 → 0.14.0 Changeset
@tanstack/ai-solid 0.11.0 → 0.12.0 Changeset
@tanstack/ai-svelte 0.11.0 → 0.12.0 Changeset
@tanstack/ai-vue 0.11.0 → 0.12.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-vue-ui 0.2.7 → 0.2.8 Dependent

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented May 30, 2026

View your CI Pipeline Execution ↗ for commit 1f67db3

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-30 16:41:13 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 30, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@661

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@661

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@661

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@661

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@661

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@661

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@661

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@661

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@661

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@661

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@661

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@661

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@661

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@661

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@661

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@661

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@661

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@661

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@661

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@661

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@661

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@661

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@661

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@661

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@661

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@661

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@661

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@661

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@661

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@661

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@661

commit: 787c99a

The test relied on a 10ms timer racing against vi.waitFor to ensure
clear() ran before the delayed chunks. On faster machines/CI the chunks
were processed before clear(), so clear() wiped them and the assertion
saw an empty message list. Gate the chunks on a deferred released after
clear() instead, making the ordering deterministic across platforms.
@AlemTuzlak AlemTuzlak requested a review from tombeckenham May 30, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant