Skip to main content

Watcher Internals

This is the developer-facing companion to Watchers. The user-facing page covers what watchers are and how to set them up; this page covers how each one fires.

How a watcher fires

1. FSEvents detects a change in the watched folder
2. Debouncing: rapid changes coalesce into a single trigger (per the responsiveness tier)
3. Fingerprinting: a Merkle hash of file metadata captures the current state
4. Dispatch: an AI agent task runs with your instructions and the folder context
5. Convergence: after the agent completes, re-fingerprint
- If it changed (e.g. agent moved files), re-dispatch
- If stable, return to idle (max 5 iterations)

The convergence loop matters: it lets the agent organize files without re-triggering itself endlessly.

States

Each watcher operates as a small state machine:

┌──────┐ ┌────────────┐ ┌────────────┐ ┌──────────┐
│ idle │ ──▶ │ debouncing │ ──▶ │ processing │ ──▶ │ settling │
└──────┘ └────────────┘ └────────────┘ └──────────┘
▲ │
│ │
└─────────────────────────────────────────────────────┘
(fingerprint stable)
StateDescriptionCard badge
IdleWaiting for changes"Watching" (green)
DebouncingCoalescing rapid events"Watching" (green)
ProcessingAgent task running"Running" (accent + spinner)
SettlingWaiting for self-caused FSEvents to flush"Watching" (green)
DisabledManually paused"Paused" (gray)

Why fingerprinting is fast

Fingerprints use a Merkle hash of file metadata only — path, size, modification time. No file contents are read during change detection. Even very large directories are fingerprinted in milliseconds.

Smart exclusion of nested watchers

If you have a watcher on ~/Documents and another on ~/Documents/Projects, Osaurus automatically excludes the nested folder from the parent watcher's monitoring. No duplicate triggers.

Storage

Watchers are stored as JSON, one file per watcher:

~/.osaurus/watchers/
├── {uuid-1}.json
├── {uuid-2}.json
└── ...

Each file contains the watcher's configuration with ISO 8601 dates.

Sessions tagged watcher

Each triggered run is persisted as a chat session with source = watcher, keyed by the watcher's id. So all triggers from the same watcher accumulate into a single auditable session row in the chat sidebar — great for reviewing what happened over time.

Folder access

Watchers use security-scoped bookmarks to persist folder access across app restarts. If a bookmark goes stale (folder moved or deleted), the watcher card shows a warning — edit it and re-select the folder.


Related: