fix(daemon): finalize open sessions on shutdown so turn roots export - #107
Merged
Merged
Conversation
A turn's root `invoke_agent` span was ended only at Stop or SessionEnd. Every other daemon exit path (inactivity timeout, SIGTERM/SIGINT/SIGHUP, restart control message) tore the process down without ending open turn spans, while their already-ended children (completed tool spans, finalized chat spans, closed subagent spans) had exported. The result was rootless traces: tool spans with no user turn to attribute them to, and no agent/conversation identity (both live on the root). Extract the SessionEnd finalization into finalizeSession() and call it for every live session in the shutdown drain, before the exporter is flushed, so an interrupted turn keeps its exported root. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
Merge activity
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A turn's root
invoke_agent claude-codespan is created at UserPromptSubmit and ended only at Stop or SessionEnd. Every other daemon exit path — inactivity timeout, SIGTERM/SIGINT/SIGHUP, restart/config-change control message — ranshutdown(), which flushed the exporter but never ended open turn spans. Their already-ended children (completed tool spans, finalized chat spans, closed subagent spans) had exported; the still-open root had not. Result: rootless traces (tool spans with no user turn, and no agent/conversation identity, both of which live on the root).Local daemon log evidence: 49 shutdowns fired with an open turn span (~88 leaked roots over the log's life; ~44 in the last 7 days), almost all
inactivity, plusSIGTERMandcontrol message(restart).Extract the SessionEnd finalization into
finalizeSession()and call it for every live session inside the shutdowndrain, beforeprovider.shutdown()flushes, so an interrupted turn keeps its exported root.shutdown()now delegates to a testabledrain()(everything exceptprocess.exit).Complementary to (does not overlap) #93 (holds the daemon open while in-flight, but still drops the root when it does exit), #92 (reconstructs sessions after a restart), #87 (removes the terminal-SIGHUP kill).
Test plan
node --import tsx --test tests/daemon-shutdown-finalizes-turn.test.ts— mid-turndrainexports the root turn span (+ open subagent span) under one trace; SessionEnd still exports its root.npm run check(build + full suite): green.🤖 Generated with Claude Code