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 forcreated,modified,deleted, andmovedfilesEventDebouncer– Batches rapid-fire changes (e.g., during a large refactor or IDE save) into a single coherent updateGraphUpdateHandler– Extracts file paths fromFileSystemEventobjects 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
- Detect changes externally – Typically via
git diff --name-onlyor similar tooling - Pass files explicitly – Supply the list to
incremental_update(changed_files=[...]) - 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 toincremental_update()incremental_update()(lines 1150-1190) – The unified entry point for all incremental refreshesstart_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
watchdogfile-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 howchanged_filesare sourced - The
watchdogdependency 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →