Skip to content

fix: isolate assistant projection identities per thread#882

Open
binbandit wants to merge 3 commits intopingdotgg:mainfrom
binbandit:fix/issue-871-thread-message-isolation
Open

fix: isolate assistant projection identities per thread#882
binbandit wants to merge 3 commits intopingdotgg:mainfrom
binbandit:fix/issue-871-thread-message-isolation

Conversation

@binbandit
Copy link
Copy Markdown
Contributor

@binbandit binbandit commented Mar 11, 2026

Summary

  • key projected thread messages by (thread_id, message_id) so reused assistant IDs no longer overwrite another thread's projection row
  • make new runtime/checkpoint assistant identities thread-scoped while preserving legacy persisted IDs during upgrade and resume flows
  • add regression coverage for cross-thread item ID reuse, legacy upgrade resume behavior, and checkpoint fallback identity resolution

Why

  • issue Assistant message projections collide across threads when message IDs are reused #871 is a reliability bug in the core thread projection path: reusing a provider-backed assistant identity across threads can overwrite the first thread's assistant message
  • the original persistence conflict key used message_id alone, and runtime-generated assistant IDs were not consistently thread-scoped
  • upgraded installs also needed compatibility so existing persisted assistant/checkpoint IDs keep resolving instead of duplicating assistant messages after resume

Changes

  • add migration 014_ProjectionThreadMessagesThreadScopedKey to rebuild projection_thread_messages with a composite primary key on (thread_id, message_id)
  • update ProjectionThreadMessages upserts and attachment carry-forward logic to scope conflicts and lookups by thread
  • add assistantMessageIdentity helpers and use them in ProviderRuntimeIngestion and CheckpointReactor to mint thread-scoped IDs for new data while reusing legacy IDs when they already exist in projections
  • update websocket/runtime expectations and add focused regression tests for persistence isolation and legacy compatibility

Validation

  • bun fmt
  • bun lint
  • bun typecheck
  • bun run test src/orchestration/assistantMessageIdentity.test.ts src/persistence/Layers/ProjectionThreadMessages.test.ts src/orchestration/Layers/ProviderRuntimeIngestion.test.ts src/wsServer.test.ts

Note

Isolate assistant message projection identities per thread using composite (thread_id, message_id) keys

  • Introduces assistantMessageIdentity.ts with helpers that produce deterministic, thread-scoped assistant message IDs, reusing any legacy or persisted IDs for a given turn when available.
  • Updates ProviderRuntimeIngestion and CheckpointReactor to resolve assistant message IDs via these helpers instead of constructing them inline.
  • Changes ProjectionThreadMessages upsert logic to key rows by (thread_id, message_id) instead of message_id alone, allowing the same message ID to exist independently across threads.
  • Adds migration 016_ProjectionThreadMessagesThreadScopedKey to rebuild the projection_thread_messages table with a composite primary key and migrate existing data.
  • Risk: this migration alters the primary key of projection_thread_messages; existing rows are preserved but the schema change is irreversible without manual intervention.

Macroscope summarized 8aa0b74.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 11, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8ad5749c-f4ab-4e48-8285-31a88dad5fb3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Mar 11, 2026
@binbandit
Copy link
Copy Markdown
Contributor Author

@juliusmarminge if we can, can we please prioritise this one?

thread: AssistantMessageIdentityThread,
event: ProviderRuntimeEvent,
): MessageId {
const turnId = toTurnId(event.turnId);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

when is event.turnId a string here that needs coercing?

@github-actions github-actions Bot added the size:XL 500-999 changed lines (additions + deletions). label Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants