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

> Understand incremental graph updates in code-review-graph. Learn how watch mode uses file-system events and hook-based updates use CLI commands for efficient data refreshing.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: internals
- Published: 2026-08-16

---

**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`](https://github.com/tirth8205/code-review-graph/blob/main/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`](https://github.com/tirth8205/code-review-graph/blob/main/incremental.py), following the watch-mode entrance code at **lines 1540-1550**.

### Starting Watch Mode in Your Application

```python
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`](https://github.com/tirth8205/code-review-graph/blob/main/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

```python
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`](https://github.com/tirth8205/code-review-graph/blob/main/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`](https://github.com/tirth8205/code-review-graph/blob/main/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.