fix(inject): eliminate race condition in SSE token counting - #3
Merged
DevanshuNEU merged 1 commit intoMar 23, 2026
Merged
Conversation
…ut text, BPE count once on stream end
7 tasks
DevanshuNEU
added a commit
that referenced
this pull request
May 4, 2026
…-36] Rule 0 returns Healthy when turnCount <= 2 AND contextPct < 30, before the per-model classifier runs. Blocks stale growthRate, isDetailHeavy, and any future projection wrapper from escalating a session that has no real history yet. Acceptance criteria: - Healthy on any conversation with turnCount <= 2 AND contextPct < 30 regardless of prior tab/conversation state - Wrappers like escalateForProjection still run on the returned HealthScore so a real draft can escalate after the guard - AC #3 (overlay resets on new chat) already satisfied by existing SPA-nav reset path in claude-ai.content.ts (PR #29) 12 new tests: positive path (5), boundary (3), does-not-mask (4).
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
handleClaudeEventwas callingcountTokens()as fire-and-forget per delta chunk, updatingsummary.outputTokensasynchronously. Thefinallyblock readsummary.outputTokensimmediately after the stream ended — before any of those.then()callbacks had resolved — resulting in consistently under-counted or zero output tokens inSTREAM_COMPLETE.Fix: accumulate all delta text synchronously into a string buffer during the stream, then make a single parallel BPE round-trip for both input and output tokens in the
finallyblock before postingSTREAM_COMPLETE. Intermediate batch flushes keep achars/4estimate for real-time UI updates.Type of Change
fix— Bug fixWhat Was Changed
entrypoints/inject.tshandleClaudeEvent: removed per-chunkcountTokens()call fromcontent_block_delta;message_startnow sets a synchronouschars/4input estimate instead of firing an async countdecodeSSEStream: addedoutputTextBufferstring accumulator; eachcontent_block_deltaappends text and updatessummary.outputTokensviachars/4;finallyblock awaitsPromise.all([countTokens(promptText), countTokens(outputTextBuffer)])and overwrites estimates with accurate BPE counts beforeSTREAM_COMPLETEis postedHow to Test
bun run compile— should exit cleanbun run test— all 26 tests should pass[LCO] [Complete]log line shows non-zero input and output token countsChecklist
bun run test)bun run compile)bun run build)feat:,fix:,refactor:,test:,chore:)Notes for Reviewer
The
chars/4approximation used during streaming is intentionally rough — it keeps the UI responsive without any async overhead. The accurate BPE count replaces it atomically infinallybeforeSTREAM_COMPLETEfires, so the final stored value is always the precise count. IfcountTokenstimes out (5s fallback), the chars/4 estimate is preserved rather than overwriting with zero.