Skip to content

fix: reject stale conversation turns - #162

Merged
maralbahari merged 1 commit into
mainfrom
codex/issue-102-conversation-version-check
Aug 4, 2026
Merged

fix: reject stale conversation turns#162
maralbahari merged 1 commit into
mainfrom
codex/issue-102-conversation-version-check

Conversation

@franciscojavierarceo

@franciscojavierarceo franciscojavierarceo commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • capture a typed conversation version alongside rehydrated history
  • reject stale explicit-conversation turns under the existing row lock before persistence
  • return a consistent conversation_locked error over HTTP, SSE, and WebSocket, including generate: false
  • preserve previous_response_id fork behavior and the existing unchecked storage API
  • add SQLite, transport, and two-pool PostgreSQL race coverage plus deployment documentation

This builds on the PostgreSQL persistence foundation merged in #142.

This implements optimistic conflict detection at persistence time. A pre-inference distributed lease or queue that also suppresses external tool side effects remains separate policy work.

Test Plan

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test
  • pre-commit run --all-files
  • postgres_concurrent_conversation_writes_have_contiguous_sequences against PostgreSQL 17
  • postgres_optimistic_conversation_conflict against PostgreSQL 17
  • postgres_lock_wait_is_bounded_without_blocking_other_conversations against PostgreSQL 17
  • read-only Claude review: no actionable findings
  • gstack pre-landing review: all in-scope actionable findings resolved

@maralbahari

Copy link
Copy Markdown
Collaborator

@franciscojavierarceo can retarget to main?

@franciscojavierarceo
franciscojavierarceo changed the base branch from codex/issue-102-postgres-pool-config to main August 3, 2026 14:13
@franciscojavierarceo
franciscojavierarceo force-pushed the codex/issue-102-conversation-version-check branch from 88da78c to 1aaff97 Compare August 3, 2026 14:13
.await
.map_err(ExecutorError::Storage)
.map_err(|error| match error {
StorageError::ConversationConflict { .. } => ExecutorError::ConversationLocked,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

converts StorageError::ConversationConflict { conversation_id } into the unit-like ExecutorError::ConversationLocked. Consequently, persist.rs (line 30) logs only a generic message, and Error::source() cannot reach the original typed error. This makes concurrent-write incidents impossible to correlate with a conversation. Preserve StorageError as a #[source] while keeping the client-safe response.

Capture the persisted conversation version during rehydration and verify it under the existing storage lock before appending. Return conversation_locked consistently across HTTP, SSE, and WebSocket when the history changes, with SQLite and PostgreSQL race coverage.

Signed-off-by: Francisco Javier Arceo <farceo@redhat.com>
@franciscojavierarceo
franciscojavierarceo force-pushed the codex/issue-102-conversation-version-check branch from 1aaff97 to 228de23 Compare August 3, 2026 16:07
@maralbahari
maralbahari merged commit 80e3095 into main Aug 4, 2026
5 checks passed
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.

2 participants