Skip to content

fix(daemon): finalize open sessions on shutdown so turn roots export - #107

Merged
rgao-coreweave merged 1 commit into
mainfrom
fix/daemon-finalize-sessions-on-shutdown
Jul 1, 2026
Merged

fix(daemon): finalize open sessions on shutdown so turn roots export#107
rgao-coreweave merged 1 commit into
mainfrom
fix/daemon-finalize-sessions-on-shutdown

Conversation

@rgao-coreweave

Copy link
Copy Markdown
Contributor

Summary

A turn's root invoke_agent claude-code span 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 — ran shutdown(), 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, plus SIGTERM and control message (restart).

Extract the SessionEnd finalization into finalizeSession() and call it for every live session inside the shutdown drain, before provider.shutdown() flushes, so an interrupted turn keeps its exported root. shutdown() now delegates to a testable drain() (everything except process.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-turn drain exports 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

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>
@rgao-coreweave
rgao-coreweave marked this pull request as ready for review July 1, 2026 03:03
@rgao-coreweave
rgao-coreweave requested a review from a team as a code owner July 1, 2026 03:03

@drtangible drtangible left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍 👍

rgao-coreweave commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Jul 1, 5:07 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 1, 5:07 PM UTC: @rgao-coreweave merged this pull request with Graphite.

@rgao-coreweave
rgao-coreweave merged commit 86fefdb into main Jul 1, 2026
4 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants