From f27bd470ab49c8c8fbf5551a80924eedb8bab371 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 25 Jul 2026 13:01:36 -0700 Subject: [PATCH] simulation: isolate claude -p reflections from user-level hooks In this commit, we point CLAUDE_CODE_CONFIG at a sterile home directory for every headless reflection call, mirroring the CODEX_HOME isolation we built for the codex backend. The --system-prompt replacement already keeps CLAUDE.md and memories out of the session, but user-level hooks still fire in -p mode and their feedback text reaches the model: iteration 19 of the first Opus run came back reasoning about the user's mail-watcher Stop hook instead of emitting a router, wasting the iteration. With CLAUDE_CONFIG_DIR pointed at ~/codez/claude-harness-home, no user settings load at all; auth is unaffected because the OAuth token travels via the environment. --- simulation/claude_lm.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/simulation/claude_lm.py b/simulation/claude_lm.py index 0d8571912..9127cb465 100644 --- a/simulation/claude_lm.py +++ b/simulation/claude_lm.py @@ -13,6 +13,12 @@ tooling chatter instead of candidates. Two defenses here: model to its role as a text-generation function. - The subprocess runs from a neutral temporary directory, so project-level CLAUDE.md discovery has nothing to find even in modes that perform it. +- CLAUDE_CONFIG_DIR points at a sterile home (~/codez/claude-harness-home), + so user-level settings — and, critically, user-level HOOKS — never load. + Hooks fire even in headless -p mode and their feedback text reaches the + model: one reflection in the first Opus run came back discussing the + user's mail-watcher Stop hook instead of emitting a router. Auth is + unaffected because the OAuth token travels via the environment. When ANTHROPIC_API_KEY is present we additionally pass `--bare`, which skips hooks, auto-memory, and CLAUDE.md discovery entirely (bare mode only @@ -82,6 +88,12 @@ class ClaudeLM: TOKEN_FILE.read_text().strip() ) + # A sterile config home keeps user-level settings and hooks out + # of the session (see module docstring); created on first use. + config_home = pathlib.Path.home() / "codez" / "claude-harness-home" + config_home.mkdir(parents=True, exist_ok=True) + env["CLAUDE_CONFIG_DIR"] = str(config_home) + # The claude binary is a wrapper that spawns the real CLI as a # grandchild sharing our stdout pipe. subprocess.run's timeout # kills only the direct child and then blocks forever reading a