Skip to content

--exclude patterns only apply to the initial scan — update/watch/hook rebuilds silently re-include excluded paths #1886

Description

@Alwyn93

Summary

CLI --exclude patterns are applied only by the initial scan. Every other re-detection path — graphify update, the watcher, and the git-hook rebuilds — re-runs detect() without them, so deliberately excluded paths silently re-enter the graph on the first rebuild.

Verified on v8 HEAD (961b78e).

Repro

$ mkdir -p repro/vendor && cd repro
$ printf 'def foo():\n    return 1\n' > main.py
$ printf 'def bar():\n    return 2\n' > vendor/big.py

$ graphify extract . --exclude vendor --no-cluster
[graphify extract] found 1 code, 0 docs, 0 papers, 0 images
[graphify extract] wrote .../graph.json — 2 nodes, 1 edges (no clustering)
# graph source_files: ['main.py']  ✓ exclude honored

$ graphify update .
[graphify watch] Rebuilt: 4 nodes, 2 edges, 2 communities
# graph source_files: ['main.py', 'vendor/big.py']  ✗ vendor is back, silently

There is also no way to re-state the excludes at update time — update rejects the flag:

$ graphify update . --exclude vendor
error: unknown update option: --exclude

Root cause

  • --exclude is parsed into cli_excludes and forwarded as extra_excludes only in the extract command path (cli.py:2029-2032, cli.py:2100, cli.py:2112); detect() appends these root-anchored patterns last (detect.py:1163-1166).
  • extra_excludes is never persisted. The build saves the scan root (.graphify_root, watch.py:967) — which is what makes update feel like a faithful re-run of the original build — but not the exclude patterns.
  • _rebuild_code() (watch.py:721) has no extra_excludes parameter at all; its detect call is detect(watch_path, follow_symlinks=follow_symlinks) (watch.py:822).
  • All four rebuild drivers therefore scan unfiltered: update (cli.py:1275), the watch loop (watch.py:1251), the post-commit hook (hooks.py:134), and the post-checkout hook (hooks.py:175).

Suggested fix

Persist the build's extra_excludes alongside .graphify_root (or in manifest.json) and have _rebuild_code() re-apply them; optionally also accept --exclude on update as an explicit override. This is the same shape as #1800 (--exclude-hubs not surviving hook/watch rebuilds) — a single persisted "build options" sidecar would solve both.

Workaround (verified)

Put the patterns in a .graphifyignore at the scan root instead of using --exclude — ignore files are re-read from disk on every detect(), so they survive update/watch/hooks. One wrinkle: converting an already-polluted graph back needs graphify update --force, since the shrinking node count otherwise trips the safety check in to_json.

Context

Found while diagnosing #1873 (fix in #1885): with the nested-ignore bug live, a CLI --exclude .hypothesis workaround appeared to fix a zeroed scan, then regressed on the next update — which is how this gap surfaced.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions