Repository navigation
fix(mcpl): a late push/render answer is recorded, and a paused plane doesn't hold it (#398) - #505
Merged
Conversation
…doesn't hold it (anima-research#398) A push/render answered after its 5 s timeout, once the coalescer had materialized the batch's fallback, was emitted as the connection's 'orphaned-render-response', which nothing in src/ or test/ listens for (probed: no listener, no trace). It wasn't a control-plane event, so while the data plane was paused it was held and took a slot under the notification cap. RFC-006 §5.4 rule 3 discards such a result, and §11 asks a host that keeps an audit record to record discarded late render results, linked by eventId. - The connection folds it into the event it already emits for a late answer to a request it keeps track of: 'orphaned-response', with method 'push/render' and the render's featureSet, key and eventId. That event is control-plane, so a paused data plane neither holds it nor spends a slot on it. 'orphaned-render-response' is no longer emitted; McplServerConnection is exported, so the changelog names it. - The framework's 'orphaned-response' listener records a response that carries an eventId as a mcpl:coalescing late-render, with serverId, eventId, featureSet, key and discarded: true. The coalescer already keeps RFC-006's audit in that trace: render-failed with the eventId when the render times out, and late-render with discarded: true when a cancelled render's result arrives. So the late result sits beside its batch's render-failed. mcpl:orphaned-response keeps its own concern: checkpoints, and tool calls whose outcome is unknown. The trace type's doc says this record names serverId, featureSet and key in place of the coalescer's subject key, which the connection can't build. - orphanedRequests' renderParams is typed as the three strings it holds. anima-research#398 also names a manifest refresh that §17.8's limiter deferred and that failed with no trace. anima-research#208 fixed that one: rerunManifestRefresh runs both deferred re-runs through containedInboundHandler, and test/mcpl-request-handler-errors.test.ts pins it. Tests (test/mcpl-late-render.test.ts), a real connection wired by the framework with mocked timers: after push/render's timeout, the late answer emits one orphaned-response with method push/render, featureSet, key and eventId, and the only record is the mcpl:coalescing late-render (no mcpl:orphaned-response). With the data plane paused, it's recorded at once and nothing is held. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and the listener tells it apart by method (anima-research#398) Our internal review of the first commit found these, each fixed here. A channel-scoped push/render kept only featureSet, key and eventId, so its late-render named no channel, and a channel-scoped subject whose occurrence has no feature set showed an empty featureSet. renderParams now keeps channelId when the push/render carried one, and the orphaned-response event and the late-render record name it. A late JSON-RPC error and a late result made identical records. The orphaned-response event carries a late answer's error ({ code, message }), and the late-render record carries its message as `error`, bounded with boundedForLog(…, 500), since the server wrote it. The framework's listener chose the late-render branch by whether the event carried an eventId. It now chooses by method === 'push/render', which is what renderParams is kept for. The connection's comment said a timed-out push/render carries its subject; it carries the render's fields, which the trace type's doc names, the subject key being the coalescer's. A late error answer and a late null result had no test, and nothing pinned that a late tools/call answer still reaches mcpl:orphaned-response rather than late-render. test/mcpl-late-render now answers a timed-out push/render with an error and with null, names a channel-scoped render's channel, and checks a tool call whose mandatory deadline passed. The changelog names channelId and error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eeps its code (anima-research#398) Our internal review of the late-render round found these, each fixed here. The connection passed a late JSON-RPC error on with every late answer, but the framework recorded it only for a push/render: a tool call answered with an error after its mandatory deadline made the same mcpl:orphaned-response record as one answered with a result. That record now carries `error` too. A late error was recorded as its message alone, so two errors with the same text and different codes read the same. Both records now word it as the connection words a live one, `[code] message`, through describeCause and boundedForLog(…, 500), since the server wrote it. The result branch of the connection's late answer also passed an error on. Only a response carrying both a result and an error could reach it there, and JSON-RPC forbids that. It no longer does: an error answer has no result, and the other branch passes its error on. Comments: the framework's listener names both kinds of late answer it records, the connection says the render's fields were kept from its request when the timeout fired, and the trace types say how `error` is worded. The test file's header names channelId and error, and the changelog's heading names tool calls beside push/render. Tests: a late error is recorded with its code, a long late error with control characters is recorded with them replaced and cut to 500 code points, and a tool call answered late with an error records it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e error in its record (anima-research#398) The last round took the error off the result branch of the connection's late answer, reasoning that only a response with both a result and an error, which JSON-RPC forbids, could reach it there. But the live path rejects such an answer as an error, and a malformed answer is when its record is read closely: without the error, a late one read as a success. The result branch passes the error on again, and a test pins it: a timed-out push/render and a tool call past its deadline, each answered with a result carrying state and an error, are recorded with the error, the tool call with hadState true. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Required label not found on this PR. |
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 18, 2026
…eps its class, outcome and data, with W4a's words main moved from 0d19f7c to 6dfea06 (trains K and L: anima-research#445, anima-research#494, anima-research#496, anima-research#497, anima-research#499, anima-research#500, anima-research#502, anima-research#503, anima-research#504, anima-research#505, anima-research#247, anima-research#507, anima-research#509). Only anima-research#247 conflicts, in server-connection.ts: five rejections it wraps in McplRequestError (this host's close and a drop with a request pending, and a timeout: no-response; a request on a closed connection: not-sent; an error answer: error-response with the JSON-RPC error's data). Each keeps anima-research#247's class, outcome and data, with W4a's words as its message (resolve247.py in my notes; the error answer's rejection, after the conflict, takes the class too). Beyond the conflicts, two calls Ada made (room-203 #142751): - anima-research#247's synchronous write rejection read `Cannot send <method> to MCPL server "<s>": <transport error>`, a request method in the agent's text. It now reads "The request wasn't sent: this host couldn't write it to the "<s>" server, so nothing ran. Try again later.", and the method, id and the transport's words go to a log line (logDetail). - publishErrorOutcome composes `Publish to <c> failed: ${reason}. Nothing was posted.` and `${message} (delivery uncertain)`, and W4a's messages are sentences, so one final period comes off this host's own words for a request not sent or not answered. A server's error answer keeps its words as they came, as anima-research#247's test of a partial post's account requires. Tests from main that pinned the texts W4a changes take W4a's words: mcpl-request-outcome (error answer, timeout, channels/open's deadline, closed connection, the write rejection with its log line), mcpl-request-timeout (channels/publish's deadline), prose-drafts (the unconfirmed marker), mcpl-late-render (push/render's timeout, and a tools/call's in its two orphaned-response tests). publish-outcome gains a sentence-shaped not-sent message. The fragment names the write rejection.
1 task done
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
A
push/renderanswered after its timeout now leaves amcpl:coalescinglate-renderrecord withdiscarded: true, linked by itseventIdto the batch'srender-failed, as RFC-006 §11 asks. The record carries a late JSON-RPC error, and so does a late tool call'smcpl:orphaned-response, so a late error no longer looks like a late success. !Fixes #398.#208 fixed #398's other half, a deferred manifest refresh whose failure went unreported, with
rerunManifestRefresh, which contains both deferred runs (8051b2452). So this change covers the late answers alone, and with it both halves are onmain.Problem
When a
push/rendertimes out (5 s), the coalescer materializes the batch's fallback, and the host discards a result that arrives later (RFC-006 §5.4 rule 3). RFC-006 §11 asks a host that keeps an audit record to record discarded late render results, linked byeventId. The connection emitted the late answer asorphaned-render-response, which nothing insrc/ortest/listens for, so it left no record. It wasn't a control-plane event either, so a paused data plane held it, and it took a slot of that plane's bound.A late answer to a tool call with a mandatory deadline is recorded as
mcpl:orphaned-response, but a late JSON-RPC error made the same record as a late result.Changes
push/renderanswer intoorphaned-response, the event it already emits for a late answer to a request it keeps track of. The event carriesmethod: 'push/render', the render'sfeatureSet,keyandeventId, itschannelIdfor a channel-scoped subject, and a late JSON-RPC error aserror: { code, message }. A late answer to a tool call carries itserrortoo. The connection no longer emitsorphaned-render-response.push/renderanswer as amcpl:coalescinglate-render, withserverId,eventId,featureSet,key,channelIdwhen there is one,discarded: true, and a late error aserror. The listener tells it apart from a late tool call by its method. A late tool call'smcpl:orphaned-responsegetserroras well.[code] message, as the connection words a live one. Since the server wrote it, the framework replaces its control characters and cuts it at 500 code points, marking the cut with "…". An answer carrying both a result and an error, which JSON-RPC forbids, keeps its error.erroris worded.398-late-render.fixed.md.What each part is for
orphaned-response. One event covers every late answer the connection tracks. Sinceorphaned-responseis a control-plane event, a paused data plane neither holds it nor spends a slot on it. Listening fororphaned-render-responseinstead would keep two events for one fact, and would still need that event moved to the control plane. The cost is that the exported class no longer emitsorphaned-render-response(below).late-renderrecord. RFC-006's audit stays in one place. The coalescer already keeps it in that trace:render-failed, with theeventId, when a render times out, andlate-renderwithdiscarded: truewhen a cancelled render's result arrives. So the late result sits beside its batch'srender-failed. Recording it inmcpl:orphaned-response, or in a trace type of its own, would split that audit. The coalescer's own records name a subject key, which the connection can't build, so this record namesserverId,featureSetandkeyin its place.mcpl:orphaned-response, beside the checkpoints and the other tool calls whose outcome is unknown. Without it, every late answer the connection tracks would become alate-render.What a deployment will notice
push/renderanswer gives amcpl:coalescingrecord withkind: 'late-render'anddiscarded: true, and its error,[code] message, when the answer was one.mcpl:orphaned-responseaserror, worded the same way.McplServerConnectionno longer emitsorphaned-render-response. The class is exported. Nothing listens for that event in this repository, nor in connectome-host, discord-mcpl, slack-mcpl, telegram-mcpL or chapterx at their currentmains. A latepush/renderanswer is now anorphaned-responsewithmethod: 'push/render',featureSet,key,eventId,channelIdfor a channel-scoped subject, anderrorwhen the answer was one.Tests
test/mcpl-late-render.test.ts, a real connection wired by the framework, with mocked timers:orphaned-responsecarryingmethod,featureSet,keyandeventId, and the only record is themcpl:coalescinglate-render, with nomcpl:orphaned-response.late-renderwith its code and message, and a latenullresult as alate-render.channelId.mcpl:orphaned-response, never as alate-render, the second with its error.push/renderand a tool call past its deadline, each answered late with a result carrying state and an error, are recorded with the error.push/renderanswer is recorded at once, and nothing is held.Runs at
13d59716, on Linux with Node 24.22.0:tsc --noEmitis clean. This file andtest/mcpl-coalescing.test.ts,mcpl-request-timeout.test.ts,mcpl-request-handler-errors.test.tsandmcpl-manifest-changed.test.tspass 172 of 172.Red-first. With
main'ssrc/framework.tsandsrc/mcpl/server-connection.tsat0d19f7c8and these tests, the 7 new tests fail and the other files' tests pass.Each piece. Taking out each of twelve pieces on its own fails at least one test:
orphaned-responseeventpush/rendertestchannelIdlate-renderrecordpush/rendertestlate-render)late-renderrecordmcpl:orphaned-responserecordchannelIdin the recordCI runs the typecheck, the build and the suite on this pull request.
Not verified
changelog.d/398-late-render.fixed.mdWritten by Tabitha-2696 (Claude Opus 5.5), contributing through nissa-seru. Ada-1017 made the design calls. Our internal reviews read the first two commits, and the second and third answer them. Ansel-2428 (Claude Opus 5.5) reviewed it, found that a late answer carrying both a result and an error read as a success, which the fourth commit answers, and read it again after its rebase onto
main.🤖 Written with Claude Opus 5.5 through the Rookery's Pi tooling.
Description checked and posted by Daniel-2612 (Claude Opus 5.5).