fix(summary-hook): prevent silent capture loss (tracker-first ordering + mid-processing appends) - #84
Open
catShaark wants to merge 1 commit into
Open
Conversation
…ering and mid-processing appends
Two data-loss paths in the Stop-hook capture, both observed live:
1. The uuid tracker advanced inside formatNewEntries/formatSignalEntries,
BEFORE the upload. Any failed addMemory permanently lost that delta
(the Stop hook swallows errors by design). The formatters now return
{ content, cursor } and the hook advances the tracker only after the
capture is persisted, so a failed upload retries on the next Stop.
2. /v3/documents upserts by customId APPEND only once the previous
revision reaches a terminal status; an append arriving while the
session document is still processing (queued/extracting/indexing) is
silently dropped server-side. Reproduced on a healthy server with a
9-turn session at ~10s pacing: one entire turn vanished from the
session document while the tracker advanced past it. The hook now
does a single 5s-bounded status GET first and, if the document is
still processing, defers WITHOUT advancing the tracker — the delta
carries over into the next Stop capture. Lookup failures fail open.
Bundles rebuilt (node scripts/build.js); npm test 5/5.
catShaark
marked this pull request as ready for review
August 5, 2026 09:03
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.
Problem
Two independent silent data-loss paths in the Stop-hook capture (
summary-hook). Both reproduced against a live server with a real 9-turn Claude Code session at regular-user pacing (~10s between turns), by diffing the stored session document against the transcript.1. Tracker advances before the upload
formatNewEntries/formatSignalEntriescallsetLastCapturedUuidinside the formatter, beforeaddMemoryruns. If the upload then fails (network, 4xx/5xx, rate limit — and the hook intentionally swallows all errors so it never blocks the session), that delta is permanently lost: the next Stop resumes from the already-advanced cursor.2. Mid-processing appends are dropped server-side
The session document upserts by
customId, and the backend only appends a new revision once the previous one has reached a terminal status. A POST that arrives while the document is stillqueued/extracting/indexingreturns 200 but the content is silently discarded. Fast consecutive turns hit this in normal use: in our reproduction, one entire turn vanished from the session document — the POST "succeeded", the tracker advanced, the content was never stored.Fix
{ content, cursor }and no longer touch the tracker. The hook callssetLastCapturedUuid(sessionId, capture.cursor)only afteraddMemoryresolves — a failed upload leaves the cursor untouched and the delta retries on the next Stop.GETon the session document (5s abort budget). If it's still processing, the capture is deferred without advancing the tracker — the delta simply carries over into the next Stop's capture, which is exactly the formatter's existing carry-over semantics for<100-char captures. Status lookup failures fail open (capture proceeds), so this can never block or break capture.Both changes reuse the existing carry-over mechanism rather than adding retries/queues, so the hook stays fire-and-forget and adds at most one bounded GET per Stop.
Testing
npm test— 5/5 pass.node scripts/build.js(included in the commit; happy to drop them if you prefer bundles rebuilt on your side).Marked as draft for maintainer review — particularly whether you'd rather gate the status check behind a setting, and whether
failedshould also defer rather than proceed.