vouch capture finalize-all is wired into the claude-code SessionStart hook to sweep buffers left by sessions that ended without SessionEnd (#304). it can never do that: the shipped hook passes no --session-id, and nothing sets VOUCH_SESSION_ID, so the command silently no-ops on every session start.
where — cli.py:2404:
sid = session_id or os.environ.get("VOUCH_SESSION_ID") or ""
if not sid:
# No session ID provided; silently succeed
_emit_json({"finalized": [], "skipped_recent": [], "skipped_current": []})
return
the shipped hook (adapters/claude-code/.claude/settings.json) is bare:
vouch capture finalize-all || true
and grep -rn VOUCH_SESSION_ID finds only the cli reader plus two tests that set it themselves — nothing in the adapter, the hook wiring, or the runtime ever exports it. so sid is always "" and the sweep returns {"finalized": [], ...} every time.
unlike capture finalize, finalize-all never reads the stdin hook payload — which is where claude code actually puts session_id.
why it matters — #304 added this as the recovery path for buffers orphaned when a session dies without SessionEnd, and the settings.json comment says so outright ("fallback: windowclose event not yet supported"). those buffers instead accumulate in .vouch/captures/ indefinitely and their sessions never roll up into pending proposals, so the session never reaches the review queue at all.
why it went unnoticed — test_adapter_settings_wires_capture_hooks (tests/test_capture.py:399) asserts observe / finalize / banner are wired but never finalize-all, and every cli test for the command passes --session-id or the env var explicitly. the shipped configuration is the one path no test exercises.
fix — read the session id off the stdin payload the same way capture_finalize_cmd already does at SessionEnd, keeping --session-id and VOUCH_SESSION_ID as fallbacks. happy to send it.
vouch capture finalize-allis wired into the claude-code SessionStart hook to sweep buffers left by sessions that ended without SessionEnd (#304). it can never do that: the shipped hook passes no--session-id, and nothing setsVOUCH_SESSION_ID, so the command silently no-ops on every session start.where —
cli.py:2404:the shipped hook (
adapters/claude-code/.claude/settings.json) is bare:and
grep -rn VOUCH_SESSION_IDfinds only the cli reader plus two tests that set it themselves — nothing in the adapter, the hook wiring, or the runtime ever exports it. sosidis always""and the sweep returns{"finalized": [], ...}every time.unlike
capture finalize,finalize-allnever reads the stdin hook payload — which is where claude code actually putssession_id.why it matters — #304 added this as the recovery path for buffers orphaned when a session dies without SessionEnd, and the settings.json comment says so outright ("fallback: windowclose event not yet supported"). those buffers instead accumulate in
.vouch/captures/indefinitely and their sessions never roll up into pending proposals, so the session never reaches the review queue at all.why it went unnoticed —
test_adapter_settings_wires_capture_hooks(tests/test_capture.py:399) assertsobserve/finalize/bannerare wired but neverfinalize-all, and every cli test for the command passes--session-idor the env var explicitly. the shipped configuration is the one path no test exercises.fix — read the session id off the stdin payload the same way
capture_finalize_cmdalready does at SessionEnd, keeping--session-idandVOUCH_SESSION_IDas fallbacks. happy to send it.