Skip to content

fix(framework): refuse start() and addModule() while a stop() is still running - #532

Merged
nissa-seru merged 1 commit into
anima-research:mainfrom
nissa-seru:fix/refuse-start-during-stop
Oct 18, 2026
Merged

nissa-seru merged 1 commit into
anima-research:mainfrom
nissa-seru:fix/refuse-start-during-stop

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

start() and addModule() made while a stop() is still running are now refused. start() throws and addModule() rejects, each with <call> refused: the framework is stopping. This is the in-flight half of #315, as proposed in this comment there. It leaves #315's open question, whether a start() after a settled stop() should begin a new lifecycle, to be answered either way.

Problem

stop() sets running to false before its first await, so a start() made while it runs passes start()'s running check. resetCompletedStop() clears a stop only once it has completed, so start() then sets running, reopens provider admission and starts a second event loop under the stop still running. What follows depends on how far the stop has got. On main at c0155d3 (Linux, Node 24.22.0), each case on its own framework over a store it owns:

  • The stop hasn't yet seen its loop end. It waits on the new loop instead.

    • With no budget, the default, the stop was still pending 3 s after start(), with running true. It resolved only once running was set false by hand.
    • With stop(1000), it rejected after 1,007 ms with Shutdown timed out after 1000 ms; pending: event loop, and running stayed true.
  • The stop is past that wait, here while a module's stop() was still running.

    • The stop resolved, but the new loop ran on, over the store the stop had closed.
    • A second stop() returned the same settled promise, and the loop ran on.
    • A further start() changed nothing, since running was true.

    Neither stop() nor start() could end that lifecycle.

  • addModule() goes through the same resetCompletedStop().

    • Made right after stop(), the module was started and then stopped by the stop's stopAll().
    • Made while another module's stop() was still running, after stopAll() had taken its list, the module was started and never stopped. It stayed registered after the stop settled, over the closed store.

In connectome-host at eb6f008, switchSession (src/index.ts:1333) awaits stop() and creates a new framework rather than starting the stopped one. We found no call there that starts a framework while it stops, or adds a module to one.

Changes

  • resetCompletedStop(), which start() and addModule() both call before they begin any work, refuses while a stop is in flight, that is, while stopPromise is set and the stop hasn't completed. Its existing check comes first, so a stop that failed after closing an owned store is still refused as Cannot start a new lifecycle: owned store is closed. A stop that failed with its store open has already cleared stopPromise, as before.
  • The error names the call, in the form the operator entry points use once a stop has begun (quiesce refused: the framework is stopping).
  • start() passes resetCompletedStop() before its running check. stop() claims its attempt before it clears running, so a start() made in the stop's first synchronous steps, such as from the push coalescer's suspend(), is refused too. On a running framework with no stop, start() still does nothing.
  • The comments that described a start() made during a stop now say it's refused: three in stopOwned(), the one on guardSpillsClosedFor, one in src/gate/event-gate.ts, and a sentence in docs/tool-result-guard.md. The docs of start(), stop() and addModule() say so too.
  • stop()'s doc said a start() could be made "meanwhile" beside a loop that a timed-out stop() left in a call. It meant after that stop has failed, which is still allowed, and now says so.

What each part is for, and what we left out

  • The check in resetCompletedStop(). It's one condition and one throw in the method both entries already pass through before they begin a lifecycle's work, so a third entry added there would get it too. A stop() that captured, at entry, the lifecycle it stops would end the first case's wait. The second case shows what that leaves: the stop goes on to stop modules and close an owned store under the new lifecycle, and its settled promise answers every later stop(). A start() that waited for the stop would have to become async, and would then begin the restart start() after a completed stop() reuses an instance whose queue, coalescer and watchdog stay stopped #315 asks about.
  • Throwing rather than doing nothing. A caller whose start() silently did nothing would go on as if the framework were running. The operator entry points throw too.
  • The check before start()'s running check. Without it, a start() made before the stop cleared running returned silently. It's the order of two statements.
  • addModule(). It shares the method and the window. Without it, the orphaned module above remains.
  • The verb in the message. It names the call that was refused. It's a parameter of resetCompletedStop(), typed to the two callers.
  • The changed comments and docs. Each described an order this PR refuses, or could be read that way.
  • Left out: anything after a settled stop. start() and addModule() after a completed or failed stop() behave as before, which is start() after a completed stop() reuses an instance whose queue, coalescer and watchdog stay stopped #315's open question.

Tests

Overlapping pull requests

Out of scope

Our internal review found three things outside what this PR changes, which it leaves as they are:

  • After a stop() that failed with its store open, start() goes ahead, as before, and by our reading so does addModule(). By our reading, leftRunning, which a later stop() waits for, holds a stream or an event loop that outlasted the failed stop's wait, but not a module's stop(). That's the settled-stop side of start() after a completed stop() reuses an instance whose queue, coalescer and watchdog stay stopped #315.
  • By our reading, runUntilIdle() drives the event loop with no check of the stop's state.
  • By our reading, runEphemeralToCompletion() admits a run by providerAdmissionClosed, which stop() sets only after its first synchronous steps.

Not verified

  • That no consumer other than connectome-host starts or adds a module to a framework whose stop() hasn't settled. Such a call now gets an error where it got the behaviour above.

  • Changelog fragment added: changelog.d/start-during-stop.fixed.md

Written by Abigail-2459 (Claude Opus 5.5), contributing through nissa-seru. Ada-1017 (Claude Opus 5.5) agreed the design. Duncan-2410 (Claude Opus 5.5) reviewed the change, and it had an internal review before publication.

Description checked and posted by Lottie-2661 (Claude Opus 5.5).

🤖 Written using Anthropic's Claude Opus 5.5 through the Rookery's Pi tooling, and posted from the shared nissa-seru account.

…l running

stop() claims its attempt and sets running to false before its first
await, and resetCompletedStop() cleared a stop only once it had
completed. So a start() made while the stop ran passed start()'s
running check, reopened provider admission and started a second event
loop under the stop. On main at c0155d3, with a store the framework
owns:
- a stop() with no budget waited on the new loop and was still pending
  3 s later; with stop(1000) it rejected after 1,007 ms, "pending: event
  loop", and running stayed true;
- a start() made while a module's stop() ran, past the stop's wait on
  its loop: the stop resolved and closed the store, the new loop ran on
  over it, every later stop() returned the same settled promise, and a
  further start() changed nothing, so nothing could end that loop;
- an addModule() made then, after stopAll() had taken its list: the
  module was started, never stopped, and stayed registered.

resetCompletedStop(), which start() and addModule() both pass before any
work, now refuses while a stop is in flight (stopPromise set, the stop
not completed), after its owned-store check: "start() refused: the
framework is stopping", and the same for addModule(), which rejects.
start() passes it before its running check, since stop() runs steps
that can emit traces before it clears running: a start() made there,
from a trace listener, is refused too. emitTrace catches a listener's
throw, so that can't fail the stop. On a running framework with no
stop, start() still does nothing. start() and addModule() after a
settled stop() behave as before, which is anima-research#315's open question.

Text that described a start() made during a stop now says it's
refused: comments in stopOwned(), on guardSpillsClosedFor and in
src/gate/event-gate.ts, and docs/tool-result-guard.md. stop()'s doc
said a start() could be made "meanwhile" beside a loop a timed-out
stop() left in a call; it means once that stop() has failed, and now
says so.

This is the in-flight half of anima-research#315, as proposed in a comment there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 18, 2026

Copy link
Copy Markdown

Required label not found on this PR.

@nissa-seru
nissa-seru merged commit 124edb4 into anima-research:main Oct 18, 2026
6 checks passed
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 18, 2026
main gained anima-research#526 (modules read, edit and remove the writes the framework
holds for them), anima-research#531 (fetch_reference's failures say what happened and
what calling again can do, and the refs save hook logs why a payload
wasn't stored) and anima-research#532 (start() and addModule() are refused while a
stop() is still running). Where they meet the include:

- reference-fetcher.ts conflicted where both sides changed what the fetch
  answers when the save hook returns null. It takes main's failure and its
  log, and its comment says the null covers no workspace, no writable mount
  without an include, and a failed write.
- The refs save hook's log for a missing mount said "no workspace mount is
  writable". firstWritableMountName also answers null where the only
  writable mount has an include, so the log says "no writable workspace
  mount without an include", and main's test of the hook's log lines says
  so too.
- The rest merged on its own: docs/tool-result-guard.md's two sides change
  different paragraphs, and anima-research#526's and anima-research#532's framework.ts changes touch
  no mount or workspace path.

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
main moved 71 merges since e670363. Beyond the conflicts, this is where
they meet this branch.

One rule for server ids, this branch's, across both engines:
- A server id is held while a connect or a disconnect of it is in
  flight, or while a restart moves its server to it (mcpServerIdTaken,
  holdMcpServerId). anima-research#469's refuseMcplConnect and anima-research#506's mcplConnecting
  set went, so two rules don't guard the same ids.
- A connect or a restart into an id whose connect is in flight, and
  whose server isn't registered yet, is refused in anima-research#506's words: 'MCPL
  server "<id>" has a connect in flight; try again once it settles'.
- A disconnect made while a connect is in flight ends that connect and
  removes what it registered, where anima-research#506 refused the disconnect.
- anima-research#469's prefixesMeet joins mcplPrefixCollision. It is checked at runtime
  connects and restarts in both engines, and a prefix that meets one a
  restart holds is refused too. create() already checks every configured
  server, a modern one included.
- A tool call while its server connects reads anima-research#506's "connecting" text
  when a connect holds the id or a restart claims it
  (mcplServerAbsentText).

Request errors (anima-research#247, in 0.22.3, and anima-research#506):
- McplRequestError has no code, as 0.22.3 has it. The modern engine's two
  error-response failures pass their data alone, and the code stays in
  the message. The class this branch's first commit added is gone, so
  there's one.
- The legacy engine's request errors take anima-research#506's words. The not-sent
  refusal after a stdio pipe failure says this host can no longer write
  to the server, and the method and the pipe's error go to the log.
- protocolVersionRejection reads the server's message with describeCause,
  bounded, as the initialize error does. A message with no primitive form
  threw out of the handshake's line listener.

Tool results:
- An MCP tool's error result with no words, or only whitespace, reads as
  anima-research#506's text in both readings and on the direct path
  (mcplToolNoWordsText; normalizeStandardToolResult takes it as noWords).
- The direct path's failure is mcplToolFailure's text without the
  server's other blocks, as 0.22.3 gives it, with structured kept by
  presence. A script's structured failure keeps its content.
- anima-research#510's in-process registration: autoFetchReferences takes a mode,
  'register', 'model' or 'script', in place of a positional boolean whose
  meaning differed on each side. executeMcplToolCall registers an MCPL
  peer's references only. The model's dispatch fetches eagerly, and a
  script's reads fetch everything.
- A script's call of a tool that isn't an MCPL server's (a module's)
  materializes the references registered against their servers again, as
  main does. An MCPL server's were fetched at dispatch.
- referenceBlockForScript gives a block the classifier rejects anima-research#506's
  notice (classifyBlock's shown), as the model's lanes do.

Inbound handlers:
- Main's containedInboundHandler replaces this branch's answerOnce and
  handlerFailureText. What a failure records, in its trace and its log
  line, goes through the server's redactor, which wireMcplEvents and a
  queued manifest run (rerunManifestRefresh) pass. It defaults to the text
  as it is.

Transport (anima-research#494, anima-research#499, anima-research#485):
- StdioTransport takes main's constructor, (child, rl, serverId), with
  both pipeFailure and stderrEnded. The handshake's wait for stderr's end
  comes through unchanged.
- The boolean settings check is main's assertBooleanSetting, keeping this
  branch's dialsNetwork skip for inheritEnv.

Elsewhere:
- stop() is main's step()-by-step teardown, with endConnectsInFlight as
  its first step.
- addModule checks the tool prefix, then resetCompletedStop('addModule()')
  (anima-research#532).
- thrownText is main's boundedErrorMessage, with this branch's redactor
  applied before the cut.
- Changelog: this branch's fragments say what changes from 0.22.3. The
  folded mcpl-request-outcome fragment is main's, and anima-research#506's
  connect-in-flight fragment describes the merged rule.
- Tests: main's new stub servers answer protocolVersion. Words and log
  lines follow the merged code. The test stand-ins use the merged
  constructor, and wireMcplEvents gets its config. A close that now waits
  for the child is awaited after its request's rejection is observed. Two
  manifest re-run tests count the configuration read at each run's start.
  anima-research#506's test of refuseMcplConnect now tests the merged rule.

Receipts, on Linux at this commit's tree: the typecheck and the build are
clean, and every test file, each in a process of its own, passes: 232
files and 4,339 tests (4,322 passing, 17 skipped, none failing) on Node
24.22.0, and the same on Node 20.20.2, with CI's stop pair passing on
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
main at 5227741 brings anima-research#526, anima-research#531, anima-research#532, anima-research#535, anima-research#533 and anima-research#262. anima-research#262
landed before this branch, so this branch carries how the two meet.

The one conflicting file is src/framework.ts, in two blocks of a script's
tool-result conversion:
- anima-research#262 gives a script an MCP structured result whole
  (scriptStructuredResult), and names the 5,000,000-character cap on what
  a script receives AgentFramework.SCRIPT_RESULT_MAX_CHARS. Both blocks
  take anima-research#262's side.
- This branch named the same cap SCRIPT_TOOL_RESULT_MAX_CHARS and set it
  as a script's call's resultMaxChars. That constant is gone, and
  resultMaxCharsFor gives a script's call anima-research#262's
  AgentFramework.SCRIPT_RESULT_MAX_CHARS, so one name carries the cap a
  script receives and the field that tells a module about it.

anima-research#535 and anima-research#533 change the workspace module without a conflict. Since
anima-research#535, parseFilePath also refuses a path that leads into a nested mount
through a link, and edit and edit_many get that refusal through
editablePath, as every file tool does. anima-research#535's test of a directory replaced
by a link into the nested mount covers edit, write and delete, and now
covers edit_many too, with the refusal's exact text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tel-0s added a commit that referenced this pull request Oct 18, 2026
…eld writes for modules and the modern MCP client meet the registry

What main brings since 3af13bf (97 merges): the 0.22.3 release; held prose
drafts and the `drafts` tool (#247); a module reads, edits and removes its
held writes (#526); the deferred-writes follow-up (#461: a targetless write
on a host with no primary, boot renumbering, sharded injection); the modern
MCP client (#262) and the inbound source envelope; a stop that reaches a
turn with no live stream (#529), every cleanup (#517), an MCP connect in
flight, and a start during stop() (#532); allowedTools enforced before every
dispatch (#220, breaking for hosts); repeated agent names refused at
create() (#523, breaking); the fork-name collision fix (#513); the
tune-out coordinator's timer-failure reporting and new refusal words
(#506); request-only rows (silent ticks and [Continue] replies) rendered
before their response; `callerTurnId` on tool calls.

Conflicts (36 hunks, 7 files), and what was decided:

- src/framework.ts (23): imports and the registry fields are unions
  (main's `releasesAfterResume` and `conversationSpawns` kept; the
  shared-slot set and the home-channel map stay answered by the registry,
  the one new use in the drafts resend reads `registry.homeChannel`).
  `isAddressedMessage` stays tag-only; main's call sites pass tags alone.
  Turn routing is main's (`turnId`, drafts, `keptPrivate`, hybridHold as
  main types it) plus the branch's `addressedElsewhere`. The subconscious
  setup keeps the reader-forks branch; the coordinator hooks keep
  `announce`. `getAllTools` offers main's `drafts` beside `tune_out`
  and `focus`; an agent's tool block is its surface owner's, with the
  reader tools, filtered by main's `utils` rule. The deferred-writes
  restore is main's (refuses to start on a queue it can't read; `heldAt`;
  numbered 1..N) with the branch's re-wake for a restored tune-out release.
  The channel-incoming handler stamps main's inbound source envelope, then
  presents the author and admits. The ephemeral run keeps both watchdogs
  (the branch's deadline, main's stalled-batch abandon). Dispatch sets
  `callerTurnId` then applies the branch's deny-at-dispatch.
  `endEphemeralRunRefused` is gone: main's #532 refuses the start itself.
  `tune_out`/`focus` still dispatch through the queue (main's change there
  was isError).
- src/tune-out/coordinator.ts (6): the branch's `readOptions`, all-except
  types and `renderHeldLine` beside main's `EXPIRY_RETRY_MS` and
  `thrownReason`; `enter` still allows no reader (silent hold) and takes
  main's words for a bound or already-tuned-out channel; the coalesced wake
  timer wraps the branch's `fireWake` in main's `timerFailed`;
  `cancel_tuneout` keeps the server-qualified lookup and the all-except
  path, with main's `notTunedOut` words; `speak_in_channel` has main's
  words and the branch's caller fallback.
- src/agent.ts (2): main's `requestSettings()` plus
  `assistantParticipant`; `presentOwnTurns` before main's request-only
  rows, which render under `presentAs`.
- src/mcpl/channel-registry.ts (2): main's acceptance step keeps only its
  transport confirmation; the locus is admission's. `onRouteFailure` is
  main's type (with `outcome`).
- src/index.ts, src/types/trace.ts, test/tune-out-timers.test.ts: unions.

Outside the hunks, where main's tests met the branch's rules:
- main's #506 rule (a refusal is an error result) is applied to `tune_out`
  and `focus`, whose refusals were `isError: false`; an unknown channel is
  refused in main's words (`unknown channel: <id>`).
- a tune-out with no reader is a silent hold only where the host offers
  `tune_out` without one: the coordinator gains `holdWithoutReader`, the
  framework passes `tuneOutOffered`, and main's tune-out-messages test
  holds as written; the branch's §3.3 row opts its host in.
- main's tests that reached `conversationAgentHomes` use the registry
  (`bindAsConversationFork`, a registry stub); the ended-agents log is
  registered through `ensureStateRegistered`, as main's registration test
  requires; main's content-parity test counts the placeholder to the end of
  its text field, since the channel lane stores it under its author; main's
  route-failure rows that route by the registry's default locus run under
  `speech.locus: 'latest-inbound'`, the rule they assume; the branch's
  present-while-acting routing literal gains main's `turnId`, `drafts` and
  `keptPrivate`.
- main's new `@modelcontextprotocol` dependencies installed (rig relinked
  after), and the merge fragment extended.

Composed rig (the branch chronicle at 03b7bc1 and context-manager at
bce0c8b linked, one membrane copy), Node 26.5.0: `npm test` 4707 tests,
4679 pass, 0 fail, 28 skipped. The skips are main's: 22 workspace-watcher
rows (chokidar on macOS), two case-folding disk rows, the Linux setuid row,
the Python eager-task-factory row, and two deferred-window-only rows that
context-manager 0.15 makes moot. Typecheck clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/agent-framework that referenced this pull request Oct 19, 2026
…anima-research#506's words and F240

main moved 331 commits on from 9e3e407, among them anima-research#262 (the modern MCP
client and the legacy engine's protocol boundary), anima-research#506 (the agent's words
for failed requests), anima-research#532, anima-research#537, anima-research#210 and anima-research#542 (F240: a redaction that
can't read its configuration withholds the text). What anima-research#250 removes stays
removed, and what main added keeps working beside it.

Conflicts:
- src/framework.ts, imports: main's new imports, with anima-research#250's
  McplRequestError beside McplProtocolVersionError and
  McplUnreapedLaunchError.
- src/framework.ts, the barrier: main changed failMcplDataPlaneGate's
  recycle (a connection it couldn't close takes its tools off the list).
  anima-research#250 removes the gate and its recycle, so that change has nowhere to go;
  the close for good it rests on (closeMcplServerForGood) is main's and
  stays, and a runtime connect whose policy registration throws still goes
  through it.
- src/framework.ts, the connect: main split connectMcplServerInternal
  into the id hold and connectHeldMcplServer, with a modern family, a stop
  check and an abort. That structure is main's. anima-research#250's parts go into it:
  the parameter is `staged`, the ledger is read first only by a legacy
  connect that derives the protective default (anima-research#542's sentence about a
  ledger that breaks after that read stays), listeners are wired before
  any plane opens, a runtime connect whose policy registration throws
  closes for good, and a staged startup connect returns its connection
  while a runtime one opens its planes and starts delivery in the
  background before returning it.
- src/framework.ts, the restart: main's restart (its in-flight, claim, id
  and prefix checks, and its claims) with anima-research#250's rule for the ledger read:
  only a legacy server that derives the default reads it before
  disconnecting.
- src/mcpl/server-connection.ts: main's file, which moved the reconnect
  into reconnectOnce, with anima-research#250's five rewrites of comments that named the
  awareness barrier.
- test/failed-create-releases: main's two-engine test, with anima-research#250's legacy
  server (`chat`, the reregistering fixture) and no ledger options.
- test/mcpl-reconnect-reregistration, test/stop-with-host-operations:
  wireMcplEvents takes the config now.
- test/stop-ends-awareness-drains: anima-research#250's assertions, with anima-research#506's words
  for a request the close ended.
- test/placed-reaction-baseline: anima-research#542's two tests go in ahead of anima-research#250's
  restart tests. The reconnect one breaks the ledger with breakLedger, since
  version 3 reads the file only when it has no document in memory.
- test/mcpl-awareness-barrier.test.ts and its fixture stay deleted. Of
  main's changes to them, the deadline wording and the generation tests'
  signatures go with the file; the relist after a failed close is anima-research#459's
  test, which anima-research#250 keeps in mcpl-runtime-connect-failure.test.ts, so that
  assertion moves there.

Outside the conflicts:
- test/fixtures/reregistering-mcpl-server.mjs, anima-research#250's own fixture, answers
  initialize with protocolVersion 2024-11-05: main's legacy engine refuses
  an initialize answer without it (anima-research#262), and main gave its own fixtures
  the field.
- test/discord-awareness-delivery's inbound test passes a config to
  wireMcplEvents, which records through that server's redactor now.
- main's modern-MCP connect and restart read no ledger and install no
  gate, so they meet nothing anima-research#250 removed. Two of main's
  comments, and the modern client's pending fragment, named an awareness
  gate as MCPL surface a modern server lacks; with no gate left, they no
  longer name it.
- main's modern test in placed-reaction-baseline breaks the ledger with
  breakLedger too, for the same reason.
- anima-research#506's words reach anima-research#250's own assertions: a hung reaction's recorded
  error says the server didn't answer within its deadline, and a request
  the connection's loss ended says the connection closed before it
  answered. anima-research#250's test of a request the host's close ends goes: main's
  "closing the connection while a request awaits its answer" pins the same
  outcome, and stop-ends-awareness-drains pins its words.
- test/mcp-server-ownership: main's two tests of the gate's recycle (an
  accounting failure recycling a server whose child can't be reaped, with
  and without reconnect) call installMcplDataPlaneGate and
  failMcplDataPlaneGate, which anima-research#250 removes, so they go with the gate. The
  close for good they lead to is still pinned for a runtime connect's
  failure, in mcpl-runtime-connect-failure.

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 19, 2026
…arch#506's words and anima-research#542

main moved 331 commits on from 9e3e407, among them anima-research#262 (the modern MCP
client and the legacy engine's protocol boundary), anima-research#506 (the agent's words
for failed requests), anima-research#532, anima-research#537, anima-research#210 and anima-research#542 (a redaction that can't
read its configuration withholds the text). What anima-research#250 removes stays
removed, and what main added keeps working beside it.

Conflicts:
- src/framework.ts, imports: main's new imports, with anima-research#250's
  McplRequestError beside McplProtocolVersionError and
  McplUnreapedLaunchError.
- src/framework.ts, the barrier: main changed failMcplDataPlaneGate's
  recycle (a connection it couldn't close takes its tools off the list).
  anima-research#250 removes the gate and its recycle, so that change has nowhere to go;
  the close for good it rests on (closeMcplServerForGood) is main's and
  stays, and a runtime connect whose policy registration throws still goes
  through it.
- src/framework.ts, the connect: main split connectMcplServerInternal
  into the id hold and connectHeldMcplServer, with a modern family, a stop
  check and an abort. That structure is main's. anima-research#250's parts go into it:
  the parameter is `staged`, the ledger is read first only by a legacy
  connect that derives the protective default (anima-research#542's sentence about a
  ledger that breaks after that read stays), listeners are wired before
  any plane opens, a runtime connect whose policy registration throws
  closes for good, and a staged startup connect returns its connection
  while a runtime one opens its planes and starts delivery in the
  background before returning it.
- src/framework.ts, the restart: main's restart (its in-flight, claim, id
  and prefix checks, and its claims) with anima-research#250's rule for the ledger read:
  only a legacy server that derives the default reads it before
  disconnecting.
- src/mcpl/server-connection.ts: main's file, which moved the reconnect
  into reconnectOnce, with anima-research#250's five rewrites of comments that named the
  awareness barrier.
- test/failed-create-releases: main's two-engine test, with anima-research#250's legacy
  server (`chat`, the reregistering fixture) and no ledger options.
- test/mcpl-reconnect-reregistration, test/stop-with-host-operations:
  wireMcplEvents takes the config now.
- test/stop-ends-awareness-drains: anima-research#250's assertions, with anima-research#506's words
  for a request the close ended.
- test/placed-reaction-baseline: anima-research#542's two tests go in ahead of anima-research#250's
  restart tests. The reconnect one breaks the ledger with breakLedger, since
  version 3 reads the file only when it has no document in memory.
- test/mcpl-awareness-barrier.test.ts and its fixture stay deleted. Of
  main's changes to them, the deadline wording and the generation tests'
  signatures go with the file; the relist after a failed close is anima-research#459's
  test, which anima-research#250 keeps in mcpl-runtime-connect-failure.test.ts, so that
  assertion moves there.

Outside the conflicts:
- test/fixtures/reregistering-mcpl-server.mjs, anima-research#250's own fixture, answers
  initialize with protocolVersion 2024-11-05: main's legacy engine refuses
  an initialize answer without it (anima-research#262), and main gave its own fixtures
  the field.
- test/discord-awareness-delivery's inbound test passes a config to
  wireMcplEvents, which records through that server's redactor now.
- main's modern-MCP connect and restart read no ledger and install no
  gate, so they meet nothing anima-research#250 removed. Two of main's
  comments, and the modern client's pending fragment, named an awareness
  gate as MCPL surface a modern server lacks; with no gate left, they no
  longer name it.
- main's modern test in placed-reaction-baseline breaks the ledger with
  breakLedger too, for the same reason.
- anima-research#506's words reach anima-research#250's own assertions: a hung reaction's recorded
  error says the server didn't answer within its deadline, and a request
  the connection's loss ended says the connection closed before it
  answered. anima-research#250's test of a request the host's close ends goes: main's
  "closing the connection while a request awaits its answer" pins the same
  outcome, and stop-ends-awareness-drains pins its words.
- test/mcp-server-ownership: main's two tests of the gate's recycle (an
  accounting failure recycling a server whose child can't be reaped, with
  and without reconnect) call installMcplDataPlaneGate and
  failMcplDataPlaneGate, which anima-research#250 removes, so they go with the gate. The
  close for good they lead to is still pinned for a runtime connect's
  failure, in mcpl-runtime-connect-failure.

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 19, 2026
…arch#506's words and anima-research#542

main moved 331 commits on from 9e3e407, among them anima-research#262 (the modern MCP
client and the legacy engine's protocol boundary), anima-research#506 (the agent's words
for failed requests), anima-research#532, anima-research#537, anima-research#210 and anima-research#542 (a redaction that can't
read its configuration withholds the text). What anima-research#250 removes stays
removed, and what main added keeps working beside it.

Conflicts:
- src/framework.ts, imports: main's new imports, with anima-research#250's
  McplRequestError beside McplProtocolVersionError and
  McplUnreapedLaunchError.
- src/framework.ts, the barrier: main changed failMcplDataPlaneGate's
  recycle (a connection it couldn't close takes its tools off the list).
  anima-research#250 removes the gate and its recycle, so that change has nowhere to go;
  the close for good it rests on (closeMcplServerForGood) is main's and
  stays, and a runtime connect whose policy registration throws still goes
  through it.
- src/framework.ts, the connect: main split connectMcplServerInternal
  into the id hold and connectHeldMcplServer, with a modern family, a stop
  check and an abort. That structure is main's. anima-research#250's parts go into it:
  the parameter is `staged`, the ledger is read first only by a legacy
  connect that derives the protective default (anima-research#542's sentence about a
  ledger that breaks after that read stays), listeners are wired before
  any plane opens, a runtime connect whose policy registration throws
  closes for good, and a staged startup connect returns its connection
  while a runtime one opens its planes and starts delivery in the
  background before returning it.
- src/framework.ts, the restart: main's restart (its in-flight, claim, id
  and prefix checks, and its claims) with anima-research#250's rule for the ledger read:
  only a legacy server that derives the default reads it before
  disconnecting.
- src/mcpl/server-connection.ts: main's file, which moved the reconnect
  into reconnectOnce, with anima-research#250's five rewrites of comments that named the
  awareness barrier.
- test/failed-create-releases: main's two-engine test, with anima-research#250's legacy
  server (`chat`, the reregistering fixture) and no ledger options.
- test/mcpl-reconnect-reregistration, test/stop-with-host-operations:
  wireMcplEvents takes the config now.
- test/stop-ends-awareness-drains: anima-research#250's assertions, with anima-research#506's words
  for a request the close ended.
- test/placed-reaction-baseline: anima-research#542's two tests go in ahead of anima-research#250's
  restart tests. The reconnect one breaks the ledger with breakLedger, since
  version 3 reads the file only when it has no document in memory.
- test/mcpl-awareness-barrier.test.ts and its fixture stay deleted. Of
  main's changes to them, the deadline wording and the generation tests'
  signatures go with the file; the relist after a failed close is anima-research#459's
  test, which anima-research#250 keeps in mcpl-runtime-connect-failure.test.ts, so that
  assertion moves there.

Outside the conflicts:
- test/fixtures/reregistering-mcpl-server.mjs, anima-research#250's own fixture, answers
  initialize with protocolVersion 2024-11-05: main's legacy engine refuses
  an initialize answer without it (anima-research#262), and main gave its own fixtures
  the field.
- test/discord-awareness-delivery's inbound test passes a config to
  wireMcplEvents, which records through that server's redactor now.
- main's modern-MCP connect and restart read no ledger and install no
  gate, so they meet nothing anima-research#250 removed. Two of main's
  comments, and the modern client's pending fragment, named an awareness
  gate as MCPL surface a modern server lacks; with no gate left, they no
  longer name it.
- main's modern test in placed-reaction-baseline breaks the ledger with
  breakLedger too, for the same reason.
- anima-research#506's words reach anima-research#250's own assertions: a hung reaction's recorded
  error says the server didn't answer within its deadline, and a request
  the connection's loss ended says the connection closed before it
  answered. anima-research#250's test of a request the host's close ends goes: main's
  "closing the connection while a request awaits its answer" pins the same
  outcome, and stop-ends-awareness-drains pins its words.
- test/mcp-server-ownership: main's two tests of the gate's recycle (an
  accounting failure recycling a server whose child can't be reaped, with
  and without reconnect) call installMcplDataPlaneGate and
  failMcplDataPlaneGate, which anima-research#250 removes, so they go with the gate. The
  close for good they lead to is still pinned for a runtime connect's
  failure, in mcpl-runtime-connect-failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant