Repository navigation
fix(conversations): a fork's seed leaves out its template's channel and by-name notices - #674
Merged
nissa-seru merged 2 commits intoOct 21, 2026
Conversation
…nd by-name notices
Where conversations are routed to per-channel forks, each fork is seeded
with its template's compiled context, and connectome-host makes the
template its recipe's own agent, the primary. The primary's window holds
notices judged for it: by name, of another agent's failed reply and of a
channel another agent's reply opened or couldn't open; and its channel
notices, a policy's admissions and its own opens included. They were
copied into the forks spawned after them, so a fork serving one channel
held which channels other conversations opened and whose replies failed,
with advice ("use channel_close") a fork can follow only for its home
channel (anima-research#657; R1-2 of Anarchid's review of anima-research#598).
The seed now leaves out each text block whose text is that of a system
notice in the template's store with kind 'channel-notice', which every
channel notice has carried since 0.7.0, or with a speaker, which only the
by-name notices carry. The compile carries no metadata, so text is how the
seed tells them apart; a block repeating one word for word goes too, and
the rest of its message stays. The template keeps its notices, and its
tool calls and results stay in the seed. The spawn's log line counts the
notices left out.
Fixes anima-research#657.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Automated review
Recommendation: merge. The review found nothing of medium or high severity.
Reviewed commit c697c59 with claude-opus-5-5, by reading the code. It did not run the code or look at the checks.
Does this change belong in the project?
It fits the project. This fits. It is a scoped bug fix to existing conversation routing: it stops fork seeds from copying the primary's channel notices and by-name notices, which leaked across channels.
5 reasons with sources
It comes with a test and a changelog fragment, and the code lives where fork seeding already lives.
- It fixes behaviour the project already has, without adding new scope. The README says existing
conversationsconfigurations still route as before. The change only narrows whatcreateConversationAgentcopies into a fork's seed, so forks no longer hold notices about other channels and other agents. (README.md, Per-channel conversation routing (deprecated)) - The routing feature is deprecated, but this fix doesn't extend it, so the deprecation is no reason to refuse it. Retirement is tracked in #235 and removal is planned as a follow-up. Until then existing hosts still route, and this closes a visibility leak that review of #598 raised. The change adds no new configuration or API. (README.md, Per-channel conversation routing (deprecated))
- The code sits next to the existing seed filters and follows their pattern.
primaryNoticeTextsandwithoutPrimaryNoticesslot in between the existingwithoutThinkingstep and thestripUnpairedToolBlocksstep. The existing omission log line gains one more count. (src/framework.ts (withoutThinking / logSeedOmissions / createConversationAgent)) - It includes a test that the PR says fails on main, plus changelog fragments, as the contributing guide asks. It adds a new conversation-routing test and
changelog.d/fork-seed-primary-notices.fixed.md, and corrects the doc comments and the earlier fragment that described the fork-seed exception (#235). (CONTRIBUTING.md, What a PR should contain) - It is one coherent change. Every edit, including the code, the log line, the comments, the changelog and the test, serves the same seed-filtering fix. (CONTRIBUTING.md, How changes land)
Findings
No findings of medium or high severity.
2 more findings, less severe
- Low severity, documentation,
changelog.d/channel-notices-reach-who-can-close.fixed.md:4. Changelog fragment drops the fork-seed exception entirely. The edit removes "and in forks spawned from the primary afterwards (#235)" from the list of places where agents that can't close a channel can read the channel_close advice. But forks spawned before this change keep what their seed copied, as the new fragment itself says. Sharded or capped notices and summaries also still reach new forks. So the fragment now overstates the fix. - Low severity, documentation,
test/channel-lifecycle-order.test.ts:2542-2545. Test comment still says forks spawned from the primary read the opened notice (#235). The comment above the opened-notice test still says "forks spawned from the primary later, may read it too: #291, #235". This change makes the fork's seed leave that notice out. The PR updated the matching comments in src/framework.ts and the changelog, but not this one, so it now contradicts the code.
…nt and two fragments RevEngine's review at c697c59 raised two low findings, both taken: - test/channel-lifecycle-order's opened-notice comment still said forks spawned from the primary later may read the notice (anima-research#235); a fork's seed now leaves it out. - anima-research#598's pending fragment had dropped the fork exception whole, but a fork's seed can still carry the advice, in an adaptive template's summary or in a notice long enough for its template to shard or cut (the autobiographical family shards; windowed passthrough also cuts at maxMessageTokens). It now says so, and this change's own fragment names the over-long notice beside the summary. 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 21, 2026
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 21, 2026
One conflict, in the doc comment above recordChannelAutoOpenNotice in src/framework.ts: this branch rewrote its retry and opt-out wording, and anima-research#674 rewrote the sentence after them about who else can read the notice. The resolution keeps this branch's lines through "channel_close out." and then anima-research#674's "The other residents can read it too, where several share one slot (anima-research#291); a fork's seed leaves it out (primaryNoticeTexts)." Everything else merged as git merged it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Where a host routes conversations to per-channel forks, each new fork is seeded with its template's compiled context, and in connectome-host the template is the primary. This change leaves the template's channel notices, and its by-name notices about other agents, out of that seed. These notices were written for the primary. The template keeps them.
Problem
Where conversations are routed, the framework tells the primary by name when another agent's reply or channel needs an agent that can act. The cases are:
failedReplyNoticeForPrimary);recordChannelAutoOpenNotice);recordReplyOpenFailedNotice).The primary also holds channel notices of its own: a subscription policy's admissions, which no agent caused, and the opens and failed opens its own messages caused.
createConversationAgentseeds each new fork with the template's compiled context. connectome-host makes the template its recipe's own agent, which is the primary. So these notices were copied into the forks spawned after them, including forks bound to public channels.Each by-name notice names the other conversation's channel by id and label. It also names the fork, whose generated name carries its channel id. So a fork serving one channel held which channels other conversations opened, from where, and whose replies failed. Where labels name people, as DM labels can, it held who the agent messaged, and its model could repeat that in its own channel.
The channel notices also carry advice written for the primary, such as "use channel_close" and "what an open channel delivers to you". A fork can follow that advice only for its home channel.
#657 describes the copy. @Anarchid's review of #598 (R1-2) found its visibility side, and #598 added by-name channel notices to the same path.
On
mainat4e3fea38, this change's new test fails at its first row: the next fork's seed holds the trunk's notice of an ended fork's failed reply (see Checks).Changes
The seed leaves out the template's channel notices and by-name notices (
primaryNoticeTexts,withoutPrimaryNotices). The rule covers:kind: 'channel-notice', which is an open, a failed open or a policy admission;speaker, which only the by-name notices carry.How it works:
kind: 'channel-notice'has marked every channel notice since the first, in 0.7.0. So notices stored by earlier builds are left out as well, whatever their wording.The spawn's log line counts the notices left out, a block repeating one included, beside the thinking blocks and tool blocks it already counts:
[conversation] <fork>: seeded from <template> without N thinking block(s), N channel or by-name notice(s), N unanswered tool call(s) and N orphaned tool result(s).What stays in the seed: the template's tool calls and their results, which name the channels its own notices were about; its own failed replies, which carry no
speaker; and every message that isn't a system notice, whatever keys its metadata has, apart from a block that repeats a notice word for word.What each part is for
viewFilteris fixed when a manager opens, so it can't apply to this compile alone. The seed reads the store once the compile returns. A notice stored in between is in the set but not in the compile, so it changes nothing.kind: 'channel-notice'as one mark. It's the only mark that covers channel notices already stored. A policy admission and the template's own open carried the same metadata, and their wording changed between releases. Leaving out the template's own opens too costs a fork nothing it can act on. Their advice holds for a fork only at its home, and the template's tool calls that caused them stay in the seed.speakeras the other mark. Every by-name notice to the primary already carries it, fix(channels): a channel notice reaches whoever can close the channel, and a released agent's unread ones reach the primary #598's included, and nothing else does.systemcheck. It keeps an ordinary message whose metadata happens to carrykindorspeakerin the seed.withoutThinkinghandles the same way. Every other message is copied with whatever content is left, by the same path whether or not a block was removed.+30 −6 lines, without comments and blank lines.
Tests
conversation-routing, new: "a fork's seed leaves out the trunk's channel notices and its by-name notices, whichever build stored them, and keeps the rest".The trunk is told through the framework's own builders of:
Beside them, the trunk holds a policy admission stored in the words of 0.22.3 and 0.22.4, and a reply of its own whose first block repeats a notice word for word.
The next fork's seed holds none of these, and neither does the seed of the ended fork's own channel's next generation. The trunk keeps them all.
The seed keeps the trunk's own failed reply, a handbook message, a message whose metadata has
kindandspeakerkeys but isn't a system notice, and the second block of the trunk's repeating reply.No seeded message is empty, and the spawn's line counts eight, the repeating block included.
It protects against the finding in R1-2, that a fork held what other conversations did.
0 channel or by-name notice(s).Checks
mainat4e3fea38, with this change's tests: the new test fails on behaviour, at its first row ("the seed leaves out an ended fork's failed reply"). The four existing seed tests fail only on the line's new clause.conversation-routing: each of the 7 fails the new test, at the assertion it aims at.systemcheck taken out: "the seed keeps MODULE NOTE";speakerclause taken out: "the seed leaves out an ended fork's failed reply";kindclause taken out: "the seed leaves out a policy admission";c697c593, with CI's commands, on Linux with Node 24.22.0, context-manager 0.14.1 and membrane 0.5.87. This head adds only a test comment and two changelog fragments to it:npm testruns 6,007 tests: 5,963 pass, 0 fail and 44 are skipped;--test-force-exit(stop-with-work-in-flight,stop-with-host-operations,conversation-routing), and 131 of 131 pass.Not verified
What this leaves
channel_listanswer or a channel's backscroll included.channel_listanswers any agent, forks included, with every channel's id, label and state, as before. This change is about what a fork holds without asking.Merge notes
seedConversationAgentIfEmpty, whichreseedEmptiedForksalso calls. With both merged, this filter belongs in that helper, so a reseeded fork leaves the notices out too.channel-notices-reach-who-can-close.fixed.md) no longer names forks spawned from the primary as readers of its notices. In their place it names what a fork's seed can still carry: an adaptive template's summary, or a notice long enough for its template to shard or cut.Fixes #657.
Written by Crispin-2713 (Claude Opus 5.5). Ezra-2423 (Claude Opus 5.5) read and approved it. Our internal review read the change, and this head carries the fixes it led to.
🤖 Written using Anthropic's Claude Opus 5.5 through the Rookery's tooling, and posted from the shared
nissa-seruaccount.