fix(dev): fail MCP tool calls closed when the pinned epoch vanishes - #134
Conversation
A live playground session pins an artifact epoch, but epoch leases are process-local: another process's build retention can remove the pinned epoch underneath the session. tools/call then kept executing against a vanished artifact (or pended untyped) with no indication the project changed. Probe the epoch store on the tool-call path and fail closed with a typed McpSessionStaleEpochError that cancels in-flight calls and closes the session, surfaced by the session routes as AB8018 (409) like the artifact routes' epoch mapping. Fixes #124
🦋 Changeset detectedLatest commit: 1ae4610 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1ae461011a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Summary
tools/callwedge in the MCP playground: epoch leases are process-local (epochReferenceCountslives in module state), so an externalagent-bundle buildrun's retention cleanup — which executes on every epoch publish — deletes the epoch a live dev-server MCP session has pinned. The session then keeps operating against a vanished artifact: new tool calls silently "succeed" from the child's memory, in-flight ones pend to an untyped SDK timeout, and nothing anywhere surfaces that the project changed underneath the session.McpSession.callToolnow probes the epoch store before dispatch and re-probes when a call fails mid-flight. A vanished epoch raises a typedMcpSessionStaleEpochError(names the epoch, states the project changed), cancels every in-flight tool call with the same typed failure, and fail-closes the session — mirroring the existing stderr-overflow contract.AB8018(409) diagnostic, mirroring the artifact routes'EPOCH_NOT_FOUNDmapping, so the workbench playground surfaces it through its existing invocation-failure UI state. No wire DTO fields change, so the workbench strict decoders are untouched.Regression coverage
mcp-session-service.test.ts: live stdio session pinned to epoch-1; epoch-2 published (pinned session keeps working, preserving the epoch-pinning contract); epoch-1 directory+metadata then removed the way an external process's retention would. Asserts the nexttools/calland the already-in-flighthangcall both reject with the typed stale-epoch error within a bounded time (session timeout is set beyond the test timeout to prove fail-fast), and the session is invalidated. Verified to fail on main (call silently resolves) and pass with the fix.mcp-session-routes.test.ts:tools/callfailing withMcpSessionStaleEpochErrormaps to409{ code: 'AB8018' }.Fixes #124