In the subscribed message handler, prepareTurnState (which runs compactConversationIfNeeded) is called before decideSubscribedReply. If the classifier then decides not to reply, the compaction Haiku calls were incurred with no benefit.
Root cause: decideSubscribedReply needs conversationContext from preparedState, so the full preparation (including compaction) runs unconditionally before the routing decision. The subscribed classifier only actually reads the last ~6 transcript messages from that context — it does not use compaction summaries at all.
Impact: for long subscribed threads that cross the compaction threshold, every message that the classifier ultimately skips pays for one or more Haiku summarization calls. The compaction while-loop in conversation-memory.ts can batch 24 messages per iteration, so a 300-message thread could fire 12 Haiku calls on a single skipped message.
Affected files:
packages/junior/src/chat/runtime/slack-runtime.ts — subscribed message handler, prepareTurnState called before decideSubscribedReply
packages/junior/src/chat/services/conversation-memory.ts — compactConversationIfNeededWithDeps while-loop
Proposed fix:
- Split subscribed-message preparation into a lightweight routing phase (builds bounded router context from recent transcript messages, no compaction) and a full reply-preparation phase (runs compaction, full state setup) that only executes when the classifier returns
shouldReply: true
- The subscribed classifier only needs the last ~12 transcript messages plus a few signals; it doesn't need compaction summaries, so a bounded router context is sufficient and safer
Note: any fix must preserve the storage-size safety invariant — un-compacted thread state written to the adapter before a skip must not exceed adapter limits. If oversized, storage-maintenance compaction should still run but be clearly distinguished from reply-preparation compaction.
See also: #431 (compaction rework), #565 (thinking router also re-fires on resumes — related fast-model waste)
Action taken on behalf of David Cramer.
In the subscribed message handler,
prepareTurnState(which runscompactConversationIfNeeded) is called beforedecideSubscribedReply. If the classifier then decides not to reply, the compaction Haiku calls were incurred with no benefit.Root cause:
decideSubscribedReplyneedsconversationContextfrompreparedState, so the full preparation (including compaction) runs unconditionally before the routing decision. The subscribed classifier only actually reads the last ~6 transcript messages from that context — it does not use compaction summaries at all.Impact: for long subscribed threads that cross the compaction threshold, every message that the classifier ultimately skips pays for one or more Haiku summarization calls. The compaction while-loop in
conversation-memory.tscan batch 24 messages per iteration, so a 300-message thread could fire 12 Haiku calls on a single skipped message.Affected files:
packages/junior/src/chat/runtime/slack-runtime.ts— subscribed message handler,prepareTurnStatecalled beforedecideSubscribedReplypackages/junior/src/chat/services/conversation-memory.ts—compactConversationIfNeededWithDepswhile-loopProposed fix:
shouldReply: trueNote: any fix must preserve the storage-size safety invariant — un-compacted thread state written to the adapter before a skip must not exceed adapter limits. If oversized, storage-maintenance compaction should still run but be clearly distinguished from reply-preparation compaction.
See also: #431 (compaction rework), #565 (thinking router also re-fires on resumes — related fast-model waste)
Action taken on behalf of David Cramer.