Skip to content

Orphans and the registry

Cause. Hosts that were hard-killed. Every in-process guard — job objects, process groups, watchdogs — needs some of our code to run, and a SIGKILL runs none of it. The CLI survives, invisible to any later host.

Fix. This is what CliRegistry exists for. Register at spawn, unregister at exit, and sweep at startup:

const registry = new CliRegistry(stateDirectory);
// At startup, before spawning anything.
registry.prune();
for (const orphan of registry.orphans()) {
console.warn('left behind by a dead host:', orphan.pid, orphan.sessionId);
// Kill it, or offer to — the decision is the host's.
}
// Around each session.
registry.register(childProcess, sessionId, workingDirectory);
session.onClose(() => registry.unregister(session.pid));

Arm a watchdog too, so the common case — the host closing without running cleanup — is caught in-process:

new ParentWatchdog().start(() => {
session.stop('SIGKILL');
process.exit(0);
});
Cause Detail
The state directory is unwritable register() swallows write failures by design — a host that cannot write its state directory should still be able to run a CLI
The pid came back as something else liveEntries() requires the session id to still appear in the process’s command line, so a reused pid is correctly excluded
Identity could not be read commandLine() returning null means unknown, not dead — and unknown is deliberately not a match, because the alternative is killing a stranger’s process

Cause. Two CLIs are appending to the same conversation, which branches its history.

Fix. Ask before resuming:

const existing = registry.findLive(sessionId);
if (existing) {
throw new Error(`session ${sessionId} is already driven by pid ${existing.pid}`);
}

This is the second reason the registry exists, alongside orphan cleanup.

Each guard fails in a different way, which is why none of them is the only one. See Architecture for the full table; the short version is that the registry catches whatever all the in-process guards missed, after the fact, because writing a file at spawn and removing it at exit is the only record that survives a host being killed outright.