* 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>)
| 877 | * the same potentially-stale data the un-gated path would have. |
| 878 | */ |
| 879 | private async awaitCatchUpGate(gate: Promise<void>): Promise<void> { |
| 880 | const timeoutMs = resolveCatchUpGateTimeoutMs(); |
| 881 | if (timeoutMs <= 0) { |
| 882 | // 0 = opt back into the original unbounded wait. |
| 883 | try { await gate; } catch { /* engine already logged */ } |
| 884 | return; |
| 885 | } |
| 886 | let timer: NodeJS.Timeout | undefined; |
| 887 | const timedOut = new Promise<'timeout'>((resolve) => { |
| 888 | timer = setTimeout(() => resolve('timeout'), timeoutMs); |
| 889 | timer.unref?.(); |
| 890 | }); |
| 891 | try { |
| 892 | const outcome = await Promise.race([ |
| 893 | gate.then(() => 'done' as const, () => 'done' as const), |
| 894 | timedOut, |
| 895 | ]); |
| 896 | if (outcome === 'timeout') { |
| 897 | process.stderr.write( |
| 898 | `[CodeGraph MCP] Catch-up reconcile still running after ${timeoutMs}ms; serving this tool call now and finishing the reconcile in the background (#905). ` + |
| 899 | `Set CODEGRAPH_CATCHUP_GATE_TIMEOUT_MS=0 to always wait for it.\n` |
| 900 | ); |
| 901 | } |
| 902 | } finally { |
| 903 | if (timer) clearTimeout(timer); |
| 904 | } |
| 905 | } |
| 906 | |
| 907 | /** |
| 908 | * Record the directory the server tried to resolve the default project from. |
no test coverage detected