Summary
When GRAPHIFY_OUT is a nested path whose last segment is a common name (e.g. artifacts/graphify), graphify hook-guard read silently skips the read nudge for every file under any directory with that name, not just the output directory. Example: tools/graphify/notes.md or .codex/skills/graphify/SKILL.md.
Version / environment
- graphifyy 0.9.69
- Windows 11, Claude Code PreToolUse hook (should be platform-independent)
GRAPHIFY_OUT=artifacts/graphify
Reproduction
From a project root containing artifacts/graphify/graph.json:
export GRAPHIFY_OUT=artifacts/graphify
# expected: no nudge (this is the output dir) -> OK
echo '{"tool_name":"Read","tool_input":{"file_path":"artifacts/graphify/GRAPH_REPORT.md"}}' | graphify hook-guard read
# expected: nudge (regular indexed source file) -> OK
echo '{"tool_name":"Read","tool_input":{"file_path":"src/app.py"}}' | graphify hook-guard read
# expected: nudge, actual: no output
echo '{"tool_name":"Read","tool_input":{"file_path":"tools/graphify/notes.md"}}' | graphify hook-guard read
echo '{"tool_name":"Read","tool_input":{"file_path":"tools/graphify/helper.py"}}' | graphify hook-guard read
The files under tools/graphify/ are in the graph (source_file present in graph.json), so they should be nudged like any other file.
Cause
In _run_hook_guard (cli.py), the read branch has this check:
under_out = "graphify-out/" in j or (GRAPHIFY_OUT_NAME.lower() + "/") in j
GRAPHIFY_OUT_NAME is os.path.basename(GRAPHIFY_OUT), which is graphify here. So the check becomes a substring test for graphify/ anywhere in the path. The "graphify-out/" literal has the same issue for any directory named graphify-out outside the project's real output dir, but that is less likely to happen in practice.
This looks like the same pattern as #2273 (detect.py, closed), but in the hook guard, which that fix did not cover.
Suggested fix
Compare against the resolved output directory instead of a bare name substring. Something like:
out_dir = out_path().resolve()
def _is_under_out(v: str) -> bool:
try:
p = Path(v)
p = (root / p) if _is_cwd_relative(v) else p
p.resolve().relative_to(out_dir)
return True
except (ValueError, OSError, RuntimeError):
return False
under_out = any(_is_under_out(v) for v in (file_path, path) if v)
The Glob pattern field (e.g. artifacts/graphify/**) could keep a prefix match against the output dir's project-relative path.
Impact
Fails open: tools are never blocked, only the nudge is lost. In our repo this dropped the nudge for 11 indexed files (all of tools/graphify/), plus a skill directory. Because of this, we kept the old inline hook for Read|Glob and only switched Bash|Grep to hook-guard search, which works well. Thanks for #522 and #3121!
Summary
When
GRAPHIFY_OUTis a nested path whose last segment is a common name (e.g.artifacts/graphify),graphify hook-guard readsilently skips the read nudge for every file under any directory with that name, not just the output directory. Example:tools/graphify/notes.mdor.codex/skills/graphify/SKILL.md.Version / environment
GRAPHIFY_OUT=artifacts/graphifyReproduction
From a project root containing
artifacts/graphify/graph.json:The files under
tools/graphify/are in the graph (source_filepresent ingraph.json), so they should be nudged like any other file.Cause
In
_run_hook_guard(cli.py), thereadbranch has this check:GRAPHIFY_OUT_NAMEisos.path.basename(GRAPHIFY_OUT), which isgraphifyhere. So the check becomes a substring test forgraphify/anywhere in the path. The"graphify-out/"literal has the same issue for any directory namedgraphify-outoutside the project's real output dir, but that is less likely to happen in practice.This looks like the same pattern as #2273 (
detect.py, closed), but in the hook guard, which that fix did not cover.Suggested fix
Compare against the resolved output directory instead of a bare name substring. Something like:
The Glob
patternfield (e.g.artifacts/graphify/**) could keep a prefix match against the output dir's project-relative path.Impact
Fails open: tools are never blocked, only the nudge is lost. In our repo this dropped the nudge for 11 indexed files (all of
tools/graphify/), plus a skill directory. Because of this, we kept the old inline hook forRead|Globand only switchedBash|Greptohook-guard search, which works well. Thanks for #522 and #3121!