Skip to content

[v0.8 Core 7] overlap refinement drain with kernel teardown - #1335

Merged
sethkarten merged 2 commits into
v080/core-split-c4-sibling-validationfrom
v080/core-split-c7-lifecycle
Aug 13, 2026
Merged

[v0.8 Core 7] overlap refinement drain with kernel teardown#1335
sethkarten merged 2 commits into
v080/core-split-c4-sibling-validationfrom
v080/core-split-c7-lifecycle

Conversation

@sethkarten

@sethkarten sethkarten commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Replacement scope

This PR reconstructs and supersedes the unique implementation delta reviewed in #1265 without rewriting that historical branch. The original PR remains the immutable discussion record: #1265

  • Base: v080/core-split-c4-sibling-validation
  • Replacement branch: v080/core-split-c7-lifecycle
  • Replacement commit: 2cab0cd1003432619b9e253dd58f01febae93a4f
  • Propagation/reconciliation merge commits are intentionally excluded.
  • fix(mcp): make cleanup failures observable #1264 is intentionally omitted from the replacement stacks because its declared-base-to-head tree delta is empty.
  • Frozen feat(kernel): dispatch host requests as capabilities #1243 (77b188b92dc91365cb2bc41bdb46a50669d104a8) is the shared foundation. For reconstructed deltas it is a proven tree-compatible base, not an ancestry claim about the historical PR stack.

Validation

  • Biome 2.5.5 on the exact changed paths: pass
  • root tsgo --noEmit: pass
  • Core focused suite on the final Core tip with live daemon/RLM environment removed and single-worker execution: 11 files, 388 tests passed
  • MCP focused suite on the final MCP tip: 9 files, 106 tests passed
  • Independent Terra tree/delta review: pass

No original PR was retargeted, closed, merged, or otherwise mutated.


Note

Medium Risk
Changes session shutdown ordering and error surfacing for kernel teardown and concurrent dispose callers; behavior is well covered by new tests but affects core lifecycle paths.

Overview
AgentSession.disposeAsync is reworked so graceful shutdown overlaps refinement drain and IPython kernel disposal instead of awaiting them strictly in sequence. Both start before the first await, run under Promise.allSettled, and rejections surface as a single error or an AggregateError when both fail—while synchronous cleanup still runs via dispose() in a finally path.

Concurrent and late disposeAsync callers now always join the same in-flight _disposeAsyncPromise, including when _disposed is already true, so they observe the same terminal outcome (success or failure). Kernel dispose() is no longer invoked from _disposeAsyncOnce (where failures were previously swallowed).

A new _asyncTeardownStarted flag is set at the start of async teardown; reload() and _buildRuntime() throw if called while disposal is underway, preventing the runtime from replacing the kernel mid-teardown.

Tests in agent-session-concurrent.test.ts cover parallel start order, failure propagation, single finalization, late callers, reload blocking, and dual failure aggregation.

Reviewed by Cursor Bugbot for commit 3055505. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Overlap refinement drain with kernel teardown in AgentSession.disposeAsync

  • disposeAsync now runs _drainPendingRefinementForDisposal() and _ipythonKernelProvisioner?.dispose() concurrently via Promise.allSettled, aggregating failures into an AggregateError when both reject.
  • All concurrent and late callers (including those arriving after _disposed is already true) share the same terminal promise result, including any rejection.
  • Kernel disposal is removed from _disposeAsyncOnce, which previously silently swallowed kernel disposal errors.
  • A new _asyncTeardownStarted flag causes _buildRuntime and reload to throw immediately if called after async teardown has begun.
  • New tests in agent-session-concurrent.test.ts cover drain/kernel ordering, failure propagation, late-caller sharing, and reload blocking.

Macroscope summarized 3055505.

Reconstruct the unique net delta from PR #1265, excluding propagation merges.

@jonaowen jonaowen left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The overlap is unsafe without a complete admission fence. disposeAsync() sets _asyncTeardownStarted and immediately starts kernel disposal while deliberately leaving _disposing === false until both kernel disposal and refinement drain settle. But normal action admission, queue pumping, subagent spawning, and most lifecycle guards still check only _disposed || _disposing (for example _assertSessionActionAdmissionAvailable, _admitSessionInput, and _spawnRlmChild). A concurrent prompt/message/heartbeat can therefore be admitted and begin using the authoritative kernel while it is being snapshotted/disposed. The patch gates only reload() and _buildRuntime(), so it does not establish exclusivity.

Please add one admission predicate that treats _asyncTeardownStarted as closed for every external/new-work entry point and queue pump, while allowing only the already-owned refinement drain/finalizers to complete. Then add a behavioral test that holds kernel disposal and refinement drain separately, attempts prompt/steer/message/subagent/reload during each boundary, and proves zero action admission/provider/kernel use plus one terminal teardown outcome. Also cover synchronous dispose() racing after async teardown starts.

@sethkarten
sethkarten merged commit d4dec3a into core02-host-request-dispatcher Aug 13, 2026
2 checks passed
@sethkarten
sethkarten deleted the v080/core-split-c7-lifecycle branch August 13, 2026 21:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants