Field report (nectar 0.1.3, Windows, honeycomb 0.2.3 fleet, ~85k-file repo)
/health on 3854 never responds even after indexing completes. Memory ramps to ~868MB walking the tree, plateaus and declines to ~670MB (walk finished), the port is a clean Listen, earlier /health connections went CloseWait then cleared, and a fresh GET /health still times out indefinitely. Fleet-status therefore omits nectar and the Hive dashboard stalls on "starting" forever.
Root-cause investigation (current main, 2026-07-04)
The daemon is one Node process; GET /health is served by node:http on the same event loop that runs the indexing pipeline, and the pipeline contains multi-minute fully synchronous stretches:
- Discovery blocks:
spawnGitLsFiles uses spawnSync (src/brooding/discovery.ts:117-124), and the walk fallback is a synchronous readdirSync generator (src/registration/disk-fs.ts:100).
- Content prep blocks hardest:
prepareFiles is a tight synchronous loop over every discovered file (src/brooding/precheck.ts:126-133), and each readContent is a bare readFileSync (src/registration/disk-fs.ts:70) followed by hashing. On ~85k files this is minutes of uninterrupted synchronous work.
- The registration cold-catch-up resync is one macrotask:
runCycle (src/registration/service.ts:290-320) enumerates fs.listPaths() (sync walk) and runs processOne (statSync + readFileSync + hash + TLSH) for every path inside a while loop with no awaits and no yields. The function is async in name only on this path.
While any of these run, the HTTP handler cannot be scheduled: the socket accepts (kernel backlog) but the request never gets serviced, which is exactly the reported Listen-but-timeout behavior.
Mitigation already shipped (PR #21)
Dormant-by-default (PRD-019) means a fresh boot no longer indexes anything, so /health answers immediately at boot and the Hive gate clears. But the moment a project is activated and a brood or cold resync starts on a large repo, the starvation above recurs.
Ask
Make /health respond independently of indexing, structurally:
- Move discovery + prepare + resync work off the event loop (worker thread), or make the loops yield (async iteration with per-N-files awaits and async fs), or both.
- Bound the per-macrotask work so no single stretch exceeds a small budget.
- Add a regression test: /health must answer within a bounded time WHILE a large synthetic brood/resync is in flight.
Related: the post-index busy-loop is filed separately (it also starves /health after indexing).
Field report (nectar 0.1.3, Windows, honeycomb 0.2.3 fleet, ~85k-file repo)
/health on 3854 never responds even after indexing completes. Memory ramps to ~868MB walking the tree, plateaus and declines to ~670MB (walk finished), the port is a clean Listen, earlier /health connections went CloseWait then cleared, and a fresh GET /health still times out indefinitely. Fleet-status therefore omits nectar and the Hive dashboard stalls on "starting" forever.
Root-cause investigation (current main, 2026-07-04)
The daemon is one Node process; GET /health is served by node:http on the same event loop that runs the indexing pipeline, and the pipeline contains multi-minute fully synchronous stretches:
spawnGitLsFilesusesspawnSync(src/brooding/discovery.ts:117-124), and the walk fallback is a synchronousreaddirSyncgenerator (src/registration/disk-fs.ts:100).prepareFilesis a tight synchronous loop over every discovered file (src/brooding/precheck.ts:126-133), and eachreadContentis a barereadFileSync(src/registration/disk-fs.ts:70) followed by hashing. On ~85k files this is minutes of uninterrupted synchronous work.runCycle(src/registration/service.ts:290-320) enumeratesfs.listPaths()(sync walk) and runsprocessOne(statSync + readFileSync + hash + TLSH) for every path inside a while loop with no awaits and no yields. The function isasyncin name only on this path.While any of these run, the HTTP handler cannot be scheduled: the socket accepts (kernel backlog) but the request never gets serviced, which is exactly the reported Listen-but-timeout behavior.
Mitigation already shipped (PR #21)
Dormant-by-default (PRD-019) means a fresh boot no longer indexes anything, so /health answers immediately at boot and the Hive gate clears. But the moment a project is activated and a brood or cold resync starts on a large repo, the starvation above recurs.
Ask
Make /health respond independently of indexing, structurally:
Related: the post-index busy-loop is filed separately (it also starves /health after indexing).