Skip to content

fix(framework): awaited tool calls and the turn's dispatch share one routing function - #913

Merged
nissa-seru merged 5 commits into
anima-research:mainfrom
nissa-seru:alfred/puppet-routes
Oct 30, 2026
Merged

nissa-seru merged 5 commits into
anima-research:mainfrom
nissa-seru:alfred/puppet-routes

Conversation

@nissa-seru

@nissa-seru nissa-seru commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

With this change an awaited tool call takes its route from the function the turn's dispatch uses, so where such a call made as an agent failed with "Invalid tool name format" on 13 framework tools the agent has, it now gets each tool's own answer, and on skip_reply, which ends the turn it's called in, a refusal.

An awaited call is one whose caller waits for the result in process. The admin puppet's puppetToolCall and a host's executeToolCall make it as the agent, and a module's ModuleContext.callTool makes it as the module. In a probe built from main's source, a host's executeToolCall of each of 14 names that the turn's dispatch routes, agent_settings among them, answered "Invalid tool name format: NAME, so nothing was done". The published 0.30.0 gave the puppet's agent_settings the same answer, and connectome-host's /puppet command calls puppetToolCall. Built from this change at d1be0036a3, 13 of the 14 answered as their handlers do, and skip_reply got a refusal that says why. This change's ten new tests fail against main's src/framework.ts and pass at d1be0036a3.

Problem

puppetToolCall, executeToolCall and callTool all go through executeToolCallFrom (src/framework.ts:27124 at main 8145085d06). It checked its own list of routes and passed any other name to the module registry (:27233), which answers a name without -- with "Invalid tool name format: NAME, so nothing was done" (src/module-registry.ts:371). Fourteen names the turn's dispatch routes weren't on that list:

  • agent_settings;
  • save_recent_image, read_image and fetch_reference;
  • gate_status, event_tags, wake_add_rule, wake_remove_rule, sleep and wake;
  • think, journal and skip_reply;
  • prose_help.

In the probe, each of the 14 got that answer when a host's executeToolCall named the agent in callerAgentName. The probe's framework had an MCPL server and an event gate, which give the agent think, journal and skip_reply, and the gate tools (see Changes).

The puppet is where an operator meets it. connectome-host's /puppet <tool> [json] command calls puppetToolCall (src/commands.ts:980 in connectome-host 0.19.0). puppetToolCall's doc says the call "executes FOR REAL through the shared dispatch (MCPL, channel tools, utils, modules)", is refused "for tools outside the agent's own surface", and is stored in the agent's window "as a model-initiated call is stored" (src/framework.ts:26925–26948 at 8145085d06). Two runs show what it does with agent_settings:

  • The published package. A script that installs @animalabs/agent-framework 0.30.0 from npm, makes an agent with agent_settings on its surface and calls puppetToolCall('agent', 'agent_settings', {}) gets {"success":false,"error":"Invalid tool name format: agent_settings, so nothing was done","isError":true}.
  • main's source. A puppeted agent_settings adds two messages to the agent's window: a tool_use of agent_settings under the agent's name, and its tool_result, marked as an error, reading "Invalid tool name format: agent_settings, so nothing was done".

The puppet also refused prose_help for an agent with explicit prose routing. Its surface check read getToolsForAgent filtered by canUseTool (src/framework.ts:26998–26999 at 8145085d06). With main's src/framework.ts, this change's prose_help test fails with "puppet refused: tool prose_help is not on agent's surface".

Changes

  • One function decides the route. toolRoute (src/framework.ts:29152) returns a call's route as a ToolRoute (:819), checking names in the order the turn's dispatch checked them. dispatchToolCall (:29249) and executeToolCallFrom (:27190) each switch on what it returns. dispatchToolCall's switch ends in route satisfies never (:29449).
  • The awaited path runs the turn's handlers. The handlers of agent_settings, save_recent_image, read_image, fetch_reference, gate_status, sleep and wake, wake_add_rule and wake_remove_rule, and event_tags take a deliver callback. It defaults to pushToolResult (:29181), which pushes the tool-result event they pushed before. The awaited path passes one through awaitedToolAnswer (:29185), which resolves with the handler's result and answers a handler's synchronous throw with its text. think and journal go to the channel registry, as the awaited path's channel tools already did, and prose_help answers with the routing reference the turn's call gets.
  • skip_reply is refused off a turn. It ends the turn it's called in, and an awaited call isn't made in one. A host's call gets "skip_reply ends the turn it's called in, and this call wasn't made in a turn, so nothing was done." The puppet refuses it by throwing, and stores nothing, whether skip_reply is called by name or through a toolsets--act that resolves to it.
  • A module can't call the agent's own tools. A module's callTool of think, journal, skip_reply or drafts gets "NAME is the agent's own, which a module's call can't make for it, so nothing was done." On main a comment on this path says the agent-only synthesized verbs think and skip_reply are deliberately not routed there (src/framework.ts:27188–27190 at 8145085d06, from @slimepriestess's edbb62271c). With this change think and journal run for a call executeToolCall makes as the agent, the puppet's or a host's, and a module's call of any of the four is refused.
  • The four need the channel registry. toolRoute gives them their routes only when the framework has a channel registry (:29174, :29176), as the turn's dispatch did, and the framework builds the registry when it starts with an MCPL server or connects its first one (:5045, :34055–34057).
  • The gate tools need an event gate. toolRoute gives gate_status, sleep, wake, wake_add_rule, wake_remove_rule and event_tags their routes only when the framework has one (:29167, :29170, :29172, :29173), as the turn's dispatch did.
  • The puppet's surface check reads availableToolsForPresentation, the tools the agent may call (:27036), so an explicit-prose agent's prose_help is on it.
  • No refused on an awaited answer. Where the dispatcher answers one of the calls above with a refusal, such as a conversation fork's sleep, awaitedToolAnswer passes on the refusal's text without refused. ToolResult.refused's doc says a programmatic call "opens no RFC-007 call, so it's never refused" (src/types/events.ts:227–228).
  • Two handlers that left a call without an answer. fetch_reference now answers "fetch_reference couldn't answer: …" when its work throws before it answers, such as for a ref_id that can't be turned into text, on the turn's path and the awaited one (:37182). save_recent_image waits for its round's other calls only when it is one of that round's pending calls (:37309), so a module's save made from inside its own pending tool call isn't held by the round that call is in. The check this replaces exempted a script's calls (pytc-…) alone.
  • Docs. executeToolCall's doc said it routes "(module registry or MCPL)". It now says the call goes where the agent's own call of the name goes, which includes the framework's own tools the agent has, and that skip_reply is refused. puppetToolCall's doc names the surface it checks and its skip_reply refusal.
  • test/awaited-tool-routes.test.ts, new, with 10 tests.
  • changelog.d/awaited-call-routes.fixed.md and changelog.d/awaited-call-routes.added.md, new. The second covers a host's executeToolCall reaching the framework's own tools that the agent has.

What each part is for

  • toolRoute and its 21-kind ToolRoute put the route decision in one place for both paths. The 14 names the awaited path missed were names the turn's dispatch routed and the awaited path's own list didn't, so one function for both keeps the two from drifting apart again. dispatchToolCall's route satisfies never stops a new kind from compiling until that switch handles it. Routing agent_settings alone on the awaited path would have fixed the reported call and left the two lists to drift.
  • A deliver callback on eight handlers, defaulting to the push each made, lets the awaited path run the handler the turn runs, rather than a copy of it. The default leaves the turn's path pushing each result where it did before. Handlers that returned a promise instead would have moved those pushes to a later microtask, and copies for the awaited path would have been two bodies to keep alike.
  • awaitedToolAnswer is the one place the awaited path turns a handler's answer into its result. It drops refused, which ToolResult.refused's doc says a programmatic call never gets, and answers a handler's synchronous throw with the throw's text, so the caller gets an answer either way.
  • agentsOwnVerbRefusal gives the four tools a module can't call for the agent one refusal text.
  • The skip_reply refusal off a turn, and the puppet's check for it, keep an awaited skip_reply, made outside any turn, from reaching the handler that ends the turn it's called in. The puppet's check reads the call's route, so a toolsets--act that resolves to skip_reply is refused the same way, before anything is stored.
  • think, journal and prose_help go where the awaited path's channel tools and the turn's prose_help already went, so they need no new handler.
  • The puppet's surface check reads the tools the agent may call, so the puppet allows what the agent itself may call, prose_help for an explicit-prose agent among them.
  • fetch_reference's catch gives a call an answer when the work behind it throws, on the turn's path and on the awaited path, which this change opens to it.
  • save_recent_image's round check replaces an exemption for a script's calls (pytc-…) with the rule the exemption stood for: wait for a round's other calls only when the save is one of that round's pending calls. This change needs the rule once a module's save reaches the handler.
  • The test file has one test for each group of behaviour, on the repository's existing test fixture (test/helpers/coalescing-fixture.ts), and builds two frameworks of its own for a conversation fork and a module's image save. prose_help's test also checks that the call succeeds, so two equal errors can't pass it.
  • The two changelog fragments. .fixed says what the puppet, a host and a module meet now. .added is one sentence, that a host's executeToolCall reaches the framework's own tools the agent has, pointing to the Fixed entry for the rest.

With other open pull requests

At 01:11 UTC on October 30, of the 36 other open pull requests that change src/framework.ts, twelve merge with main at d700dce18b without conflicts, by git merge-tree:

What this leaves

#911 describes a gap this change leaves as it was: the awaited path's channel_* route doesn't make the checks the turn's dispatch makes for a conversation fork. In a probe, a host's executeToolCall naming a fork opened a channel other than the fork's home, and a module's callTool naming the fork published to that channel, where the fork's own calls of both were refused.

Tests

This branch's commit messages were reworded after the runs below, with every commit's tree unchanged, so each run is named by the commit that now holds the tree it ran: d1be0036a3 was 8d7e7c1977, 862c981e90 was 28f47f2395, and 42567acbaf was 28a55ac252. Each run below was on Linux, in a clean checkout. The runs at d1be0036a3 and 862c981e90 printed Node 24.22.1, npm 11.19.1 and TypeScript 7.0.2.

  • At d1be0036a3, in each of two runs: the build (npm run build) succeeds, and test/awaited-tool-routes.test.ts passes 10 of 10. These are the runs this description's probes ran in, each with its own probe file added to test/, and both printed the dependencies npm install gave: chronicle 0.5.0, context-manager 0.24.1, membrane 0.8.0, mcpl-core 0.4.0, @modelcontextprotocol/client and /server 2.4.0, discord.js 14.27.0, ws 8.22.0 and chokidar 4.0.3.
  • At 862c981e90, which merges main at 8145085d06 into this change. d1be0036a3 changes only executeToolCall's doc comment over it and adds changelog.d/awaited-call-routes.added.md. This run, about 20 minutes before the two runs above, didn't print its dependencies' versions.
    • test/awaited-tool-routes.test.ts: 10 of 10 pass.
    • With main's src/framework.ts in place of this change's: 0 of the 10 pass. Each fails on "Invalid tool name format", on the puppet's "not on agent's surface", or on a refusal that didn't come.
    • 115 test files, at concurrency 4: of 4,963 tests, 4,901 pass, 0 fail and 62 are skipped. They are the 114 files whose text names puppetToolCall, executeToolCall, callTool( or dispatchToolCall, or agent_settings, save_recent_image, read_image, fetch_reference, gate_status, event_tags, wake_add_rule, prose_help, tune_out or sleep with a character on each side, and main's test/turn-inputs-pin.test.ts, which the merge brought in.
    • Four breaks, each failing its own test: fetch_reference's catch removed; the save_recent_image wait's pending-call check removed; refused kept on an awaited answer; and the puppet's skip_reply check reading only the name it was called with.

42567acbaf, this head, changes only executeToolCall's doc comment and the two changelog fragments over d1be0036a3. In a run in a clean checkout of it, which printed no versions, the build succeeds and test/awaited-tool-routes.test.ts passes 10 of 10.

Probes. The probes in this description, apart from the published package's, ran in the two runs at d1be0036a3 above, built once with main's src/framework.ts and once as d1be0036a3 is. src/framework.ts is the only file under src/ that this change touches, so the first build's src/ is main's. The published package's probe installed 0.30.0 from npm on its own.

Not verified

  • The whole suite wasn't run at this head, at d1be0036a3 or at 862c981e90. The runs above cover 115 of the 388 test files.
  • Platforms, providers and hosts. Every run was on Linux, with Node 24.22.1 where the run printed it. The tests use stand-in providers. connectome-host's /puppet wasn't run against this change: the tests call puppetToolCall directly.
  • Successful calls. For save_recent_image, read_image, fetch_reference, wake_add_rule and sleep, the puppet's tests compare the answers to an input the handler refuses with an error, which shows that the call reached the handler and got the agent's own answer. The module's image save in its own test succeeds and writes the file.
  • Other callers. A module's callTool is tested for its four refusals and for save_recent_image. A call with no agent behind it is tested for think, journal and skip_reply.
  • A framework without an MCPL server. No test covers one. In a probe, at main and at d1be0036a3, its agent had none of think, journal, skip_reply and drafts, and a host's skip_reply and a module's think each answered "Invalid tool name format: NAME, so nothing was done".
  • A puppeted toolsets--act refused before it reaches a tool. No test covers one. In a probe, at main and at d1be0036a3, the puppet's toolsets--act of a hidden skip_reply with its toolset closed answered "skip_reply is in [settings], which isn't open: toolsets--open {toolset:"settings"} opens it. Nothing was sent." and added that call and its result to the agent's window.
  • Route order. The awaited path now checks names in the turn's order, which moves its code_execution check after MCPL's and changes the order of the heartbeat's tools, drafts, tune_out and focus. No test checks the order.
  • A turn named by the caller. callingTurnOf takes a call's turn from its callerTurnId, matched against the agent's latest and open turns (src/framework.ts:22389–22393), and on an awaited call that field is whatever its caller set. The sleep handler announces a sleep only for a call made in the turn, and otherwise logs "sleep announcement not posted" (:37098–37099, :37104–37105). No test covers an awaited call that names a turn, or a sleep's announcement on an awaited call.
  • The merges with other pull requests were only checked for conflicts. No test ran on them.

  • Changelog fragments added: changelog.d/awaited-call-routes.fixed.md and changelog.d/awaited-call-routes.added.md.

Alfred-2451 made the change, and Ada-1017 made its design calls. Naomi-2422 read it word for word, Duncan-2410 read its merge of main, and Byron-2452 read its last commit and read the change for anything it could do without. Internal reviews read earlier rounds of it. Lottie-2248 wrote this description.

Alfred-2451 added 5 commits October 29, 2026 17:04
…call does

The admin puppet (puppetToolCall), a host's executeToolCall and a
module's ModuleContext.callTool all run through executeToolCallFrom,
which routed only some of the framework's own tools. The rest fell to
the module registry and failed with "Invalid tool name format", and the
puppet stored that failure in the agent's window as a call the agent had
made. A household's operator met it as `/puppet agent_settings`.

The file pins what an awaited call must do:
- the puppet's agent_settings, workspace tools (save_recent_image,
  read_image, fetch_reference), gate tools (gate_status, event_tags,
  wake_add_rule, wake_remove_rule, sleep, wake), think, journal and
  prose_help each answer as the agent's own call of the same input does,
  and prose_help, on an explicit-prose agent's surface for the puppet,
  answers with the routing reference;
- skip_reply, which ends the turn it's called in, is refused off a turn:
  the puppet refuses it, by name or through a toolsets--act that reaches
  it, with the agent's window unchanged, and a host's call gets the
  refusal as its result;
- a module's call of the agent's own tools (think, journal, skip_reply,
  drafts) is refused with a reason naming the tool as the agent's own,
  even when it names the agent as its caller;
- a call with no agent behind it (no callerAgentName): think and journal
  answer as they answer the agent's own call, skip_reply is refused;
- a conversation fork's awaited sleep gets the dispatcher's refusal
  without `refused`, which ToolResult reserves for the dispatcher's own
  calls;
- a host's fetch_reference whose ref_id has no text form answers with
  the error, as the agent's own call does;
- a module that saves an image from inside its own tool call is answered
  at once, and the image is written, rather than held by the round its
  own call is in.

Against the base's src all ten fail, each on the defect: "Invalid tool
name format", the puppet's "not on agent's surface" refusal, or no
refusal where one is due.
…tool

toolRoute decides where a call goes, from its name, in the order the
turn's dispatch decided it, and both dispatchToolCall and
executeToolCallFrom switch on its answer. Each switch covers every
ToolRoute kind (dispatch's ends in `route satisfies never`, and the
awaited path must return), so a route added for one caller is a compile
error until the other handles it.

Before, executeToolCallFrom kept its own list, which lacked 14 of the
names the turn routes: agent_settings, save_recent_image, read_image,
fetch_reference, gate_status, event_tags, wake_add_rule,
wake_remove_rule, sleep, wake, think, journal, skip_reply and
prose_help. Each fell to the module registry and failed with "Invalid
tool name format".

- The eight dispatch handlers those routes need (agent settings, save
  and read image, fetch reference, gate status, sleep and wake, wake
  rules, event tags) take a `deliver`, defaulting to the tool-result push
  they made before, so the awaited path runs the handler the turn runs
  and gets its answer through awaitedToolAnswer. That drops `refused`
  (ToolResult.refused: a programmatic call is never refused) and answers
  a synchronous throw with its text.
- think and journal on an awaited call with agent origin go to the
  channel registry as the awaited channel tools already did. skip_reply
  is refused there: it ends the turn it's called in, and an awaited call
  isn't made in one.
- The puppet refuses skip_reply, by name or through a toolsets--act that
  reaches it, before anything runs or is stored, as it refuses a tool off
  the agent's surface: a stored pair must be an act the agent could have
  made, and the agent's own skip_reply never answers "not in a turn".
- A module's call of think, journal, skip_reply or drafts is refused as
  the agent's own (agentsOwnVerbRefusal). This keeps the rule from
  e86ae33's review round, that these verbs are the agent's, for every
  caller but the agent itself; the old comment saying they were
  "deliberately not routed here" is removed with the list it described.
- The awaited path now takes the turn's order: code_execution after
  toolsets--act, MCPL, the channel tools and the framework's own tools,
  and drafts and the heartbeat after tune_out, focus and the
  subconscious's tools. None of these names can reach another of those
  routes (an MCPL name always carries `prefix--`, a heartbeat tool
  `heartbeat--`), so nothing a caller meets changes from the order alone.
- The puppet's surface check reads availableToolsForPresentation (the
  tools the agent may call, prose_help included for an explicit-prose
  agent) instead of getToolsForAgent filtered by canUseTool.
- fetch_reference's detached body had no catch, so a throw before it
  answered (a ref_id with no text form) left the agent's own call
  unanswered, as it would have an awaited one. It now answers once,
  with "fetch_reference couldn't answer: …".
- save_recent_image's sibling barrier waits only for a call that is one
  of its round's pending calls. A call outside the round, a script's or
  an awaited call's, counts the images the agent has now. Only a
  script's was exempt before, so a module's save from inside its own
  pending tool call, which this change lets reach the handler, would
  have waited on its own call until the barrier's bound.
…s own tools

Its doc named only "(module registry or MCPL)" as the handlers it routes
to, which said less than the method does once it takes the turn's
routing: it also reaches the framework's own tools and the channel
tools, and refuses skip_reply, which ends the turn it's called in.

A host's reach is an addition under the rule for published libraries (a
host that doesn't call these names meets no change), so it gets its own
`.added` entry beside the `.fixed` one.
…ches

The gate tools route only with an event gate, and think, journal and
skip_reply only with a channel registry, which an MCPL server brings, on
the turn's dispatch and the awaited path alike. executeToolCall's doc and
the Fixed fragment said the calls run unconditionally; they now say they
reach the tools the agent has.

- The Fixed fragment's "(__ephemeral__ where it names none)" for a
  module's call is dropped: tune_out and focus take the primary agent
  when no callerAgentName is given, as on main.
- The Added fragment is trimmed to one line that points at the Fixed
  entry, which already lists the tools and the skip_reply refusal. It no
  longer says a host that doesn't call these names meets no change,
  since a module's call of the agent's verbs now gets a named refusal.
@nissa-seru
nissa-seru force-pushed the alfred/puppet-routes branch from 28a55ac to 42567ac Compare October 30, 2026 01:09
@nissa-seru
nissa-seru merged commit fd6d6a3 into anima-research:main Oct 30, 2026
12 checks passed
@nissa-seru
nissa-seru deleted the alfred/puppet-routes branch October 30, 2026 02:03
tel-0s added a commit that referenced this pull request Oct 30, 2026
…p durable before its end, gate texts as written, awaited calls routed as the turn's, a stop that waits for a start

main at 48f26ce is four merges past d700dce, with no release among
them, so package.json stays 0.30.0 and this branch's floors stand
(chronicle ^0.6.0, context-manager ^0.25.0, membrane ^0.8.0), which
test/dependency-ranges pins. It brings #910 (the gate's refusals and file
errors name fields, rules and gate.json's path as written, and a change
says it is saved only when it brings gate.json into force), #909 (a
channel's tune-out cancel stores its dump and makes it durable before it
records the end; each dump row records the held rows it covered
(`released`), so a retried cancel shows nothing twice; commitDeliveries
answers whether the release is durable; every timer's cancel is tried
again when it throws, at the start too, and a tune-out past its wake
budget is cancelled at the start; a tool close of the excepted channel
whose release fails answers "The close wasn't recorded"), #913 (the
turn's dispatch and an awaited tool call take one route per tool,
toolRoute; the puppet refuses skip_reply) and #912 (a module's stop()
waits for its start() to settle; addModule() refused once a stop() is
admitted, after the add).

Three paths conflicted (17 regions):

- src/tune-out/coordinator.ts (12), the #909-into-#915 port. This branch
  has no deferred queue (#291), and its cancel waits while the framework
  keeps writes in memory (writesHeld: a surgery's hold, a manager
  refusing its work; deferCancel / runDeferredCancels, answered
  `deferred`, the tools' `waiting` + `note`). #909's rule goes into that
  cancel as it stands:
  - cancel(): reads the tune-out's state (getTuneOutState) first, and
    answers "not tuned out" without it; then, writes held, waits as
    before, nothing dumped or ended. Past the wait: the backlog is the
    epoch's rows (residentMessages: stored, then a surgery's kept writes
    where writesHeld threw) less those an earlier dump of the epoch
    covered (releaseRecordsOf over the same rows); the dump is written
    with its `released` record, and none when an earlier dump covered
    every row; commitDeliveries() === false throws, the tune-out in
    force; only then cancelTuneOut records the end, and the channel's
    timers and diverted count are cleared. The subconscious's report
    and the last reader fork's notice sum what the epoch's dumps left
    out (truncationNote). Main's heldForReport (queued rows handed to
    the subconscious as tune-out-cancel-held rows) stays out, as on this
    branch: every held message is stored, and the report keeps its
    unseenBySubconscious count; the record's `report` is always empty
    here, kept so the record has the shape main reads.
  - cancelAllExcept(): the same, over the hold's rows; main's `released`
    record replaces this branch's in-memory scopeReleased mark, and
    commitDeliveries() === false throws before writeScope(null).
  - Rows are keyed by id, and by `deferredWriteId` (writeIdOf, kept):
    a row an earlier version queued, stored by the boot's import, is
    named by its queued write's id in that version's records, so a
    cancel retried after an upgrade finds it covered. A surgery's kept
    write has no id, so a dump made during the hold (writesHeld having
    thrown) can't name it; a retry of that dump dumps it again.
  - resumeActiveEpochs(): main's try/catch around the start's cancels,
    their retries (armExpiry's and armScopeTimers' EXPIRY_RETRY_MS) and
    the start's wake-budget cancel (cancelPastBudget); main's
    `inForce`/oldestAnchor bookkeeping goes, since this branch anchors
    from the tune-outs the record holds after the cancels
    (pruneWindowStarts, anchorSubconscious('resume')), which leaves out
    one whose cancel ended it and counts one whose cancel waits.
  - The hooks' getDeferredMessages stays out (this branch's
    getSurgeryHeldMessages); commitDeliveries is `() => boolean`, its doc
    kept and rewritten. cancel()'s and cancelAllExcept()'s doc comments,
    which main removed, stay, rewritten to the new order: they carry this
    branch's `deferred` answer.
- src/framework.ts (4):
  - addModule(): main's refuseOnceStopped('addModule()') after the add,
    with this branch's comment on useHistory (its check leaves out an
    undone row).
  - the deferred queue's persistence (persistDeferredWrites and the
    queue's record): stays deleted, as on this branch; main's change
    there was persistDeferredWrites' boolean.
  - onToolClose: main's form (a release that throws or refuses answers
    "The close wasn't recorded: …"), with this branch's sentences for a
    release that waits and one that went through ("The focus on it
    ended, …", 6243858).
  - commitDeliveries(): answers false when the store's sync fails and
    true otherwise; there is no queue to persist.
- src/gate/event-gate.ts (1): a pending batch whose delivery throws:
  main's `Rule "<name>"` opening, with this branch's deliveredInPart for
  a batch that woke some recipients before it threw.

Read onto this branch past the conflicts:
- docs/dendrite.md: a cancel stores and syncs its release before it
  records the end; one whose release can't be made durable throws, the
  tune-out in force, and the next cancel delivers what no earlier
  release covered (`released`). Main's sentence on the all-except
  hold's start came in clean.
- test/all-except-unit: main's tests take this branch's harness
  ({ stored, kept, writesHeld }); its anchors hook reads the start's
  sequence (setSubconsciousAnchor takes a SubconsciousWindowStart here).
- test/tune-out-cancel-order: no getDeferredMessages hook; the anchors
  hook as above. Main's "a message queued at a dump whose end record
  failed, which lands before the retry" has no queue to stand on here;
  it becomes the upgrade case: a dump an earlier version wrote, naming a
  row by its queued write's id, and that row stored by the import with
  deferredWriteId, gets no second dump.

storeSees: no new site; main opens and derives no context manager.
agentSees: no new view decision. #913's awaited routes run the turn's
handlers with a `deliver` that answers the caller; none stores a row of
its own (the puppet's pair is stored as before, its agent's own row).
Notice tags: none new; #910's texts are tool results and stderr lines,
#909's are stderr lines and errors thrown to a tool or timer.

Changelog: no release in main's range, so no fragment was consumed;
main's seven new fragments come in as written. 291-deferral-queue's
tune-out hosts' bullet says that with no queue a release fails to be
made durable only where the store's sync fails, so no queue over its
cap keeps a tune-out in force (tune-out-cancel-order's sentence), and
how a dump's record names a row.

Tests: built with zero errors; no stale compiled test; fixtures copied.
main's 16 changed test files: 419 of 419. presentation*, window-*,
dendrite-*, stored-injections, audience-stamp*, dependency-ranges,
held-*, tune-out-*, admission-e2e, all-except*, mcpl-coalescing*,
operator-surgery, read-marks* and turn-inputs-pin (53 files): 964 of
964. With this branch's coordinator as it was (HEAD's), and the rest of
the merge, tune-out-cancel-order, all-except-unit, tune-out-timers and
tune-out-gate-composition fail 21 of 77; at this merge, 77 of 77.

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