fix: harden daemon and large-index recovery paths (#1562)

* fix: harden indexing recovery and daemon liveness

* test: cover daemon and recovery review gaps

* test: pin that a failure marker never blocks a later successful parse (#1557 retry-discard guard)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Colby McHenry <me@colbymchenry.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Daniil
2026-08-20 12:53:49 -05:00
committed by GitHub
co-authored by Claude Fable 5 danusha2345 Colby McHenry
parent d8f2eeaddf
commit 81e1f4a92f
20 changed files with 680 additions and 83 deletions
+50
View File
@@ -29,6 +29,7 @@
*/
import * as crypto from 'crypto';
import * as net from 'net';
import * as os from 'os';
import * as path from 'path';
import { getCodeGraphDir } from '../directory';
@@ -101,6 +102,55 @@ export interface DaemonLockInfo {
startedAt: number;
}
/**
* Verify that the process named by a lockfile is the CodeGraph daemon serving
* its socket. A bare PID liveness probe is insufficient because OSes reuse PIDs
* after an OOM/SIGKILL (#1553).
*/
export function probeDaemonIdentity(info: DaemonLockInfo, timeoutMs = 1_000): Promise<boolean> {
if (!Number.isInteger(info.pid) || info.pid <= 0 || !info.socketPath) return Promise.resolve(false);
return new Promise<boolean>((resolve) => {
let socket: net.Socket;
let buffer = '';
let done = false;
const finish = (ok: boolean) => {
if (done) return;
done = true;
clearTimeout(timer);
socket.destroy();
resolve(ok);
};
const timer = setTimeout(() => finish(false), timeoutMs);
timer.unref?.();
try {
socket = net.createConnection(info.socketPath);
} catch {
clearTimeout(timer);
resolve(false);
return;
}
socket.setEncoding('utf8');
socket.on('data', (chunk) => {
buffer += String(chunk);
if (buffer.length > 4096) return finish(false);
const newline = buffer.indexOf('\n');
if (newline < 0) return;
try {
const hello = JSON.parse(buffer.slice(0, newline)) as Record<string, unknown>;
finish(
hello.protocol === 1 &&
hello.pid === info.pid &&
(info.version === 'unknown' || hello.codegraph === info.version)
);
} catch {
finish(false);
}
});
socket.on('error', () => finish(false));
socket.on('close', () => finish(false));
});
}
/**
* Serialize a {@link DaemonLockInfo} for writing to the pidfile. JSON for
* human readability — operators occasionally `cat` this when debugging.