Repository navigation
test(mcpl): vector 34c bounds its store syncs, not its time - #431
Merged
nissa-seru merged 2 commits intoOct 18, 2026
Merged
Conversation
Vector 34c sends 300 coalesced channel messages one at a time and failed when they took 30 s or more. Each acceptance waits for its durable commit, a store sync, so the time follows the disk and the host's load more than the code. In six runs on a Linux host at load averages of 57 to 78, three of them beside six busy node loops, main's version took from 10.8 s to 34.1 s and failed once. With each store sync slowed by 120 ms, it failed both runs on time alone, at 44.2 s and 46.7 s, since 300 syncs at 120 ms take 36 s by themselves. The bound guarded the cost anima-research#196's review measured, 85 ms a message, from a journal that synced the store for each of three operations per message. The test now counts store.sync() calls during the 300 sends and requires at most 300, so a commit shared between acceptances would still pass. It turns the framework's periodic store sync off (syncIntervalMs: 0), which the fixture never starts anyway, so every sync it counts is an acceptance's. It reports the time beside the count, and its functional checks are unchanged. This version passes with each store sync slowed by 120 ms and beside six busy loops, making 300 store syncs in every run. With each acceptance committing twice, it fails with 600 store syncs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t holds it From an internal review of the commit before this one: - The count had a ceiling and no floor, so a count that stopped seeing the commits, or acceptances that stopped committing, read 0 and passed. It must now be at least one as well as at most one a message, and the ceiling comes from the loop's number of sends. - The comment credited syncIntervalMs: 0 with making every counted sync an acceptance's. start() arms the periodic sync, and this fixture never calls it; the option keeps that sync off if one ever does. The comment now says so, and that the count takes every store sync during the sends. - The comment said the time is reported, not asserted. Each send still has the fixture's reply deadline, its guard against a hang, and the comment says that too. Run with each acceptance's commit removed, the test now fails at the floor: "300 coalesced messages made no store sync: the acceptances didn't commit, or the count can't see their commits". With each acceptance committing twice, it fails at the ceiling with 600 store syncs, and as it is it passes with 300. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Low impact] This test-only change appears safe to merge. SummaryVector 34c replaces its 30-second limit with a count of
Reviews (1) · Last reviewed commit: "test(mcpl): vector 34c's count gets a fl..." · Reviewed by Greptile |
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 18, 2026
main now carries anima-research#427 and anima-research#431. One conflict, in test/mcpl-coalescing.test.ts's fixture import: anima-research#427 imports REPLY_DEADLINE_MS and this branch imports settles(), so the import takes both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 18, 2026
…search#429's text main moved through train G: anima-research#424, anima-research#430, anima-research#369, anima-research#427, anima-research#429 and anima-research#431. anima-research#429 rewords agent-framework's channel texts to say only what holds on every MCPL server (anima-research#406), and this change lands second, so it carries anima-research#429's text, as both pull requests' descriptions say. Ten hunks conflicted, all with anima-research#429, and each is resolved in anima-research#429's words under this change's rule (speechOpens decides what a reply does): - The closed-channel invitation: anima-research#429's opening ("a channel you haven't opened. Messages there that address you, such as an @-mention, reach you whether or not you open it."), its option 2 ("Open the channel") and its `#` handling (hashLabel). Option 1 keeps this change's two cases. Where a reply opens the channel it says so in anima-research#429's words, "which opens the channel", then how to reply without opening it (decline first). Where the agent closed or declined the channel itself, it says "and the channel stays closed", with no follow-ups note: what else reaches the agent there is the server's to say (anima-research#406; anima-research/discord-mcpl#97 says it for discord-mcpl). The no-target hint keeps this change's send-tool advice, in the same words. - channel_close's description: anima-research#429's two sentences, then this change's ("You can still speak there. It stays closed until you open it with channel_open: neither speaking there nor subscription policy reopens it."). - The "[channels] Now open" notice: anima-research#429's middle sentence, then this change's ending. - Doc comments: both sides' points, with option 1 described as speechOpens decides it. The send path keeps this change's comment, which replaced the INVARIANT comment anima-research#429 reworded. - trunk-channel-routing.test.ts: both sides' tests. anima-research#429's invitation-label test's stub gains speechOpens, which this change's invitation calls. Outside the hunks, the edits the same rule needs: this change's test of a reply into a channel the agent closed now expects "and the channel stays closed" (and is renamed), send-into-closed-channel's notice assertion takes anima-research#429's middle sentence, and two comments say "open" where they said "join". Train G's other merges touched framework.ts and the registry in other functions; git merged them on their own (anima-research#369's text-block join in dispatchMcplToolCall sits after this change's script note, and anima-research#429's decline error message beside this change's refusal).
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.
TL;DR: vector 34c now checks how many store syncs its 300 coalesced messages make, at least one and at most one a message, instead of how long they take. The time follows the disk and the host's load. On a loaded Linux host the test failed at 34.1 s with nothing wrong in the code, and with each store sync slowed by 120 ms it failed in both runs, since 300 syncs at 120 ms take 36 s, past the 30 s bound. The count is the cost #196's review was about, and a regression to more than one sync a message on the path this test drives fails it on any machine. Test-only; no changelog fragment.
We won't merge this before 06:00 UTC on October 18, or before our pre-merge checks pass on its merge with the then-current
main, and a line here asking us to wait holds it.Problem
"#196 finding 2 / vector 34c: a busy channel never hits a subject cap" sends 300 coalesced
channels/incomingmessages one at a time, then asserts that they took under 30 s. Each acceptance awaits a durable commit, onestore.sync(), so the time is mostly fsync time and CPU contention. On Linux with Node 24.22.0, at load averages of 57 to 78,main's version took 10.8 s to 34.1 s over six runs, three of them beside sixnode -e "for(;;){}"loops, and failed once, at 34.1 s. With everystore.sync()slowed by 120 ms, through a preload that busy-waits inside it (below), it failed both times, at 44.2 s and 46.7 s.The bound came from #196's review, which measured 85 ms a message on this path because its journal synced the store for each of three operations per message. That cost is a count of syncs per message, and the wall-clock bound couldn't tell it apart from a slow disk.
Changes
test/mcpl-coalescing.test.ts, vector 34c only, in two commits:10314c5dreplaces the time bound with the count, andf446e8d0answers an internal review of it.store.sync()call during the 300 sends, and asserts at least one and at most 300. The ceiling lets commits shared between acceptances pass. The floor fails a run in which all the acceptances stop committing, or the count stops seeing their commits. In every run of the test as committed, below, the count was exactly 300.syncIntervalMs: 0.start()arms the periodic store sync, and this fixture never calls it, so the option changes nothing today. It keeps that sync off if a later fixture callsstart().What each part is for
The test targets the repeated store-sync work reported in #196's review. It replaces the elapsed-time bound with bounds on the number of
store.sync()calls.host.committwice per acceptance reports 600 sync calls and fails. Unlike an exact count of 300, the ceiling permits fewer calls so a later implementation sharing commits between acceptances can satisfy this count check.sync()or the counter stopped seeing those calls.syncIntervalMs: 0. It changes nothing in these runs because the fixture never callsstart(). It explicitly selects no periodic store-sync timer if a later fixture does callstart(), keeping that source of calls out of the intended measurement.Cut: the 30 s assertion over the 300 sends. The new count bounds neither elapsed time nor work other than
store.sync()calls.Tests
At
f446e8d0, on Linux with Node 24.22.0:await this.host.commit?.()inPushCoalescer.acknowledge()), it fails at the floor: "300 coalesced messages made no store sync: the acceptances didn't commit, or the count can't see their commits".At
10314c5d, whose test has the same count and ceiling without the floor:store.sync()slowed by 120 ms, 2 runs: 60.1 s and 61.1 s, 300 store syncs, both passing.main's version failed both of its runs on time, at 44.2 s and 46.7 s at load averages of 40 to 43.10314c5d, withmain(14e25dc0) run the same way beside it, on Linux at load averages between about 15 and 45. Typecheck and build are clean. Each file on its own, without--test-force-exit, passes 2378 of 2378 tests on Node 24.22.0 and on Node 20.20.2, the same count asmain.npm test, as CI runs it on Node 24.22.0, passed 2352 of 2352, and the same forced run with TAP passed 2334 of 2334 on Node 24 and 2378 of 2378 on Node 20. Forced runs report fewer tests because--test-force-exitcan end a file before all its tests report (npm test's --test-force-exit can end a test file before its tests report, so a short run still passes #294). Our run of CI's unforced step passed 105 of 105 on both Nodes. Inmain's run of the same commands, vector 34c failed on time on Node 20, beside other tests that fail under load.The slowed syncs come from Nigel-2654's preload, which wraps each framework's
store.sync()and busy-waitsF61_SYNC_DELAY_MSbefore calling it, since a slow fsync holds the event loop the same way (F61_SYNC_DELAY_MS=120 node --import ./f61-sync-delay.mjs --test-name-pattern='vector 34c' dist/test/mcpl-coalescing.test.js):f61-sync-delay.mjs
Not verified
mainhas moved to24826254since the full check, by three merges (fix(mcpl): a failed handshake keeps the last of the server's stderr #399, fix(mcpl): SIGTERM a stdio child whose stdin or stdout fails #384 and build(release): install the packed release on its own before publishing, and on pull requests #349). This branch merges onto it without conflicts, alone and with fix(mcpl): malformed MCPL blocks and failed Discord attachment fetches are shown, not dropped #369 and fix(mcpl): speaking no longer reopens a channel the agent closed #330, the other open PRs that change the coalescing code. On its merge with24826254, everymcpl-*test file passed on its own on Node 24.22.0 and on Node 20.20.2, 30 files and 428 tests on each. I didn't run the full suite on that merge, or any suite with fix(mcpl): malformed MCPL blocks and failed Discord attachment fetches are shown, not dropped #369 and fix(mcpl): speaking no longer reopens a channel the agent closed #330 merged in.Built by Meg-2646 (Claude Opus 5.5), with design calls from Ada-1017. Nigel-2654 wrote the preload that slows each sync, and read its code and this description.
Rent-section text checked and edited by Fiona-2757 (GPT-6).