Watch Mode vs. Hook-Based Updates in code-review-graph: What Triggers Incremental Graph Updates

Watch mode triggers incremental updates via automated file-system events captured by watchdog, while hook-based updates are explicitly invoked through CLI commands or scripts that pass changed files directly to incremental_update().

Both mechanisms in the tirth8205/code-review-graph repository serve the same purpose—keeping the knowledge graph synchronized with code changes—but they differ fundamentally in how they detect changes and when they execute. Understanding these triggers helps you choose the right integration strategy for your development workflow.


How Watch Mode Works: Automated File-System Monitoring

Watch mode provides continuous, background monitoring of your repository. The implementation lives in code_review_graph/incremental.py, where the watch() function orchestrates the entire process.

The Watch Mode Architecture

The system relies on three coordinated components:

  • watchdog.Observer – An OS-level file system watcher that emits events for created, modified, deleted, and moved files
  • EventDebouncer – Batches rapid-fire changes (e.g., during a large refactor or IDE save) into a single coherent update
  • GraphUpdateHandler – Extracts file paths from FileSystemEvent objects and prepares them for processing

When a debounced batch is ready, the handler calls incremental_update(repo_root, store, changed_files=…) directly. This occurs at line 1559 of incremental.py, following the watch-mode entrance code at lines 1540-1550.

Starting Watch Mode in Your Application

from pathlib import Path
from code_review_graph.incremental import start_watch_thread

repo_root = Path("/path/to/repo")
store = GraphStore(Path("/tmp/graph.db"))

# Launches a daemon thread that watches and updates automatically

watch_thread = start_watch_thread(repo_root, store, daemon=True)

The start_watch_thread() function (lines 1586-1590) spawns a background thread that runs watch() indefinitely. If the optional watchdog dependency is unavailable, this gracefully degrades to a no-op (lines 1595-1599).


How Hook-Based Updates Work: Explicit Invocation

Hook-based updates flip the trigger model: instead of reacting to events, you explicitly declare when the graph should refresh. This is the preferred approach for CI pipelines, Git hooks, and manual workflows where watchdog is impractical or unnecessary.

The Hook-Based Flow

  1. Detect changes externally – Typically via git diff --name-only or similar tooling
  2. Pass files explicitly – Supply the list to incremental_update(changed_files=[...])
  3. Execute one-off refresh – The function recomputes impacted files and their transitive dependents

The public API entry point is incremental_update() at lines 1150-1190 in code_review_graph/incremental.py. This same function powers watch mode, but hook-based callers invoke it directly without the event infrastructure.

Hook-Based Usage in Practice

from code_review_graph.incremental import incremental_update

repo_root = Path("/path/to/repo")
store = GraphStore(Path("/tmp/graph.db"))

# Changed files discovered by Git hook or CI step

changed = ["src/moduleA.py", "tests/test_moduleA.py"]

result = incremental_update(repo_root, store, changed_files=changed)
print(f"Files updated: {result['files_updated']}")

A typical Git pre-commit or post-commit hook would wrap this logic, calling incremental_update() only when the repository state actually changes.


Critical Differences: Watch Mode vs. Hook-Based Triggers

Aspect Watch Mode Hook-Based
Trigger source OS file-system notifications (watchdog events) Explicit CLI command or script invocation
Timing Continuous, near real-time with debouncing On-demand, tied to specific workflow events
File discovery Automatic via FileSystemEvent objects Manual—caller provides changed_files list
Environment requirement Requires watchdog dependency No additional dependencies
Process model Background thread in same process Synchronous call in caller's process
Typical use case Local development, IDE integration CI/CD pipelines, Git hooks, scheduled jobs

Implementation Details from the Source Code

The code_review_graph/incremental.py file contains all core logic for both triggering mechanisms:

  • watch() (lines 1540-1569) – Sets up the observer, configures the debouncer, and bridges events to incremental_update()
  • incremental_update() (lines 1150-1190) – The unified entry point for all incremental refreshes
  • start_watch_thread() (lines 1586-1590) – Convenience wrapper for daemon thread execution

Both trigger types converge on incremental_update(), ensuring consistent graph computation regardless of how changes are detected. The function recomputes transitive dependents internally, so callers only need to identify direct changes.


Summary

  • Watch mode provides hands-off automation using watchdog file-system events, ideal for active development environments
  • Hook-based updates offer precise control for CI/CD and Git workflows, invoked explicitly via incremental_update()
  • Both mechanisms use the same core implementation in code_review_graph/incremental.py, differing only in how changed_files are sourced
  • The watchdog dependency is optional; hook-based updates function without it

Frequently Asked Questions

What happens if watchdog is not installed?

Watch mode gracefully degrades. The start_watch_thread() function detects the missing dependency at lines 1595-1599 and returns without starting the observer. Hook-based updates continue to work normally since they bypass the event system entirely.

Can I use both watch mode and hooks together?

Yes. They are complementary. Run watch mode locally for real-time feedback during development, and configure Git hooks or CI steps for guaranteed updates on commits and deployments. Since both call the same incremental_update() implementation, there's no risk of inconsistent graph states.

How does the debouncer handle rapid saves?

The EventDebouncer aggregates file-system events within a short time window (typically hundreds of milliseconds). This prevents redundant graph recomputations when an IDE saves multiple files simultaneously or when a refactoring tool touches many modules. Only one incremental_update() call executes per debounced batch.

Is there a performance difference between the two approaches?

Hook-based updates can be more efficient in CI environments because you compute changed_files once (via git diff) and invoke a single update. Watch mode incurs continuous background overhead and may trigger more frequent updates during heavy editing sessions. However, both use identical graph computation logic, so per-update performance is equivalent.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →