Repository navigation
fix(tune-out): mark held messages in module reads, keep them out of a live turn, and carry them at the cancel - #583
Conversation
… module's reads A message that arrives in a tuned-out channel is stored with metadata.tuneOut, and every resident's viewFilter leaves it out of the compiled view, during the tune-out and after it. A module's context answered it as an ordinary row: ctx.queryMessages and ctx.getMessage returned it with the stamp in its metadata and nothing else to tell it apart. So a module that keeps to what the residents see by leaving out `held: true` rows, as connectome-host's retrieval module does when it picks lessons from the last ten rows, took tuned-out traffic in. - held-writes.ts: asModuleRow(row) marks a row that carries the stamp `heldFromResidents: true`, and returns any other row as it is. - framework.ts: queryMessages marks the stored rows, both when no held write matches and beside the held rows; heldRows marks a held write whose metadata carries the stamp (a tuned-out message that arrives while the resident's turn is alive); getMessage marks what it answers, by id, by a held write's handle, and the stored row a handle names once its write lands. - types/module.ts: ModuleMessage.heldFromResidents, and the getMessage and queryMessages docs name it. It's a field of its own because `held` means the framework still holds a write whose id is a handle. The doc says the mark is about the row: a cancel brings the residents the text of the newest stored ones, up to the tune-out's backlogCap, in one <tuned-out-backlog> row of its own, which isn't marked. - test/module-rows-held-from-residents.test.ts, on tune-out-e2e's MCPL fixture: a stored held message is marked by both reads, during the tune-out and after it, while an ordinary module write and the release's dump aren't, and the resident's compiled view leaves out the marked one and shows the ordinary one; a tuned-out message that arrives while the resident's round has a tool pending is a held write marked as well as held, beside a marked stored row, and its stored row is marked once it lands. - changelog.d/module-rows-held-from-residents.fixed.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At a tool-result boundary, drainDeferredFor stored a tuned-out held write and handed it to the live turn with the other held writes: isInjectableMidTurn checks the participant and tool blocks, not the tune-out stamp. So a resident that tuned out a channel was shown its messages mid-turn, though its compiled view leaves them out. This is gap 5 in anima-research#232, which our proposal of October 6 there named; anima-research#546 fixes it the same way in its own version of the loop. - framework.ts, the tool boundary's injection: a write carrying the stamp is stored (and acked, and noted for other residents, as before) but not injected, so it isn't a conversational injection either. - types/module.ts, held-writes.ts, framework.ts (heldRows): the docs say the row itself enters no resident's compiled view or live turn, and where its text can still reach one in rows of its own, unmarked: the cancel's <tuned-out-backlog> row, and a tool that reads history on request, such as a channel's fetch_history or the history module's extract. heldRows' comment names the mark. - changelog.d/module-rows-held-from-residents.fixed.md: a bullet for the injection, and the module bullet says where the text can still reach a resident. - test/module-rows-held-from-residents.test.ts: each test opens its own framework and mock, so no reply one test queues reaches another's turn. Test 1 checks totalCount and a reversed limit-1 query on the no-held branch, and says "cancel" as the code does. Test 2 adds a module write held in the same turn, queued behind the tuned-out one: it has no mark and is injected at the boundary, while the tuned-out message is stored and not injected, so a skip that stopped the loop fails it. Its wait for the landed row needs the row, and the landed row is checked by its stored id and in queryMessages too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s report
With the tool boundary no longer injecting a tuned-out held write, a
message still held when the tune-out is cancelled reached the resident
through nothing: the cancel's <tuned-out-backlog> was built from stored
messages only, and on main the injection had been what delivered it.
The subconscious's cancel report, which covers what the dump leaves out
past backlogCap (all of it at backlogCap 0), couldn't cover it either:
the subconscious's view holds stored messages only, and its report can
run while the resident's turn still waits on a tool.
- tune-out/coordinator.ts:
- An optional getDeferredMessages hook. The backlog is the epoch's
stored messages in store order, then its held ones, oldest first
(gap 7 in anima-research#232; anima-research#546 has the same hook and backlog, without the
sort).
- The held messages the cap leaves out go to the subconscious with
its cancel report notice, each with its own content blocks after its
author, so an image or audio message reaches it as it came.
- A cancel the subconscious makes itself gets no report turn on main,
since its note covers what it saw. It now gets the notice and a
report turn when it leaves held messages out, for those alone, as it
wrote its note before it could see them.
- The notice says the messages above the cap weren't delivered raw to
the resident.
- framework.ts: the hook gives the deferred writes for the primary, in
seq order, as every other listing of the queue does: a store write
refused at a boundary goes back to the end of the queue. The drain's
comment says a message a tune-out holds is stored at a tool boundary
but not injected, and why that keeps the window and the live request
matched; the deferral comment names the exception.
- types/module.ts, held-writes.ts, the fragment: no resident is shown
the row itself, and no resident's compiled view includes it (the
subconscious's does). Its text can still reach a resident in rows of
their own: the dump, with the last backlogCap of the tune-out's
messages, the stored ones first and then the held ones; what the
subconscious says of it; and a tool that reads history on request.
- test/module-rows-held-from-residents.test.ts:
- with backlogCap 3 over two stored messages, two held ones and a held
module write, the dump reads messages=4 truncated=1 and carries the
newer stored message and both held ones, the notice carries none,
exactly one injected message carries the held text, and the first
original lands under its handle, marked;
- with backlogCap 1, two held and the queue out of seq order as a
requeue leaves it, the dump shows the newer, and the notice carries
the older;
- with backlogCap 0, the dump counts both held messages and shows
neither, and the notice carries both, oldest first.
- test/tune-out-cancel-held.test.ts, on the coordinator alone: the dump
and the notice take only the ended epoch's messages, the notice carries
a held image as it came, and a cancel the subconscious makes itself
gets the notice and a report turn only when it leaves a held message
out.
- test/deferred-window-only.test.ts, test/present-while-acting.test.ts:
their headers name the exception to "stored and injected".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cancel's dump renders each message it shows from its text blocks
only, so a held image or audio message inside the cap reached the dump
as an empty author line. It counted as shown, so the report notice
didn't carry it either, and with the tool boundary no longer injecting
it, its media reached neither the resident nor the subconscious.
- tune-out/coordinator.ts:
- A held message the dump shows whose content has anything but text
goes to the report notice too, with its content blocks. The dump
stays as it was.
- The notice always gives the count of messages above the cap. A
self-cancel's left it out on the premise that its note covered
them, but cancel_tuneout's note is optional.
- A cancel the subconscious makes itself gets the notice and a report
turn when any held message goes to it. The comment above the report
no longer says such a cancel gets no turn.
- the fragment says so.
- test/tune-out-cancel-held.test.ts:
- the dump shows only its own epoch's messages, with a cap that
shows them, and the notice then gives no count and no held message;
- at backlogCap 0 the notice gives the count and carries each held
message right under its author, an image as it came;
- a held image the dump shows goes to the notice;
- a cancel the subconscious makes itself, with no note, gets the
notice, the count and a report turn when a held message is left
out or the dump shows a held image, and neither when the dump shows
a held text message or the cap cuts only a stored message.
- test/module-rows-held-from-residents.test.ts: test 3 checks the
notice's count above the cap; test 5's cancel names the subconscious
as its source, with a note, through the framework's coordinator.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce's blocks
A cancel the subconscious makes itself got no report turn, since its
note carries its words. The note is optional, so a self-cancel without
one whose cap left stored messages out of the backlog brought nothing
to cover them: at backlogCap 0, where the tool says the subconscious's
report is the whole delivery, the resident got an empty backlog and no
report.
- tune-out/coordinator.ts: a cancel the subconscious makes itself with
no note now gets the notice, with the count above the cap, and a
report turn when the cap cut anything. With a note, its note still
speaks for what it saw. The comment above the report says so.
- the fragment: a bullet for it.
- test/tune-out-cancel-held.test.ts:
- the notice's held messages are checked as an exact block sequence,
so a message carried twice, or out of order, fails;
- a cap of 2 over three held messages, a text-and-image one, a URL
image alone and an audio clip alone: the notice carries the one the
cap cuts and then the two media-only ones the dump shows, each once
with its blocks as they came;
- a self-cancel with no note at backlogCap 0 over two stored messages
gets the notice, the count and a report turn; with a note, or with
nothing cut, it gets neither.
- test/module-rows-held-from-residents.test.ts: test 5's name and
comment say its cancel is in the subconscious's name, called while
only the resident's turn runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Medium impact] The changes affect tune-out message delivery and the shared module message-read contract. The PR appears safe to merge, with a non-blocking concern about large cancel-report messages. Findings
Fix with agent prompt### Issue 1
src/tune-out/coordinator.ts:389-397
With `backlogCap` set to `0`, this combines every held message into one `tune-out-cancel-report` row. A busy channel can build up a large batch while a resident waits on a tool. If `maxMessageTokens` is configured, the subconscious applies that limit to the combined batch rather than each original message. Without that limit, the combined row can outgrow its context budget.
Keep the notice small and pass the content through bounded rows or batches. Add a test where the combined content exceeds the per-message limit.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.SummaryKeeps tuned-out messages out of live resident turns and marks them in module reads.
Reviews (1) · Last reviewed commit: "fix(tune-out): report a note-less self-c..." · Reviewed by Greptile |
| const heldBlocks: ContentBlock[] = heldForReport.length > 0 | ||
| ? [ | ||
| { type: 'text', text: 'Still held at the cancel, so your view may not have them yet:' }, | ||
| ...heldForReport.flatMap((m): ContentBlock[] => [{ type: 'text', text: `${authorOf(m)}:` }, ...m.content]), | ||
| ] | ||
| : []; | ||
| this.hooks.addMessage( | ||
| 'user', | ||
| [{ type: 'text', text: `[Tune-out cancelled: ${channelId} — ${reason}.${truncNote}]` }], | ||
| [{ type: 'text', text: `[Tune-out cancelled: ${channelId} — ${reason}.${truncNote}]` }, ...heldBlocks], |
There was a problem hiding this comment.
Cancel reports grow without bounds
With backlogCap set to 0, this combines every held message into one tune-out-cancel-report row. A busy channel can build up a large batch while a resident waits on a tool. If maxMessageTokens is configured, the subconscious applies that limit to the combined batch rather than each original message. Without that limit, the combined row can outgrow its context budget.
Keep the notice small and pass the content through bounded rows or batches. Add a test where the combined content exceeds the per-message limit.
Knowledge Base Used: Agent runtime orchestration
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tune-out/coordinator.ts
Line: 389-397
Comment:
**Cancel reports grow without bounds**
With `backlogCap` set to `0`, this combines every held message into one `tune-out-cancel-report` row. A busy channel can build up a large batch while a resident waits on a tool. If `maxMessageTokens` is configured, the subconscious applies that limit to the combined batch rather than each original message. Without that limit, the combined row can outgrow its context budget.
Keep the notice small and pass the content through bounded rows or batches. Add a test where the combined content exceeds the per-message limit.
**Knowledge Base Used:** [Agent runtime orchestration](https://app.greptile.com/anima-labs/-/custom-context/knowledge-base/anima-research/agent-framework/-/docs/agent-runtime.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Addressed in 62856c53. The notice is now one row that says how many held messages follow it, and each held message follows it as a row of its own for the subconscious, with its author's label and its own blocks. The subconscious's per-message cap, which it mirrors from the primary's maxMessageTokens, now cuts each one on its own rather than the batch's newest, and a batch over the usable budget no longer arrives as one row, which by our reading of context-manager 0.14.1 the strategy would refuse.
The test you asked for is test 6 in test/module-rows-held-from-residents.test.ts. It gives the primary a maxMessageTokens of 80, holds four messages of about 55 tokens each while the resident's turn waits on a tool, cancels at backlogCap: 0, and checks that each held message is whole in the subconscious's compiled view. At this branch's previous head, 4be0dacc, on Linux with Node 24.22.0, it fails on "the subconscious's view holds held message 1 whole".
Written by Hilda-2681.
— Lottie-2661
The cancel's report notice carried every held message it hands the subconscious in one row, and nothing bounded how many: at backlogCap 0, every message held while the resident's turn waited on a tool. The subconscious's strategy mirrors the primary's maxMessageTokens and cuts one message's text past it, so a large batch lost its newest messages, which the notice lists last; with no cap, a row past the usable budget refuses the report turn. Before the notice carried them, each held message reached the subconscious as a row of its own once stored. - tune-out/coordinator.ts: the notice is one row, which says how many held messages follow it, and each held message follows as a row of its own for the subconscious, with its author label and its own blocks, so a per-message cap cuts each one on its own, its label included, rather than the batch's newest. - the fragment: the held messages follow the notice as rows of their own. - test/module-rows-held-from-residents.test.ts: with the primary's maxMessageTokens at 80 and four held messages of about 55 tokens each, a cancel at backlogCap 0 leaves each one whole in the subconscious's compiled view; tests 4 and 5 read the held rows after the notice. - test/tune-out-cancel-held.test.ts: the held messages are checked as the exact rows after the notice, each a user row for the subconscious, and no held row comes without a notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main has moved since the last merge (815bfe3): anima-research#579 merges 0.23.1 back, anima-research#578 keeps credentials out of host errors, anima-research#581 projects mid-turn images, anima-research#584 turns Greptile's status comments off, anima-research#561 adds a WebSocket auth header, anima-research#586 defines dispositions for stubs, anima-research#574 reports an MCP pipe that fails while connecting, anima-research#583 holds module rows from residents, and anima-research#585 sends a stream's chunks and complete to each publish's destination, its server and its channel. Git merges both sides on its own. One test file needs a change: anima-research#585's three tests with the real channel registry start their turn from an external-message that names no channel, which this branch makes a private turn, so none of its prose is published. Their trigger now names the channel they stream to, as this branch's other outgoing-stream tests do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@antra-tess Flagging for your awareness this PR, which made the following changes to what residents see (in brief):
— Lottie-2661 |
A message a tune-out holds from the residents now stays out of a resident's live turn. When it's still held at the cancel, the cancel's backlog carries it to the residents. When the cap leaves it out, the subconscious's cancel report notice hands it to the subconscious, whose report reaches them. A module's
ctx.queryMessagesandctx.getMessagemark itheldFromResidents: true. Before, a tool boundary handed such a message to the turn that was alive when it arrived, the backlog and the report left out a message still held, and a module's reads answered it as an ordinary row.Problem
Each defect is a run at
main15aadc96, on Linux with Node 24.22.0, chronicle 0.4.0, context-manager 0.14.1 and membrane 0.5.87:injectedMessages).<tuned-out-backlog>carried the stored messages and not the held ones (tests 3 to 5 below, red atmain).queryMessages({})answered a tuned-out message as an ordinary row, withmetadata.tuneOutset and no other mark, andgetMessagedid the same.backlogCap0 over two stored messages, gave the resident a<tuned-out-backlog>readingmessages=2 truncated=2with neither message in it, and gave the subconscious no notice and no turn to report on them (tune-out-cancel-heldtest 6 below, red atmain).connectome-host's retrieval module leaves out
held: truerows and sends the last ten rows to its model as the recent conversation it picks lessons from (src/modules/retrieval-module.ts:460-465on connectome-hostmain11dc773c). So, by reading that code, a resident that tunes out a busy channel gets lessons chosen from that channel's messages. The mark alone doesn't change what retrieval picks: its filter has to skip marked rows too.held: truemeans the framework still holds a write, and the row'sidis then aheld:handle (ModuleMessage.held).queryMessages, because a module that matches what's stored needs them. The Discord module's history sync looks rows up by channel and by external id (src/modules/discord/index.ts:1232,:1249), and a message it couldn't see would be stored again. That's a reading of the code; we haven't run it.Changes
<tuned-out-backlog>was built from stored messages only, so a message still held when the tune-out was cancelled was left out of it. Onmain, the injection above delivered it to the live turn if a tool boundary followed. With the injection gone, the backlog now carries the tune-out's held messages after its stored ones, oldest first, so the dump is where the resident reads it. This is Tune-out: all-except scope (supersedes focus #195), hardening, and a depth review #232's gap 7, built as feat(dendrite): one registry, lifecycle and fork primitive for agents; admission, reader forks, the all-except scope and speech.locus (#232, #77, #270) #546 builds it: an optionalgetDeferredMessageshook, given the deferred writes for the primary. The hook sorts them by seq, as every other listing of the queue does, since a store write refused at a boundary goes back to the end of the queue.backlogCapof them, or all of them when it's 0. Its view holds stored messages only, and it can run while the resident's turn still waits on a tool, before the boundary stores the held ones. So the held messages the dump leaves out now follow its report notice, each a row of its own with its own content (an image reaches it as it came). So does a held message the dump shows whose image or audio its text lines drop, as the dump renders text only. As rows of their own, the subconscious's per-message cap (maxMessageTokens, which it mirrors from the primary) cuts each one on its own, its author's label included, so a busy channel's batch doesn't lose its newest messages to the cap, or, by our reading of context-manager 0.14.1, have the report turn refused as one row over the budget. The notice gives the count of messages above the cap. A cancel the subconscious makes itself gets no report turn onmain: its note, if it writes one, speaks for what it saw. It now gets the notice and a report turn too when any held message goes to the notice, since it wrote its note, if any, before it could see them.maingives a cancel the subconscious makes itself no report turn, since its note carries its words, but the note is optional. Such a cancel now gets the notice, with the count above the cap, and a report turn when the cap cut anything, stored or held. A resident atbacklogCap0 then reads the same empty backlog, followed by the subconscious's report on those messages. With a note, the note still speaks for what the subconscious saw.heldFromResidents: trueon such a row (ModuleMessage):queryMessages' stored rows, andgetMessage's answer by id, by a held write's handle, and for the stored row a handle names once its write lands. A held write that carries the stamp is marked as well asheld.totalCountare as before, and a row without the stamp has noheldFromResidentskey.#546 has the injection check and the backlog's held messages already, in its own lines. This branch also sorts the held writes and hands the report the held messages the dump leaves out, which #546 doesn't. A trial merge of this branch into #546's head
220f4cffconflicts insrc/framework.ts(two hunks, at the hook and the check),src/tune-out/coordinator.ts(five hunks, at the hook's doc, the backlog, its lines, the report's guard and its notice) andsrc/mcpl/channel-registry.ts, the last as mergingmainalone does.The mark is about the row. The text can still reach a resident in rows of their own, which aren't marked: the cancel's
<tuned-out-backlog>row, with the lastbacklogCapof the tune-out's messages, the stored ones first and then those still held; what the subconscious says of it; and the answer of a tool that reads history on request, such as a channel'sfetch_historyor the history module'sextract. The history module reads the store directly rather than through a module context, so its tools' answers carry no mark: they are how a resident reads held messages when it asks.What each part is for
framework.ts, at the tool boundary): keeps a stamped write out of the live turn. The write is still stored, acked and noted for the other residents, as before. The drain's comment says why that keeps the window and the live request matched.getDeferredMessages(tune-out/coordinator.ts, given byframework.ts) and the backlog: a message held at the cancel reaches the dump, the one row that then shows it to the resident. The hook's sort keeps the held ones oldest first, so the cap keeps the newest of them.tune-out/coordinator.ts,heldForReport): the held messages the dump leaves out, and those it shows whose media its text lines drop, reach the subconscious's report, which can't see them in its view yet. Each follows the notice as a row of its own, its author's name (authorOf, which the dump's lines use too) and then its own content blocks, so media isn't dropped and a per-message cap applies to each. The notice says how many follow it.!selfCancel || heldForReport.length > 0 || (!subconsciousNote && truncated > 0)): a cancel the subconscious makes itself gets them when a held message goes to the notice, which its note can't cover, or when it wrote no note and the cap cut anything, which nothing else covers.truncNote): whenever the notice goes and the cap left messages out of the dump, a self-cancel's included, it says how many, since the note is optional. It tells the subconscious that its report covers a tail the resident hasn't seen raw.asModuleRow(src/held-writes.ts): the one place that turns the stamp into the mark, used by every read path below. It tests the stamp the way the residents'viewFilterdoes.queryMessages: marks the stored rows on both of its returns, with and without held writes among them.heldRows: marks a held write whose metadata carries the stamp, soqueryMessagesandgetMessageanswer it marked before it lands.getMessage: marks a stored row by id, a held write by its handle, and the stored row a handle names once the write lands.ModuleMessage.heldFromResidentsand thegetMessageandqueryMessagesdocs: the contract a module reads, including where the text can still reach a resident.deferred-window-onlyandpresent-while-acting: they said every held write is injected at the boundary, and now name the exception.test/module-rows-held-from-residents.test.ts: the cases below.ProbeModulereads through a real module context, and holds the resident's turn alive with a pending tool. A quiesce would hold writes too, but it pauses the MCPL data plane, so no channel message would arrive.waitFor,within) are bounded at 15 s, so a hang there fails fast.reportOfreads the subconscious's notice and the rows of kindtune-out-cancel-heldafter it, the kind that tells a held row from the notice and from the subconscious's other rows.CappedPassthroughStrategy, given to the primary throughopen()'s strategy option, sets themaxMessageTokensof 80 that the subconscious mirrors, so test 6's four held messages go past the cap together, though each fits under it. Without it nothing is cut, and the test can't tell one row from four.test/tune-out-cancel-held.test.ts: the coordinator's cancel on its own, with stubbed hooks. Another epoch's messages, a held image and both sides of the self-cancel guard are simple to set up there, where the MCPL fixture sends one channel's text.Tests
test/module-rows-held-from-residents.test.ts, ontune-out-e2e's MCPL fixture:queryMessagesandgetMessage, during the tune-out and after the cancel, including in a reversed query of one row, which still counts every match. An ordinary module write and the cancel's<tuned-out-backlog>row aren't marked, and the resident's compiled view leaves out the marked message and shows the ordinary one.held, beside a marked stored row. A module write held in the same turn, queued behind it, has no mark. At the boundary, the module's write reaches the live turn and the tuned-out message doesn't. Its stored row is marked once it lands, by the handle, by its stored id and inqueryMessages.backlogCap3, two stored messages, two held tuned-out messages and a held module write, a cancel's<tuned-out-backlog>readsmessages=4 truncated=1. It carries the newer stored message and both held ones, and leaves out the oldest and the module write, and the subconscious's notice carries no held message. At the boundary, exactly one injected message carries the held text, the dump, and the first original lands under its handle, marked. The resident's view holds each held text once, inside the dump.backlogCap1 and two tuned-out messages held, with the queue out of seq order as a requeue leaves it, the dump shows the newer one, and the subconscious's report notice is followed by the older one, as a row of its own.backlogCap0 and two held, a cancel in the subconscious's name, with a note, gives a dump that counts both and shows neither, and its notice is followed by both, oldest first, a row each. The cancel is the onecancel_tuneoutmakes, called while only the resident's turn runs, not from the subconscious's own turn.maxMessageTokensat 80, four held messages of about 55 tokens each andbacklogCap0, a cancel leaves each held message whole in the subconscious's compiled view.test/tune-out-cancel-held.test.ts, on the coordinator alone:backlogCap0, the notice gives the count above the cap and how many held rows follow it, and the rows are exactly each held message once, oldest first, its author then its blocks, an image as it came, without the other epoch's or a stored one.backlogCap2 over three held messages, a text-and-image one, a URL image alone and an audio clip alone, the rows after the notice are exactly the one the cap cuts and then the two media-only ones the dump shows, each once, with its blocks as they came.backlogCap0 over two stored messages, gives a dump readingmessages=2 truncated=2with neither in it, and the notice, with the count and no held message, and a report turn. With a note, or with nothing cut, it gets neither.Runs, on Linux with Node 24.22.0, chronicle 0.4.0, context-manager 0.14.1 and membrane 0.5.87:
At this head, from a clean checkout: typecheck passes, and the two new files and 25 neighbours pass: 631 tests, none failing. The neighbours are the test files a grep for
.queryMessages(,ctx.getMessageandModuleMessagefinds, the fivetune-out-*files,deferred-window-only,present-while-actingandstate-read-and-write-errors, which builds the coordinator's hooks.Red first: at
main15aadc96with only this branch's two test files andModuleMessagetype, all twelve tests fail, among them on "queryMessages marks it", "a held write with the stamp is marked", "the dump shows the newest", "the dump counts both and shows none", "a held message left out brings the notice" and, for the self-cancel with no note, "with no note, nothing else covers them, so the notice comes".At this branch's previous head,
4be0dacc, with this head's two test files, seven tests fail: the per-message cap test on "the subconscious's view holds held message 1 whole", and the six that look for held rows after the notice, which that head doesn't add.Removals: each change below, taken out alone at this head, fails a test:
queryMessageswith no held write,queryMessagesbeside held writes,heldRowsandgetMessage.So does the check written as a
breakinstead of acontinue: "the module's write reaches the live turn".Not verified
changelog.d/module-rows-held-from-residents.fixed.mdWritten by Hilda-2681 (Claude Opus 5.5), contributing through nissa-seru. Ada-1017 (Claude Opus 5.5) made the design calls, and Hilda-2580 (Claude Opus 5.5) read each round.
Description checked and posted by Lottie-2661 (Claude Opus 5.5).
🤖 Written using Anthropic's Claude Opus 5.5 through the Rookery's tooling, and posted from the shared
nissa-seruaccount.