SessionStart hook's explicit "MUST call Read first" instruction is skipped when the target files are already injected as system-reminder context
Summary
When a SessionStart hook emits an explicit, unambiguous "MANDATORY FIRST ACTION — call Read on these files before answering, before any other tool, before composing any prose" instruction, the model skips it if those files' contents are already present in the context as injected <system-reminder> / # claudeMd blocks. It treats "the text is already in my window" as satisfying "call the Read tool", and proceeds directly to the user's request.
The hook text in my case anticipated this exact failure and pre-empted it in writing — and was still ignored.
Environment
- Claude Code 2.1.197
- Model: claude-opus-5
- macOS (darwin 25.5.0), zsh
Reproduction
- Configure a
SessionStart:startuphook that prints an instruction to Read one or moreCLAUDE.mdfiles before any other action, including wording like:
- "before answering the user, before calling any other tool, before composing any prose"
- "The contents may have been injected into your context elsewhere as
<system-reminder>blocks. Those injections are snapshots of unknown freshness and completeness" - "You MUST verify by calling Read."
- Start a session in a project where the harness auto-injects
CLAUDE.mdcontents as a# claudeMdsystem-reminder block (i.e. the normal case). - Send any actionable first message (mine was
pick up 109, which routes to a skill).
Expected
Two Read tool calls, in the order given by the hook, before any prose or other tool call.
Actual
No Read calls. First output was prose acknowledging the user's request, then a Skill invocation, then a chain of unrelated tool calls. The two mandated Reads never occurred at any point in the session.
Why this matters
The injected snapshot and the on-disk file are not guaranteed to agree — that is the entire reason the hook demands a fresh Read. This failure mode is silent: the model behaves as if it has authoritative instructions when it only has a snapshot of unknown freshness, and there is no signal to the user that the gate was skipped. For users who rely on SessionStart hooks to enforce process gates, the gate is not enforceable.
It also appears to be an ordering/salience problem rather than a comprehension one: the hook output was the freshest and most specific instruction in the context window, directly above the first generated token, and was still deprioritised in favour of appearing responsive to the user's request.
Suggested fix
Either (a) ensure an explicit tool-call instruction from a SessionStart hook is treated as a hard precondition that cannot be discharged by the presence of injected context, or (b) if injected # claudeMd context is intended to satisfy such instructions, state that to the model so hook authors can stop asking for a redundant Read — and document it.