Hot-reloading Modular TOML Without Losing File Watches
Watching config.toml was easy. Watching layered config, drop-ins, symlinks, atomic saves, and replaced directories required treating the watch set as state.
Watching config.toml worked until Glimpse gained layered configuration and drop-in directories. At that point the input was no longer one file. It was a changing set of directories, symlink targets, and files that editors could replace atomically, so the watcher itself had to become stateful.
The current implementation uses Rust's 0 crate and one native watcher for the whole set. The design changed through several iterations because each simple version missed a real filesystem transition.
Watch directories, then reread the configuration
A file watch is fragile under the common save pattern of writing a temporary file and renaming it over the original. The name stays the same, but the inode does not. Watching the parent directory catches the create, modify, remove, and rename activity around that name.
Glimpse therefore treats filesystem events as invalidation signals. It does not attempt to apply the event itself. After the directory goes quiet for 250 milliseconds, it loads the entire configuration stack and publishes a new value only if the result is valid and different from the current one.
That choice collapses a noisy event sequence into one deterministic operation. A failed reload leaves the running configuration in place, which is much safer than partially applying a stack while an editor is still replacing files.
The watch set can move
The default stack includes system and user directories plus their config.d children. A drop-in directory may not exist at startup, then appear later. A dotfile manager may remove and recreate the whole configuration directory. A base file may be a symlink whose target lives elsewhere.
Each requested directory is represented by an arm with three pieces of state: the directory it wants, the nearest existing directory it is currently watching, and that directory's inode. When an event shows that the arm is displaced, Glimpse installs the new watch before releasing the old one.
The inode check handles a particularly quiet failure. If a directory is deleted and recreated at the same path, a watcher bound to the old inode can remain alive but never report another relevant event. Comparing names alone makes that dead watch look healthy.
Two missing directories can also fall back to the same existing parent. Since unwatch operates on the path, releasing that shared parent for one arm would accidentally disable the other. Glimpse releases an old watch only when no arm still uses it.
Boundaries prevent unrelated wakeups
Walking upward to any existing ancestor sounds resilient, but watching /etc or an entire user configuration directory would wake the process for unrelated applications. The implementation only falls back within the set of directories it was explicitly given.
That means a completely absent top-level Glimpse configuration directory will not be detected when it first appears. This is deliberate. There was no active configuration to preserve, so requiring one restart is cheaper than keeping a broad, noisy watch for the lifetime of every session.
The final model is more useful than "reload this file when it changes." A modular configuration loader has a dependency graph, even when the graph is only directories and symlinks. Hot reload works when the process watches the graph's stable boundaries, rereads the whole value after changes settle, and treats a moved watch as a reason to reload everything.