Repository navigation
fix(providers): stop conversation-sync re-send loops - #620
Merged
uladzislausvetlakou merged 8 commits intoOct 9, 2026
Merged
uladzislausvetlakou merged 8 commits into
uladzislausvetlakou merged 8 commits into
Conversation
applyPayloadOutcome now updates all pending/failed records with the sent payloadId, and the send loop sends each id at most once per run, so a duplicate of an already-synced turn is no longer re-sent on every tick. Refs: EPMCDME-15771 (conversation-sync-loop-fix, Task 1) Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…n payloads Before choosing what to send, pending or retryable failed records whose payloadId already has a success record are marked success without being sent, and the healed queue is persisted even when the run defers. Refs: EPMCDME-15771 (conversation-sync-loop-fix, Task 2) Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…geUuid The sync step no longer reports lastSyncedMessageUuid; each transform processor stays the sole writer of its pointer. lastSyncedHistoryIndex is the max index among successful payloads and conversationId comes from the last successfully sent payload. parseSourceIndex is removed. Refs: EPMCDME-15771 (conversation-sync-loop-fix, Task 3) Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…sync The Claude conversations processor reads the conversation queue once before its drain loop and does not append a turn whose payloadId is already queued in any status. The sync pointer and per-iteration checkpoint still advance, and a skip-only run still returns its sync update. Refs: EPMCDME-15771 (conversation-sync-loop-fix, Task 4) Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…wer record Adds a terminal 'superseded' status. Before sending, an older candidate whose every (conversationId, history_index, role) entry is carried by a newer candidate is marked superseded, persisted and never sent. Records with an empty history or entries lacking history_index/role (codex sentinels) are left untouched, and partly covered records are sent whole. Refs: EPMCDME-15771 (conversation-sync-loop-fix, Task 5) Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Adds MAX_CONVERSATION_PAYLOADS_PER_RUN = 50. After healing and collapse, candidates are sent oldest first up to the cap; the rest stay queued for the next run. Healed and superseded records do not count toward the cap. Refs: EPMCDME-15771 (conversation-sync-loop-fix, Task 6) Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…dupe - skip the per-run send cap on deadline-bounded SessionEnd runs so no payloads are stranded - extract queue preparation into prepareSendList and readQueuedPayloadIds - rename payloadQueue.ts to payload-queue.ts (kebab-case) - cover an already-queued turn followed by a new turn in one drain Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Generated with AI Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
bohdanmaliar
force-pushed
the
fix/conversation-sync-loop
branch
from
October 9, 2026 11:46
68dfd67 to
35055ba
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Since v0.15.2,
applyPayloadOutcomein the conversation sync step markedonly the first queued record for a given payload id as sent.
Duplicates came from two sources:
lastSyncedMessageUuidto the latest successfulpayload, re-queuing already-sent turns under new history indices;
already-queued check.
This PR removes both causes on the client and bounds per-run work.
Changes
payload id is now resolved together (success/failed), restoring
pre-regression "mark all by id" semantics while keeping per-payload
persistence and deadline deferral.
pending/retryable-failedrecord whose payload idalready has a
successrecord is markedsuccesswithout beingre-sent. Queues left behind by older CLI versions self-heal on first sync
after upgrade.
syncProcessorno longer writesconversations.lastSyncedMessageUuid; each transform processor remainsthe sole writer of its own pointer, so already-synced turns are never
re-queued.
ConversationsProcessor.processMessagesreads the existing queue once before appending and skips any result whose
payload id is already queued (any status), mirroring the Codex processor.
Its checkpoint still advances.
marked
superseded(not sent, not counted as a failure) when every(conversationId, history_index, role)key it carries is also covered bya newer record. Partial coverage is sent in full.
MAX_CONVERSATION_PAYLOADS_PER_RUN = 50bounds API sendsper sync run; remaining candidates roll over to the next run.
Read pipeline order: heal → collapse → cap → send.
New
supersededstatus added toConversationPayloadRecord['status'].Acceptance criteria covered
docs/stories/2026-10-09-conversation-sync-loop-fix/story.md(duplicate dedupe, healing, no pointer rewind, no re-queue after partial
failure, concurrent-hook dedupe, 50/run cap, collapse of superseded
records, no regression for normal sessions).
Out of scope
start picks up the fix.
Test plan
syncProcessor-dedupe.test.tscovering duplicate ids with mixedsuccess/pending, healing on read, collapse (including Assistant-only
continuation and partial coverage), the 50-record cap, and absence of
lastSyncedMessageUuidfromsyncUpdates.claude.conversations-processor-dedupe.test.tscoveringskip-already-queued-payload-id with checkpoint advancement.