fix(hooks): honor silent_save when stop_hook_active is set

Claude Code 2.1.114 passes stop_hook_active:true on every Stop fire
after the first in a session (plugin-dispatched hooks in particular).
The legacy guard at line 426 was written for block-mode, where a
re-fire with the flag set meant "you already blocked, don't block
again" — correct loop prevention when the hook returns
{"decision":"block"}.

Silent-save mode (default since #673) never blocks — it saves
directly and returns. The flag is meaningless there, so the old
guard was suppressing every auto-save after the first one in a
Claude Code session. Symptom: terminal never shows the "✦ N
memories woven" notification again, hook.log stays silent, save
marker stuck.

Fix: only skip on stop_hook_active when block mode is configured.
Silent mode runs through as normal — the save is deterministic and
idempotent, no loop risk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jp
2026-04-18 14:37:24 -07:00
parent 109d7f267c
commit 914945637c
+12 -3
View File
@@ -209,10 +209,19 @@ def hook_stop(data: dict, harness: str):
stop_hook_active = parsed["stop_hook_active"]
transcript_path = parsed["transcript_path"]
# If already in a save cycle, let through (infinite-loop prevention)
# If already in a block-mode save cycle, let through (infinite-loop prevention).
# Silent mode saves directly without returning {"decision":"block"}, so there's
# no loop to prevent — and Claude Code's plugin dispatch sets this flag on every
# fire after the first, which would otherwise suppress all subsequent auto-saves.
if str(stop_hook_active).lower() in ("true", "1", "yes"):
_output({})
return
try:
from .config import MempalaceConfig
silent_guard = MempalaceConfig().hook_silent_save
except Exception:
silent_guard = True
if not silent_guard:
_output({})
return
# Count human messages
exchange_count = _count_human_messages(transcript_path)