D23. A project is watched once open (7 Oct 2026)
openProject resolves only once the folder’s watcher reports what is written from then on, and the session’s first listing of the folder comes after it, so that an agent writing as soon as the app has opened the project is seen at once rather than at the next listing, 15 s later. The cause, measured on macOS (Node 24, libuv 1.52): a process has one FSEvents stream for all its watchers, built again on its own thread whenever one starts or stops, and writes from about 16 ms before to 10 ms after are never reported, for every watcher of the process; with sessions kept open in one process (the engine tests, an app with several projects), a write right after opening was missed in 5 opens of 120. The Store contract gains watch(onChange, onReady?); FsStore on macOS writes a probe file in the project’s .elabora/ every 25 ms until its watcher reports it, then removes it (about 12 ms; 2 s at most, for a volume without FSEvents; at once when it cannot be written), and the session starts the journal’s watcher before the folder’s, so that the probe proves the stream holds both. Costs accepted: a small file written and removed in .elabora/ at each open on macOS, and the open time (median 7 → 14 ms in the measure, packages/engine/bench/watch-ready.ts); no miss in 240 opens since. The periodic listing stays the safety net.
Later rebuilds (7 Oct 2026). A watcher started or stopped later in the same process still cost an open project its writes: measured in the engine with an agent writing a file every millisecond (packages/engine/bench/watch-rebuild.ts, the listing off), a preview started and stopped 10 times lost 467 writes of 5786, another project opened and closed 10 times 336 of 4297. FsStore now watches each folder from a worker thread of its own on macOS: a worker has its own event loop, hence its own FSEvents stream, which no other watcher of the process rebuilds; none lost since, and the probe still says when the thread’s watcher reports. Chosen over listing again after each watcher of the process starts or stops, which holds only for the watchers Elabora starts and tells about, and relies on when libuv’s thread rebuilds the stream. Costs: a thread and about 10 MB per open project, and the open on macOS (the watcher reports after 38 ms instead of 11, median; the engine measure opens in 41 ms instead of 19). The journal’s watcher stays in the engine’s thread: what other Elabora tools journal they also write in the folder, and the journal is read before the files.
Revisit if: a store whose watcher cannot tell when it is live is added, the memory of a thread per open project matters (many projects open at once), or the preview must see every change (its watchers stay in the engine’s thread and miss what is written while another watcher starts or stops).