Skip to content

hook-guard: Read/Glob PreToolUse nudge has no project-membership or graph-freshness gate — fires identically on out-of-project files and stale graphs #1840

Description

@chaosanderos

Version: reproduced on 0.9.10 through 0.9.13 (current v8 HEAD); _run_hook_guard is unchanged across that range.

Problem: graphify/__main__.py::_run_hook_guard (read branch, ~line 1676 on 0.9.10; moved to graphify/cli.py on v8) fires _READ_NUDGE whenever (a) the tool call's file_path/pattern/path has a trailing extension in _HOOK_SOURCE_EXTS (which includes .md/.rst/.txt/.mdx), (b) the path isn't under graphify-out/, and (c) graphify-out/graph.json exists in the cwd. Two real-world consequences observed in production use (Claude Code, 2026-07 audit sessions):

  1. Reading a file with no relationship to the project the graph indexes — a global plugin-cache SKILL.md, another repo's config — fires the same "MANDATORY: run graphify before reading source files" additionalContext as reading the project's own .py source, because the guard only inspects the path string's extension, never whether the path is plausibly inside the tree graphify-out/graph.json was built from.
  2. A graph that is arbitrarily stale (no cron/CI rebuild; graphify update is run by hand) gates identically to a freshly-rebuilt one — the guard calls only .is_file() on graph.json, never compares its age against the tracked tree.

Both make the nudge fire on a majority of reads in a typical audit/ops/docs-heavy session (measured: ~100% of reads were on non-application-code targets in one such session), training the agent to habitually override "MANDATORY" language — which also blunts the signal on the minority of reads where the nudge is actually correct.

Proposed smallest fix:

  1. Resolve the candidate path and skip the nudge when it isn't under the graph's project root (reuse the existing .graphify_root/GRAPHIFY_REPO_ROOT root-resolution convention already used by _rebuild_code and friends elsewhere in __main__.py/build.py).
  2. Read the generated_at stamp that PR Stamp graph.json with generated_at, warn on stale graphify query #1624 already proposes adding to graph.json, and soften the nudge language (e.g. drop "MANDATORY", note staleness) when the stamp is older than the newest mtime in the tracked corpus, or than git status's latest relevant change — reusing Stamp graph.json with generated_at, warn on stale graphify query #1624's plumbing instead of adding a second staleness mechanism.
  3. Optionally scope .md/.rst/.txt/.mdx out of the hard _HOOK_SOURCE_EXTS gate (or behind a docs/ allowlist), since a knowledge graph built from AST/semantic hashing has limited signal for arbitrary non-project markdown — proposed as opt-in/config-gated rather than a default-behavior change, since the list is documented as "verbatim from the old hook".

Test plan suggestion: a fake project root + graph.json; a Read call on a path outside that root should emit no nudge even though the extension matches; a generated_at stamp older than the corpus's newest file mtime should downgrade the nudge's language.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions