Skip to content

fix(tune-out): entering a tune-out keeps the subconscious's window anchor at an all-except hold in force - #933

Open
nissa-seru wants to merge 2 commits into
anima-research:fix/tune-out-resume-late-channelsfrom
nissa-seru:hilda/916-additions
Open

nissa-seru wants to merge 2 commits into
anima-research:fix/tune-out-resume-late-channelsfrom
nissa-seru:hilda/916-additions

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

On top of #916, entering a channel's tune-out while an older all-except hold is in force now leaves the subconscious's window anchor at the hold's start, and docs/dendrite.md's tune-out paragraph says when the coordinator exists and what a restart resumes. In this pull request's new test, a hold entered at sequence 3 and then a channel's tune-out entered at 20 give the anchors [3, 20] with #916's coordinator at 9704133265, and [3, 3] at this head. main's coordinator at 48f26ce56c gives [3, 20] too.

@tel-0s, this pull request goes into #916's branch, fix/tune-out-resume-late-channels, and adds two commits to it. We're asking you to merge it there, so the anchor fix and the paragraph land with #916's fix.

Problem

At 9704133265, enter() sets the subconscious's window anchor with these lines (src/tune-out/coordinator.ts:678–682):

const oldest = this.listActive().reduce(
  (min, e) => Math.min(min, e.params.startedAtSequence),
  params.startedAtSequence,
);
this.hooks.setSubconsciousAnchor(oldest);

With #916's coordinator in place of this head's, the new test, "a tune-out entered while an older all-except hold is in force leaves the subconscious's window start at the hold's start", fails at its last assertion: the anchors are [3, 20] where [3, 3] was expected. The hold's start is 3, and the second anchor is the new tune-out's start, 20.

Changes

  • enter()'s reduce (src/tune-out/coordinator.ts:678–681) starts from scope ? Math.min(params.startedAtSequence, scope.params.startedAtSequence) : params.startedAtSequence, where scope is this.getAllExcept(), which enter() already reads (:649). The comment above it now reads "Never move the anchor past the start of an epoch still in force, a tune-out's or the all-except hold's."
  • test/tune-out-resume-late-channels.test.ts gains that test, over the real ChannelRegistry and a JsStore. Its coordinatorOn takes what the coordinator's currentSequence hook answers as an optional fourth argument, whose default is the () => 9 the hook had.
  • docs/dendrite.md's "Tune-out without a reader" paragraph (:278), in a commit of its own, e2dc0af961, on top of the fix in 65e58aa35a, so it can be dropped whole. Beyond the sentence for fix(tune-out): resume a tune-out on record whose channel isn't registered at the start #916's own change, "A restart resumes every tune-out on record, …", it replaces two sentences that main's own tests contradict. At a clean 48f26ce56c, "a tune-out on record at a start with no subconscious ends there, …" and "a tune-out on record when a start without servers connects one later ends then, …" (test/tune-out-transport.test.ts:1419 and :1448) pass (Tests). In each, MCPL comes up with a tune-out on record, the tune-out ends, and the log line the test asserts says "nothing on this host watches or cancels tune-outs". The paragraph's changes:
    • "The tune-out coordinator exists whenever MCPL channels do." becomes "The tune-out coordinator exists when create() brings up MCPL channels and a reader is configured or tuneOut.enabled is set, and residents are offered tune_out only where it exists."
    • "A persisted epoch is therefore always resumed and released, whatever the configuration (gap 10)." becomes "Where the coordinator exists, a persisted epoch is resumed, and released when it ends, with a reader or without one (gap 10)."
    • Two sentences are added: "A restart resumes every tune-out on record, whether or not its channel is registered yet, since a host's servers may register their channels after create() returns (#900)." and "Where there is no coordinator, as where no reader is configured and tuneOut.enabled isn't set, or where MCPL first comes up after create() with connectMcplServer(), each channel's tune-out on record ends when MCPL comes up, and nothing delivers its backlog."
    • The paragraph's last sentence before this change, "Residents are offered tune_out when a reader is configured or tuneOut.enabled is set.", is cut, and the new first sentence says where residents are offered tune_out.
  • changelog.d/tune-out-anchor-hold-in-force.fixed.md: "Hosts with a subconscious: entering a channel's tune-out while an older all-except hold is in force no longer moves the subconscious's window anchor past the hold's start."

With #844

@tel-0s, #844 (c7011d5c7a) merges with #916's head with no conflicts, by git merge-tree on October 30 at 08:08 UTC. With this head in place of #916's, it conflicts in src/tune-out/coordinator.ts, in one block: enter()'s window anchor, where #844 calls anchorSubconscious() and this head has the reduce under Changes. Each of the other 39 pull requests open then merges with this head as it does with #916's.

Tests

At a clean checkout of e2dc0af961, on Linux with Node 24.22.2 and npm 11.19.1, after npm install with package-lock.json and node_modules removed. npm ls --depth=0 gives @animalabs/chronicle 0.5.1, @animalabs/context-manager 0.24.1, @animalabs/mcpl-core 0.4.0, @animalabs/membrane 0.8.0, @babel/parser 7.29.12, @modelcontextprotocol/client 2.4.0, @modelcontextprotocol/server 2.4.0, @types/node 22.20.5, @types/ws 8.18.2, chokidar 4.0.3, discord.js 14.27.0, tsx 4.23.15, typescript 7.0.2 and ws 8.22.0. npx tsc --noEmit, npm run build and npm run pretest each exited 0. Each test file ran alone, with the flags npm test gives node and the TAP reporter:

At a clean checkout of main at 48f26ce56c, set up the same way, with the same versions, npm run build and npm run pretest each exited 0, and test/tune-out-transport.test.ts, which this pull request and #916 leave as main has it, passes 60 of 60.

Not verified

  • Platforms other than Linux, and this repository's CI, which hasn't run this change.
  • The rest of the suite. We ran the 46 test files Tests names, of the 391 in test/.
  • The subconscious's window. The test asserts the sequences the coordinator passes to setSubconsciousAnchor, not what the window then holds.
  • The paragraph's account of when the coordinator exists, and of a host without one. No run here started a framework, with a coordinator or without one.

  • Changelog fragment added: changelog.d/tune-out-anchor-hold-in-force.fixed.md.

Hilda-2681 made this change and ran it red first. The anchor fix and the paragraph first went into an earlier change of ours for #900, which Crispin-2406 read. Naomi-2422 read this pull request. Fern-2426 ran our internal review of #916. Daniel-2612 repeated the runs at a clean checkout and wrote this description.

Hilda-2681 added 2 commits October 30, 2026 02:05
enter() set the subconscious's window anchor to the oldest start among
the tune-outs on record and its own, and left out an all-except hold in
force, which the start's resume counts and enterAllExcept's own start
stands for. Entering a channel's tune-out while an older hold was in
force moved the anchor past the hold's start, though the comment above
the reduce said never to move it forward there, and the hold's earlier
rows left the subconscious's window. main has the same gap. The reduce
now starts from the older of the new tune-out's start and the hold's,
and its comment says what it keeps.

Test: a hold entered at sequence 3, then a channel's tune-out at 20,
over the real registry and store, leave the anchor at 3 both times. The
test file's coordinator takes the sequence as an optional argument for
it. Before this change the second anchor was 20.
…resumes

docs/dendrite.md's tune-out paragraph: the coordinator exists when
create() brings up MCPL channels and a reader is configured or
tuneOut.enabled is set, and residents are offered tune_out only where it
exists. Where the coordinator exists, a persisted epoch is resumed, and
released when it ends, with a reader or without one, where the paragraph
had said it always is. A restart resumes every tune-out on record,
whether or not its channel is registered yet. Where there is no
coordinator, as where no reader is configured and tuneOut.enabled isn't
set, or where MCPL first comes up after create() with
connectMcplServer(), each channel's tune-out on record ends when MCPL
comes up, and nothing delivers its backlog.
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