fix(history): treat mid-turn absorption as delivery, not withdrawal - #14
Open
Antisophy wants to merge 1 commit into
Open
fix(history): treat mid-turn absorption as delivery, not withdrawal#14Antisophy wants to merge 1 commit into
Antisophy wants to merge 1 commit into
Conversation
Antisophy
force-pushed
the
fix/midturn-absorption-delivery
branch
from
August 16, 2026 17:31
d6153fc to
880e2c3
Compare
Recent Claude CLIs absorb queued messages into the running turn at a tool boundary instead of holding them for the next one: the entry is removed from the queue *after* delivery, and the delivery is recorded as a queued_command attachment rather than as a user line. Every remove was reported as consumed_as "removed", so messages the model demonstrably received were badged "not delivered", and the delivery itself was invisible because attachment records were never translated. Translate queued_command attachments into user messages and pair each one strictly with a remove. The pair's records land in either order, so an attachment against a still-queued entry is held until the entry's fate is known: a remove completes the absorption and releases it, a dequeue discards it (the echo carries the message in that flow, where recent CLIs also write the attachment as presentation), and one matching nothing renders nothing. An entry removed with no delivery record keeps the not-delivered outcome, so a genuine queue clear still reports honestly. Queue records are matched resiliently, because content is an unreliable key: the CLI omits it entirely for array-valued messages and texts can repeat. Matching prefers an exact text match, then empty-to-empty (an empty search names an array message, whose entry is stored empty too), and a remove finally settles the oldest unsettled entry, since the queue really did remove one. Echo pairing follows the same principle: dequeue records do not map enqueues one to one (the CLI can drain several entries under one dequeue, joining their texts into one echo), so the echo prefers the awaiting entry whose text it carries and a joined echo settles every queued entry whose text appears in it. Any entry left permanently unsettled would shift every later pairing behind by one, confirming each echo against the previous message's provisional and rendering every turn opener twice after reload. The delivery record is now the visible message, so it anchors checkpoints like any user line, and undo truncation takes its queue records with it, matched by the text they carry since the enqueue is not adjacent to the delivery. An absorbed delivery carries no file checkpoint (there is no user record for the CLI to checkpoint at), so its boundary offers conversation-only undo instead of a file revert the CLI would reject.
Antisophy
force-pushed
the
fix/midturn-absorption-delivery
branch
from
August 16, 2026 19:14
880e2c3 to
8aaef2c
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.
Recent Claude CLIs absorb queued messages into the running turn at a tool boundary instead of holding them for the next one. The queue entry is removed after delivery, and the delivery itself is recorded as a
queued_commandattachment rather than as a user line: the CLI's own absorption path removes the entry only once it has been folded into the running request, and marks each folded command's lifecycle as started. The queue-primary pipeline read every remove as "removed without being consumed", so absorbed messages were badged "not delivered" while the model demonstrably received them, and the delivery record was invisible because attachment lines were never translated.This PR translates
queued_commandattachments into user messages and pairs each one strictly with a remove. The pair's records land in either order, so an attachment against a still-queued entry is held until the entry's fate is known: a remove completes the absorption and releases it; a dequeue discards it, since in that flow the echo carries the message and recent CLIs write the attachment as presentation only; one matching nothing renders nothing. An entry removed with no delivery record keeps the not-delivered outcome, so a genuine queue clear still reports honestly.The delivery record becomes the visible message, so it anchors checkpoints like any user line, and undo truncation takes its queue records with it, matched by the text they carry because the enqueue is not adjacent to the delivery (the turn's own output sits between them). An absorbed delivery carries no file checkpoint, as there is no user record for the CLI to checkpoint at, so its boundary offers conversation-only undo instead of a file revert the CLI would reject with "not a user message in this session".
Covered by a replay unit test exercising both pairing orders, the dequeue-flow attachment, and an undelivered queue clear, plus reducer tests for the provisional handover. Full check set run; a handful of contention flakes passed on serial retry.
(Rebased onto 50cd0f9; the previously stacked undo fix is superseded by it and dropped.)
Update: real-world replay surfaced two hardening requirements now folded in. Queue records are matched resiliently rather than by content text alone (the CLI omits content for array-valued messages and texts can repeat): exact match, then empty-to-empty, and a remove finally settles the oldest unsettled entry. Echo pairing likewise prefers the awaiting entry whose text the echo carries, and a joined echo (several entries drained under one dequeue record, an older CLI behavior still present in long-lived sessions) settles every queued entry whose text appears in it. Without these, one stranded entry shifts every later pairing behind by one, and each turn opener renders twice after reload. The replay unit test covers the array-message flow and the pairing after it.