Skip to content

harden external-trigger watcher: polling backstop + periodic re-arm + health export#246

Merged
akuehner merged 1 commit into
mainfrom
feat/harden-external-trigger-watcher
Jun 13, 2026
Merged

harden external-trigger watcher: polling backstop + periodic re-arm + health export#246
akuehner merged 1 commit into
mainfrom
feat/harden-external-trigger-watcher

Conversation

@akuehner

Copy link
Copy Markdown
Member

fs.watch on Linux can silently die (inotify queue overflow, inode rebind after dir recreation) with no error event and no observable signal. Three hardening layers added to project-external-trigger.js:

Layer 1 - Polling backstop: A 30 s setInterval calls scanExisting() periodically. The dispatched map makes re-scanning idempotent. Uses .unref() so it does not keep the process alive. Cleared in stopWatcher().

Layer 2 - Periodic watcher re-arm: A 5 min setInterval closes and re-opens the fs.watch binding via a shared armWatcher() helper. Guarantees the inotify registration is never more than 5 min stale. Uses .unref(). Cleared in stopWatcher().

Layer 3 - Health export: watcherLastEventMs tracks the timestamp of the last watcher callback (updated before the .json filename guard). getHealth() returns watcherAlive, lastEventMs, and pollActive for external monitoring.

No new dependencies. Pure Node stdlib. All 309 existing tests pass.

… health export

fs.watch on Linux can silently die (inotify queue overflow, inode rebind after
dir recreation) with no error event and no observable signal. Three layers added:

Layer 1 (pollInterval): setInterval at 30 s calls scanExisting(). The dispatched
map makes re-scanning idempotent. Uses .unref() so it does not keep the process
alive. Cleared in stopWatcher().

Layer 2 (rearmTimer): setInterval at 5 min closes and re-opens the fs.watch
binding via a shared armWatcher() helper. Guarantees the inotify registration
is never more than 5 min stale. Uses .unref(). Cleared in stopWatcher().

Layer 3 (getHealth): watcherLastEventMs tracks the timestamp of the last watcher
callback (updated before the .json guard). getHealth() returns watcherAlive,
lastEventMs, and pollActive for external monitoring.

No new dependencies. All 309 existing tests pass.
@akuehner akuehner merged commit c048268 into main Jun 13, 2026
1 check passed
@akuehner akuehner deleted the feat/harden-external-trigger-watcher branch June 13, 2026 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant