Detached watchdog telemetry process
Skill kjuhwa/skills-hub/skills/telemetry/detached-watchdog-telemetry-process
Self-correcting knowledge corpus for Claude Code — 9 stable shape clusters, bias-correction pipeline baked into contribution flow. 47 papers, 45 techniques, 1.1k skills.
npx -y skills add kjuhwa/skills-hub --skill detached-watchdog-telemetry-processAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Run telemetry shipping in a detached child process fed by the parent over stdin, so telemetry never blocks tool execution and gets a guaranteed flush-on-parent-death window.
SKILL.md
3.7 KB, 775 tokens by cl100k_base, as published. Nobody here has run it
Watchdog Child Process for Telemetry
When to use
- Your app emits telemetry events but can't afford to block tool calls on network I/O, retries, or rate-limit backoff.
- Events are low-value individually but matter in aggregate; losing a handful on crash is acceptable, dropping the whole buffer on normal exit is not.
- You want telemetry completely gone in tests and opt-outs, not just a no-op flag.
How it works
- Parent process spawns a
node watchdog/main.jsas a detached child withstdio: ['pipe', 'ignore', 'ignore']andchild.unref()so the child doesn't keep the parent alive on its own. - Parent
.send(msg)writesJSON.stringify(msg) + '\n'to the child's stdin. Messages are fire-and-forget; if stdin is destroyed the parent logs and drops. - Child reads newline-delimited JSON via
readline.createInterface({input: process.stdin}). It buffers events (e.g. 1000-entry ring with drop-oldest overflow) and flushes on a timer. - Parent-death detection: child listens on
stdin.on('end'),stdin.on('close'), andprocess.on('disconnect'). Any of those fires a shutdown path that sends aserver_shutdownevent, runs a short final-flush with a 5s race againstsetTimeout, then exits. - Config flows via argv at spawn time (
--parent-pid=,--app-version=,--clearcut-endpoint=,--log-file=). No shared filesystem state between parent and child during a session.
Example
// WatchdogClient.ts - parent side
const child = spawn(process.execPath, [watchdogPath,
`--parent-pid=${process.pid}`,
`--app-version=${appVersion}`,
`--os-type=${osType}`,
...(logFile ? [`--log-file=${logFile}`] : []),
], { stdio: ['pipe', 'ignore', 'ignore'], detached: true });
child.unref();
function send(msg) {
if (child.stdin && !child.stdin.destroyed) {
child.stdin.write(JSON.stringify(msg) + '\n');
}
}
// watchdog/main.ts - child side
process.stdin.on('end', () => onParentDeath('stdin end'));
process.stdin.on('close', () => onParentDeath('stdin close'));
readline.createInterface({input: process.stdin}).on('line', line => {
const msg = JSON.parse(line);
sender.enqueueEvent(msg.payload);
});
function onParentDeath(reason) {
sender.sendShutdownEvent().finally(() => process.exit(0));
}
Gotchas
- If you don't call
child.unref(), node won't exit while the watchdog is alive — your CLI looks hung. stdio: 'pipe'for stdin is required for IPC; stdout/stderr should be'ignore'or redirected, or the detached child's output will appear mid-prompt in a TTY parent.- On normal parent exit, stdin
endfires before the child sees disconnect — listen to both so detached vs. IPC-parented spawns both shut down. - Use
{detached: true}andunref().detachedalone on Windows doesn't decouple the child's console. - Race
finalFlush()againstsetTimeout(SHUTDOWN_TIMEOUT_MS)— neverawaita network call unconditionally in a shutdown path, or a hung endpoint can hold your process open. - Ship an opt-out env var that is checked in the parent before spawning the watchdog; a running-but-idle watchdog still uses memory and file handles.