feat(assistant): give a conversation turn an explicit identity - #318
Conversation
A turn writes its question in beginTurn and its answer in completeTurn, in separate transactions. Two turns of one conversation can therefore persist as U1, U2, A2, A1, and no ordering heuristic over sequence_id pairs them. Nothing reads the pairing today, but the transcript context reader that replaces Spring AI's chat memory must, and it cannot recover from sequence order what the writers already knew. beginTurn now allocates a turn id and returns it with the conversation id; completeTurn takes that reference and writes the answer under the same identity. A partial unique index over (turn_id, role) holds one question and one answer per turn. Rows written before this migration keep a null turn_id: they stay visible in the transcript and are exempt from the index, because their pairing cannot be recovered after the fact. Two turns completing at the same instant still raise an optimistic-locking failure through the unlocked conversation touch in completeTurn. That is pre-existing and unrelated to turn identity; it is recorded as a gap in the Assistant test matrix rather than fixed here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (10)
📜 Recent review details⏰ Context from checks skipped due to timeout. (3)
🧰 Additional context used📓 Path-based instructions (4)**/*📄 CodeRabbit inference engine (AGENTS.md)
Files:
core/src/main/resources/db/migration/*.sql⚙️ CodeRabbit configuration file
Files:
**/*.java📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/api/src/main/java/**/*.java⚙️ CodeRabbit configuration file
Files:
🧠 Learnings (2)📚 Learning: 2026-07-23T23:30:44.585ZApplied to files:
📚 Learning: 2026-07-26T05:46:47.443ZApplied to files:
🪛 Squawk (2.61.0)core/src/main/resources/db/migration/V26__assistant_message_turn_identity.sql[warning] 18-20: During normal index creation, table updates are blocked, but reads are still allowed. Use (require-concurrent-index-creation) 🔇 Additional comments (10)
📝 WalkthroughWalkthroughThe change introduces ChangesAssistant turn identity
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant AssistantController
participant AssistantConversationService
participant AssistantConversationMessage
participant PostgreSQL
AssistantController->>AssistantConversationService: beginTurn(CurrentActor, conversationId, question)
AssistantConversationService->>AssistantConversationMessage: persist user message with turnId
AssistantConversationMessage->>PostgreSQL: insert conversation message
AssistantConversationService-->>AssistantController: return AssistantTurnRef
AssistantController->>AssistantConversationService: completeTurn(AssistantTurnRef)
AssistantConversationService->>AssistantConversationMessage: persist assistant message with turnId
AssistantConversationMessage->>PostgreSQL: insert paired message
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
TegamiThis repository uses Tegami to manage releases. When your changes affect published packages, add a changelog file under Create a changelog → · Changelog format Release preview
This PR does not add changelog files. Pending changelogs from other branches are included in the preview above. Run Managed by Tegami. |
First of two PRs collapsing the Assistant conversation to one persisted store
(
docs/increments/active/2026-08-06-assistant-conversation-memory-ssot). Thisone only establishes turn identity; nothing reads it yet.
Why
beginTurnpersists the question andcompleteTurnpersists the answer, inseparate transactions. Two turns of one conversation can open before either
answers, so the rows can land as
U1, U2, A2, A1. No ordering heuristic oversequence_idpairs those correctly.The read-only transcript context advisor that replaces
MessageChatMemoryAdvisorin the next PR has to pair them. Recording the pairing the writers already know
is cheaper and correct; inferring it later is neither. Shipping the schema first
means a wrong advisor never forces a migration rollback.
What
V26addsturn_idplus a partial unique index on(turn_id, role)— onequestion and one answer per turn.
beginTurnallocates the turn id and returnsAssistantTurnRef;completeTurntakes that reference instead of a bare conversation id.turn_id. They stay visible inthe transcript and are exempt from the index: their pairing cannot be
recovered after the fact, and the debate's binding constraint is that legacy
rows are transcript-visible but context-ineligible.
Verification
:core:testand:apps:api:testgreen. NewAssistantTurnIdentityIntegrationTestscovers pairing, the out-of-order
U1,U2,A2,A1interleaving, 12 concurrentbeginTurncalls in one conversation, the unique-index rejection, and legacynull rows coexisting.
Found, not fixed
Two turns of one conversation completing at the same instant raise an
optimistic-locking failure:
completeTurntouches the conversation row withouta lock while
beginTurntakes a pessimistic one. Pre-existing onmainandunrelated to turn identity — recorded as a gap in the Assistant test matrix.
skip-release: internal turn identity and schema only; no user-visible behavior changes in this PR
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests