fix(server): skip native close in the analyze worker to avoid the LadybugDB destructor crash (#2264 review P2-3)

The forked server analyze worker runs runFullAnalysis then force-exits
(process.exit(0)). With a real native close inside runFullAnalysis, the LadybugDB
ClientContext destructor can double-free after --pdg writes and abort the worker
BEFORE it sends 'complete', failing the parent's analyze. Pass
skipNativeCloseOnExit: true so the worker checkpoints for durability and lets its
process.exit reclaim the handles — same about-to-exit contract as the CLI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBJomjoTdBV2eveDVq4JMm
This commit is contained in:
Gergo Magyar 2026-06-21 07:42:15 +00:00
parent f3b809624a
commit 8a6799c5ff

View file

@ -74,14 +74,23 @@ process.on('message', async (msg: StartMessage) => {
started = true;
try {
const result = await runFullAnalysis(msg.repoPath, msg.options, {
onProgress: (phase, percent, message) => {
send({ type: 'progress', phase, percent, message });
const result = await runFullAnalysis(
msg.repoPath,
// This worker force-exits (process.exit(0) below) right after sending its
// result, so skip the native close: it can double-free in LadybugDB's
// ClientContext destructor after --pdg writes and abort the worker BEFORE it
// can send 'complete' (#2264). flushWAL still persists the index; the
// subsequent process.exit reclaims the native handles.
{ ...msg.options, skipNativeCloseOnExit: true },
{
onProgress: (phase, percent, message) => {
send({ type: 'progress', phase, percent, message });
},
onLog: (message) => {
send({ type: 'progress', phase: 'log', percent: -1, message });
},
},
onLog: (message) => {
send({ type: 'progress', phase: 'log', percent: -1, message });
},
});
);
// Send a JSON-safe projection, NOT the raw result: the IPC channel is
// default-JSON serialization and `result.pipelineResult` carries the live