The #850 liveness watchdog SIGKILLs a process whose main-thread event loop
stalls past its window (60s default). It was extended to `index`/`init` in
#999, but reference resolution and callback-edge synthesis run synchronously
on that same thread — so on a large repo a legitimate, in-progress index gets
killed, and users had to disable the watchdog entirely (CODEGRAPH_NO_WATCHDOG=1).
Make the long synchronous spans yield cooperatively so the heartbeat keeps
firing during real work, while a genuinely wedged span (which never reaches a
yield) still trips the watchdog:
- synthesizeCallbackEdges yields between its whole-graph passes, and the heavy
scanners (closure-collection, event-emitter, JSX-child, object-registry,
field-channel) yield within their loops;
- batched resolution sub-chunks each batch with yields;
- the deferred chained-call and this-member post-passes yield per ref.
Behaviour-preserving — only timing changes; node/edge counts are identical.
Validated end-to-end with the real watchdog armed at the default 60s: the
released build is SIGKILLed partway through indexing the Swift compiler (27k
files, ~1.1M edges) and the TypeScript compiler, while the fixed build indexes
both to completion.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>