Watcher: Emit a startup heartbeat before the initial directory scan - #66
Merged
Conversation
`HeartbeatLoop.start()` now sends a synchronous `status="watching"` heartbeat and flushes the event reporter before spawning the loop thread. Without this, the loop's first `Event.wait(timeout=interval)` deferred the first beat by a full 60s — and the just-queued `WATCHER_STARTED` event with it — so the dashboard saw nothing for the entire initial scan window. On headless Windows services that made the watcher indistinguishable from a hung process. Bumps the watcher to 0.2.7. Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
HeartbeatLoop.start()now sends a synchronousstatus="watching"heartbeat and flushes the event reporter before spawning the loop thread. Without this, the loop's firstEvent.wait(timeout=interval)deferred the first beat by a full 60s — and the just-queuedWATCHER_STARTEDevent with it — so the dashboard saw nothing for the entire initial scan window. On headless Windows services that made the watcher indistinguishable from a hung process.0.2.7(lockfile regenerated viauv sync).Why this is the minimum-blast-radius fix
web-app/app/api/v1/watchers/[watcherId]/heartbeat/route.tsand thewatcher_statusPostgres enum inweb-app/lib/db/schema.tsonly accept["registered", "watching", "stopped"]. A new status ("starting"/"scanning") would have required a DB migration + dashboard work for no signal the existing"watching"doesn't already convey at this point — the watcher process is up and intends to watch; the initial scan is part of normal startup.path.stat()/path.iterdir()calls release the GIL). The only gap was the initial silence — closed by the synchronous startup beat._send_heartbeatandEventReporter.flush()both swallow network errors internally, so a startup with the API unreachable still proceeds; the loop thread starts and the first successful tick emits the standardheartbeat_recoveredevent.Test plan
TestHeartbeatStartupBeatclass inwatcher/tests/test_heartbeat.pycovering:status="watching"heartbeat is sent onstart().EventReporter.flush()is invoked from the main thread before the loop thread becomes alive (soWATCHER_STARTEDships before the initial scan).ApiErrordoes not prevent the loop thread from spawning, and the failure is recorded so the next successful tick emitsheartbeat_recovered.uv run pytest watcher/tests/test_heartbeat.py watcher/tests/test_runtime.py -v→ 40/40 pass (4 new + 36 pre-existing).make check-all→ ruff, ruff-format, pyright, prettier, eslint, tsc all clean.WATCHER_STARTEDevent appear in Data Hub within seconds (not after the previous ~60s gap).Made with Cursor