Shallow vs Recursive Watching in agentsview: A Technical Guide

agentsview employs two distinct file-system monitoring modes—recursive watching, which registers every subdirectory for instant change detection, and shallow watching, which monitors only root directories to conserve OS file descriptors.

The agentsview sync engine leverages the fsnotify library to detect file changes within session directories. To balance immediate change detection against operating system resource constraints, the codebase implements specific shallow and recursive watching strategies that prevent EMFILE errors while ensuring the synchronization engine captures critical file events.

What Is Recursive Watching in agentsview?

Recursive watching establishes comprehensive monitoring across an entire directory tree. When activated, the watcher traverses the target path and registers every subdirectory with the underlying fsnotify instance.

How Recursive Watching Works

In internal/sync/watcher.go, the WatchRecursive function implements this behavior using filepath.WalkDir to enumerate directories. For each directory encountered, it invokes watcher.Add to register the path with the kernel's notification subsystem 【/blob/main/internal/sync/watcher.go#L72-L79】.

The system also monitors for newly created directories dynamically. The watchIfDir helper automatically adds these paths to the watch list upon receiving a Create event, ensuring the watcher maintains complete coverage as the filesystem evolves 【/blob/main/internal/sync/watcher.go#L85-L99】.

This mode provides immediate notification of any file creation, modification, or deletion within the watched hierarchy. It suits session directories containing moderate numbers of subfolders where synchronization latency must remain minimal.

What Is Shallow Watching in agentsview?

Shallow watching restricts monitoring to the root directory only, explicitly excluding subdirectories from the watch list. This conservative approach targets directories containing thousands of subfolders—such as dated session archives—where recursive watching would exhaust available file descriptors.

When to Use Shallow Mode

The WatchShallow method in internal/sync/watcher.go adds the specified path to an internal roots slice and marks it as a shallow root. Unlike its recursive counterpart, this function calls watcher.Add exactly once for the root directory 【/blob/main/internal/sync/watcher.go#L30-L38】.

Rather than depending on filesystem events for nested content, shallow mode relies on the periodic sync engine (running every 15 minutes) to discover and process new files. This trades immediacy for scalability, preventing the EMFILE or ENOSPC errors that occur when fsnotify consumes the operating system's per-process file descriptor limit.

Key Differences: Resource Utilization and Responsiveness

The distinction between these modes centers on two critical factors:

  • File Descriptor Consumption: Recursive watching consumes one descriptor per subdirectory, while shallow watching uses exactly one descriptor per root. On systems with thousands of session subfolders, shallow mode avoids exhausting the ulimit or inotify watch limits.
  • Detection Latency: Recursive watching triggers immediate callbacks for any file change. Shallow watching delays detection until the next periodic synchronization cycle, making it suitable for archival directories where real-time sync is unnecessary.

Interaction Between Shallow and Recursive Roots

A critical architectural detail involves how these modes coexist within the same watcher instance. A shallow root does not shadow a deeper recursive root.

If a shallow parent (for example, .codex) contains a nested directory configured for recursive watching (such as .codex/sessions), the child maintains its autonomous watch behavior. New subdirectories created within the recursive child are automatically added to the watch list, while those created in the shallow parent remain unwatched.

The isUnderShallowRoot function enforces this logic by identifying the most specific containing root before deciding whether to auto-watch a newly detected directory 【/blob/main/internal/sync/watcher.go#L55-L69】. This check ensures that granular watch policies take precedence over broad shallow declarations.

Implementation Examples

The following examples demonstrate how to instantiate watchers using the agentsview sync package.

Recursive watch configuration:

import "github.com/kenn-io/agentsview/internal/sync"

// Monitor all subdirectories under /data/sessions
w, _ := sync.NewWatcher(500*time.Millisecond, onChange, nil)
w.WatchRecursive("/data/sessions")   // Walks tree and adds every directory
w.Start()

Shallow watch configuration:

import "github.com/kenn-io/agentsview/internal/sync"

// Monitor only the root /data/sessions directory
w, _ := sync.NewWatcher(500*time.Millisecond, onChange, nil)
w.WatchShallow("/data/sessions")    // Watches only the root, ignores subdirs
w.Start()

Summary

  • Recursive watching traverses the entire directory tree via WatchRecursive in internal/sync/watcher.go, registering each subdirectory with fsnotify for immediate change detection.
  • Shallow watching monitors only the root directory via WatchShallow, conserving file descriptors by ignoring subdirectories and relying on periodic synchronization every 15 minutes.
  • The resource trade-off involves file descriptor consumption: recursive mode risks EMFILE errors on large trees, while shallow mode scales indefinitely but delays change detection.
  • Hierarchical isolation via isUnderShallowRoot ensures recursive children of shallow parents maintain independent auto-watch capabilities without interference.

Frequently Asked Questions

What is the main performance difference between shallow and recursive watching in agentsview?

The primary distinction involves file descriptor consumption and detection latency. Recursive watching consumes one file descriptor per subdirectory and provides instant notifications, while shallow watching uses minimal descriptors but delays detection until the next periodic sync cycle (approximately 15 minutes).

Can I mix shallow and recursive watching in the same agentsview instance?

Yes. The watcher supports heterogeneous configurations where shallow roots coexist with recursive ones. The isUnderShallowRoot function ensures that directories under recursive roots maintain their auto-watch behavior even when nested inside shallowly-watched parent directories.

Where does agentsview handle automatic directory addition in recursive mode?

The watchIfDir helper function in internal/sync/watcher.go processes Create events to determine if the new path is a directory. If so, it automatically adds the directory to the fsnotify watcher, ensuring recursive coverage expands dynamically as the filesystem changes.

When should I use shallow watching over recursive watching?

Apply shallow watching to directories containing thousands of subfolders (such as date-based session archives) where recursive watching risks exhausting the OS file descriptor limit. Use recursive watching for smaller, actively modified directory trees where immediate synchronization is critical.

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 →