SessionStart hooks cannot refresh in-memory slash-command body cache populated at worktree open

Resolved 💬 3 comments Opened May 11, 2026 by Dessos Closed May 12, 2026

Summary

Slash-command body cache is populated when a worktree is first opened, before the SessionStart hook chain runs. If a SessionStart hook then writes a fresh body to .claude/commands/<name>.md, the agent for that session still receives the stale cached body when the command is invoked. The on-disk file is up to date; only the in-memory cache lags.

Cache lifetime appears tied to the worktree (not the session), so the staleness window covers every session in that worktree until the worktree is recreated.

Verified versus current

  • ~2.1.128 (Dessos/ai-os, 2026-05-09): reproduced cleanly — see "Repro" below.
  • 2.1.138 (current): not directly re-tested. Our current worktree was opened after the source change, so no cache-vs-disk divergence exists at worktree-open time to expose the cache. The mechanism (cache populated pre-hook) likely still applies but hasn't been re-verified in 2.1.138.

Filing now in case 2.1.138 still carries the bug; maintainers can dispose quickly if it's fixed.

Repro (2026-05-09 incident, pre-2.1.138)

Project setup: a SessionStart hook (tools/sync-harness.py) writes per-role command definitions from body/operators/<role>/commands/<name>.md into .claude/commands/<role>/<name>.md (or top-level for promoted commands). Authoring rule: edit the body source; the hook propagates.

  1. Worktree opened at time T1 — .claude/commands/focus.md on disk had content A.
  2. Between T1 and T2 (later that day), body/operators/cycle/commands/focus.md was edited via PR — removing a deprecated python -m tools.focus log step (PR #36 in the project).
  3. T2: new session in the same worktree. SessionStart fired tools/sync-harness.py, which rewrote .claude/commands/focus.md to content B. mtimes confirmed the rewrite.
  4. Operator invoked /focus. The <command-name>focus</command-name> body passed to the agent in its first message contained the OLD content A (with the deprecated python -m tools.focus log step) — text that existed nowhere on disk at any path the harness would read from.
  5. Agent followed step A verbatim, invoked python -m tools.focus log. The CLI silently swallowed the unknown argv and re-ran the full pipeline, double-writing to history. Defense shipped per-tool via argv-rejection.

The only consistent explanation: Claude Code resolved the body from an in-memory cache populated at worktree open (T1), and the SessionStart hook's disk rewrite happens after that initial cache load.

Why this matters

For projects that iterate slash-command bodies between sessions — anyone using SessionStart-driven sync of command definitions from a separate source-of-truth — every worktree carries the body it was opened with even after the hook writes a fresh one. The failure is silent unless the operator audits the <command-name> payload. Per-tool defenses (argv rejection on stale-arg invocations) work as a backstop but require remembering to add them to every CLI.

Suggested fix shape

Either (a) defer slash-command body cache population until after the SessionStart hook chain completes, or (b) invalidate the cache for .claude/commands/<name>.md when SessionStart hooks write to those files.

Sister observation (may or may not share a fix)

Same "long-lived process state + cached content = silent staleness" pattern appears with MCP server subprocesses: editing server-side Python doesn't take effect until Claude Code restart, and @functools.lru_cache on filesystem-reading functions inside MCP-server-reachable code paths has the same effect (we observed this today and shipped a fix internally). These are workarounds we've documented; mentioning in case there's a unifying architectural fix.

Workarounds

  1. Open a second session in the same worktree (cache rebuilds with the post-hook on-disk content).
  2. Recreate the worktree.
  3. Add argv-rejection (or equivalent) to any CLI exposed via a slash command so a stale body invoking a removed subcommand fails loudly. Per-tool defense.

Environment

  • Claude Code: 2.1.138 (current); original incident on ~2.1.128.
  • OS: Windows 11
  • Repo (public): https://github.com/Dessos/ai-os — relevant files: body/operators/cycle/commands/focus.md, tools/sync-harness.py, .claude/commands/focus.md.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗