-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Problem
Advisory content from SessionStart (and other lifecycle) handlers is visible to the human user in the Claude Code terminal UI but is not passed to Claude's context window. The content is written as if it's for the AI agent, but the AI never sees it.
Observed Behavior
After compaction, the human user sees this in their terminal:
SessionStart:compact says: ⚠️ DOGFOODING PROJECT: Bug Handling Protocol...
✅ SessionStart hook system active
🐳 YOLO container (Claude Code CLI in sandbox)
⚠️ WORKFLOW RESTORED AFTER COMPACTION ⚠️
Claude receives only:
SessionStart:compact hook success: Success
Exception: When the daemon is NOT running, the error message DOES reach Claude:
SessionStart hook additional context: HOOKS DAEMON: Not currently running
Error: daemon_startup_failed...
Root Cause (Investigated)
Claude Code's hook response schema defines two distinct fields:
| Field | Where it goes |
|---|---|
systemMessage |
Terminal display only (human sees it) |
additionalContext |
Claude's context window AND terminal |
All lifecycle event handlers (SessionStart, SessionEnd, PreCompact, Notification) are formatted using systemMessage by HookResult.to_json(), per Claude Code's schema:
# hook_result.py
# SessionStart/SessionEnd/PreCompact/Notification: systemMessage ONLY
return self._format_system_message_response()The error path (daemon not running) accidentally works because init.sh's emit_hook_error() uses additionalContext — technically a schema violation for SessionStart, but Claude Code handles it gracefully.
Implications
All these handlers currently produce content that only the human sees:
WorkflowStateRestorationHandler— workflow context after compactionYoloContainerDetectionHandler— container environment infoOptimalConfigCheckerHandler— config validation advice- Session-start dogfooding advisories
The content is written in a style optimised for AI consumption ("Read ALL files listed above using @ syntax") but the AI never receives it. The human user would need to manually copy-paste it to the agent.
Options
- Leave as-is and rewrite handler output to be human-optimised (humans are the actual audience)
- Use
additionalContextworkaround — switch SessionStart responses to usehookSpecificOutput.additionalContextinstead ofsystemMessage. Violates Claude Code's intended schema but works in practice (proven by error path) - File upstream request to Anthropic — request Claude Code to pass
systemMessageto AI context for lifecycle events - Hybrid approach — short human-readable summary in
systemMessage, key directives also injected viaadditionalContext
Research Needed
- Does Claude Code documentation explain the
systemMessagevsadditionalContextdistinction? - Have others in the Claude Code community found workarounds?
- Is Option 2 (the
additionalContextworkaround) stable/safe to rely on? - Does the behaviour differ between hook event types (e.g. does UserPromptSubmit support
additionalContext)?
Related Code
src/claude_code_hooks_daemon/core/hook_result.py—to_json()and_format_system_message_response().claude/hooks/init.sh—emit_hook_error()function (the working error path usingadditionalContext)src/claude_code_hooks_daemon/handlers/session_start/— all affected handlers
Decision Needed
Once we understand the full picture (especially whether Option 2 is safe), decide:
- Should lifecycle handler content be rewritten for human readability?
- Should we use the
additionalContextworkaround to reach Claude? - Should we file an upstream request to Anthropic?