* Await the catch-up gate, but no longer than the configured timeout (#905). * If the reconcile settles first, we got the fully-reconciled answer. If the * timeout wins, we serve the call now and let the reconcile finish in the * background — it yields to the event loop (see SYNC_RECONCILE_
(gate: Promise<void>)
| 847 | * the same potentially-stale data the un-gated path would have. |
| 848 | */ |
| 849 | private async awaitCatchUpGate(gate: Promise<void>): Promise<void> { |
| 850 | const timeoutMs = resolveCatchUpGateTimeoutMs(); |
| 851 | if (timeoutMs <= 0) { |
| 852 | // 0 = opt back into the original unbounded wait. |
| 853 | try { await gate; } catch { /* engine already logged */ } |
| 854 | return; |
| 855 | } |
| 856 | let timer: NodeJS.Timeout | undefined; |
| 857 | const timedOut = new Promise<'timeout'>((resolve) => { |
| 858 | timer = setTimeout(() => resolve('timeout'), timeoutMs); |
| 859 | timer.unref?.(); |
| 860 | }); |
| 861 | try { |
| 862 | const outcome = await Promise.race([ |
| 863 | gate.then(() => 'done' as const, () => 'done' as const), |
| 864 | timedOut, |
| 865 | ]); |
| 866 | if (outcome === 'timeout') { |
| 867 | process.stderr.write( |
| 868 | `[CodeGraph MCP] Catch-up reconcile still running after ${timeoutMs}ms; serving this tool call now and finishing the reconcile in the background (#905). ` + |
| 869 | `Set CODEGRAPH_CATCHUP_GATE_TIMEOUT_MS=0 to always wait for it.\n` |
| 870 | ); |
| 871 | } |
| 872 | } finally { |
| 873 | if (timer) clearTimeout(timer); |
| 874 | } |
| 875 | } |
| 876 | |
| 877 | /** |
| 878 | * Record the directory the server tried to resolve the default project from. |
no test coverage detected