fix(server): report session exits when a provider instance is torn down - #12533
rileyjshaw wants to merge 1 commit into
Conversation
Removing or replacing a provider instance (any `providerInstances` settings edit) closed the instance scope while threads were mid-turn, and the orchestrator never heard about it: those threads stayed "running" forever. - CodexSessionRuntime.close emitted `session/closed` and then immediately `Queue.shutdown` the events queue, which clears buffered messages, so the exit was dropped before the adapter forwarder took it. End the queue instead so the consumer drains it. - CodexAdapter.stopSessionInternal closed the session scope (interrupting the forwarder) right after `close`; wait for the forwarder to drain first. Both adapters now end their runtime event queue on release instead of shutting it down, so already-emitted exits stay readable. - ProviderInstanceRegistry.reconcile now calls `adapter.stopAll()` before closing a removed/replaced instance's scope. Adapter finalizers stop sessions silently on purpose (server shutdown keeps threads resumable); a settings edit is not a shutdown. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Macroscope skipped reviewing this pull request. Per-review cost limit exceeded (workspace setting). This review would cost an estimated $37.56, which exceeds your per-review limit of $15.00. The top 3 files driving up this estimate:
Tip To get this pull request reviewed, you can:
|
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This is a focused provider-session teardown fix that preserves exit events across registry, Codex, and Claude shutdown paths, with targeted tests. Human review is required because the PR also adds a static-analysis suppression directive in an integration test. Not approved because:
Review your spending limits in Billing settings, or comment |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: pingdotgg/t3code/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (8)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe registry now calls ChangesProvider reconcile teardown
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
What Changed
Provider instance teardown (a
providerInstancessettings edit that removes or replaces an instance) now reportssession.exitedfor every live session instead of silently killing them.CodexSessionRuntime.closeends its events queue (Queue.end) instead ofQueue.shutdown.shutdownclears buffered messages, so thesession/closedevent emitted one line earlier was dropped whenever the adapter's forwarder had not taken it yet.CodexAdapter.stopSessionInternalwaits for the event forwarder to drain (bounded by a 5s timeout) before closing the session scope, which is what interrupts that forwarder.CodexAdapterandClaudeAdapterend their runtime event queue on release instead of shutting it down, so exits already emitted stay readable byProviderService.ProviderInstanceRegistry.reconcilecallsadapter.stopAll()before closing a removed/replaced instance's scope. Adapter finalizers stop sessions silently on purpose (server shutdown keeps threads resumable), but a settings edit is not a shutdown.Tests: runtime-level (real
CodexSessionRuntimeagainst the mock peer: events collected aftercloseincludesession/closed), adapter-level (stopSessionforwards the runtime's graceful exit assession.exited), and registry-level (reconcileremoval callsstopAllbefore the scope finalizer).Why
Observed on 0.0.42: a
server.updateSettingscall replacedproviderInstanceswhile two Codex threads were mid-turn.reconcileclosed the old instance's scope, the app-server died, and neither thread ever received a terminal event. Both stayed inrunningwith the "Thinking" indicator indefinitely (the provider event log shows the last event at the moment of teardown and nothing after). Interrupting them later left one thread stuck instartingbecause the queued turn waited on an abort from a session that no longer existed.The graceful exit path already exists end to end (
session/closed→session.exited→ sessionstopped, active turn cleared); it just never made it out of the runtime's queue. This change makes the existing path deliver instead of adding a new one.UI Changes
None.
Checklist
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests
Style