watch originals, because a move is not an event

You should not have had to press Sync. Nine files moved into a folder and
nothing noticed.

Two gaps, and neither would have caught it. The platform's event bus emits
`files:uploaded` and `archive:extracted` — things Officer's own file browser
DID — so a move emits nothing at all, and neither would an rsync, a drop over
SMB, or anything done in a terminal. And no plugin subscribes to hooks anyway.

That is not a bug in the bus: it reports actions, and this needs to know about a
directory. So a filesystem watcher, the same shape as the mailbox one already
here.

The debounce is twenty seconds and long on purpose. A move of nine files is
dozens of events and an upload of two hundred is thousands, while a sync walks
the whole tree — so waiting for quiet means one run after the dust settles rather
than one per file. Being slightly late costs nothing, because the sync skips
everything already current; being eager costs a full walk per event.

It cannot feed itself: only originals/ is watched and the sync writes only to
derived/. Worth stating, because the opposite arrangement is a loop that looks
like a busy machine and nothing else.

A run already in flight is left alone rather than queued — it walks the tree when
it starts, and whatever lands after that is what the next quiet stretch is for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 20:50:07 +01:00
co-authored by Claude Opus 5
parent 01652f3480
commit 063d2c4a88
2 changed files with 101 additions and 0 deletions
+11
View File
@@ -11,6 +11,7 @@ import { reconcile } from './derive';
import { createDatabases, pushSchema, readDatabases } from './databases';
import { readBackups, restoreBackup, runBackup, saveBackups, schedule, testDestination } from './backups';
import { watchMailbox } from './messages';
import { watchOriginals } from './originals-watch';
import { readServers, savePorts, siteSecrets, startServer, stopServer } from './servers';
import { buildSite, readStatic, startStatic, stopStatic } from './static-site';
import { join } from 'node:path';
@@ -621,3 +622,13 @@ void schedule().catch(() => {});
same reason as above.
*/
void watchMailbox().catch(() => {});
/*
Watch `originals/` and convert what arrives.
A filesystem watcher rather than the platform's event bus, because the bus reports ACTIONS Officer
took — an upload, an extraction — and moving files into a folder is not one of them. Nine files moved
produced no event at all, and the conversion waited for somebody to press a button they had no reason
to know about.
*/
void watchOriginals().catch(() => {});