Skip to content

Commit 7277828

Browse files
sirily11claude
andcommitted
fix(summary): skip thread summary/memory for hook-injected commit turn
- the Commit & Push hook injects a synthetic commit prompt as a user message, which leaked into thread summaries and extracted memories - capture the commitPush setup-session marker before the after-stop dispatch consumes it, and skip summary/memory updates for that turn Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
1 parent 6ecc656 commit 7277828

1 file changed

Lines changed: 38 additions & 13 deletions

File tree

RxCode/App/AppState+CrossProject.swift

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,11 @@ extension AppState {
698698
let markUnread = !isFg && !resultEvent.isError
699699

700700
let stopProject = projects.first(where: { $0.id == projectId })
701+
// Capture queued-followup state now, synchronously, before
702+
// `finalizeStreamSession` schedules the auto-flush that pops
703+
// the next queued message. Stop hooks (review/commit) use this
704+
// to defer until the queue has fully drained.
705+
let hasQueuedFollowups = !threadStore.loadQueue(sessionKey: sessionKey).isEmpty
701706

702707
finalizeStreamSession(for: sessionKey) { state in
703708
if let cost = resultEvent.totalCostUsd { state.costUsd = cost }
@@ -726,7 +731,8 @@ extension AppState {
726731
sessionId: resultEvent.sessionId,
727732
reason: .completed,
728733
turnDidError: resultEvent.isError,
729-
lastAssistantText: lastAssistantResponseText(in: stateForSession(sessionKey).messages)
734+
lastAssistantText: lastAssistantResponseText(in: stateForSession(sessionKey).messages),
735+
hasQueuedFollowups: hasQueuedFollowups
730736
))
731737
if stopResult.hasError {
732738
stopHookFailureOutput = stopResult.combinedOutput
@@ -762,6 +768,18 @@ extension AppState {
762768
reconcileFromDisk(sessionId: resultEvent.sessionId, projectId: projectId, cwd: cwd)
763769
}
764770

771+
// Whether this turn was the synthetic commit/push follow-up the
772+
// Commit & Push hook injected. Captured BEFORE the after-stop
773+
// dispatch below, which is where CommitPushHook consumes the
774+
// marker. A hook-injected turn's last "user" message is the
775+
// commit prompt, not the user's words — summarizing or
776+
// extracting memories from it pollutes the briefing/memories
777+
// with "Commit the changes from this session…" boilerplate.
778+
let wasHookInjectedTurn = isSetupSession(
779+
kind: HookSetupKind.commitPush,
780+
sessionKey: sessionKey
781+
)
782+
765783
// After-session-stop hooks: shown only, not re-saved. This
766784
// dispatch also drives the response-complete notification
767785
// (ResponseNotificationHook), which self-suppresses unless
@@ -773,7 +791,8 @@ extension AppState {
773791
sessionId: resultEvent.sessionId,
774792
reason: .completed,
775793
turnDidError: resultEvent.isError,
776-
lastAssistantText: lastAssistantResponseText(in: stateForSession(sessionKey).messages)
794+
lastAssistantText: lastAssistantResponseText(in: stateForSession(sessionKey).messages),
795+
hasQueuedFollowups: hasQueuedFollowups
777796
))
778797
}
779798

@@ -801,17 +820,23 @@ extension AppState {
801820
// ResponseNotificationHook via the after-session-end
802821
// dispatch above.
803822

804-
scheduleThreadSummaryUpdate(
805-
sessionId: resultEvent.sessionId,
806-
projectId: projectId,
807-
cwd: cwd,
808-
messages: stateForSession(sessionKey).messages
809-
)
810-
scheduleMemoryExtraction(
811-
sessionId: resultEvent.sessionId,
812-
projectId: projectId,
813-
messages: stateForSession(sessionKey).messages
814-
)
823+
// Skip summary/memory updates for the hook-injected
824+
// commit & push turn — its last user message is the
825+
// commit prompt, not the user's, and would otherwise
826+
// leak into the thread summary and extracted memories.
827+
if !wasHookInjectedTurn {
828+
scheduleThreadSummaryUpdate(
829+
sessionId: resultEvent.sessionId,
830+
projectId: projectId,
831+
cwd: cwd,
832+
messages: stateForSession(sessionKey).messages
833+
)
834+
scheduleMemoryExtraction(
835+
sessionId: resultEvent.sessionId,
836+
projectId: projectId,
837+
messages: stateForSession(sessionKey).messages
838+
)
839+
}
815840

816841
// If this session is running in the background, automatically process any queued messages.
817842
// Foreground sessions are handled by InputBarView via isStreaming onChange.

0 commit comments

Comments
 (0)