Repository navigation
Slim the test suite, and treat an undeclared supportsInjectedMessages as false - #934
Merged
Merged
Conversation
added 30 commits
October 30, 2026 08:31
…tands test's opening "initiallyOpen at first sight registers open with server-bootstrap provenance" made the same three steps as the first three lines of "an agent decision to close is never overridden by initiallyOpen": a fresh registry, ensureInitialDesiredState with initiallyOpen, and a desired state of 'open'. It asserted nothing about the provenance its title names; the other tests in the file show what provenance decides by its consequences. Cover unchanged.
…s assertion - "a version verdict gets no reconnect stub even with reconnect: true": "a version verdict is never retried: one launch, and that child is reaped" makes the same connectWithReconnect call with reconnect: true on a rejecting server and asserts the same McplProtocolVersionError rejection (no stub), then also counts the launches. - "a modern stdio configuration is refused before anything is spawned": "a modern configuration is refused by the legacy engine before anything is dialed or spawned" makes the same connect with protocol 'modern' on a stdio configuration, expects the same refusal, and checks that the fixture never wrote its start log. Cover unchanged.
… duplicate and a check that can't fail - "a turn asks membrane for no retries of its own: the framework is the one retry layer": removed. "a failed turn is retried by the framework on the base schedule, each attempt a fresh stream with no membrane retries" asserts retry: false on every stream the framework sends, the failed first one included. Cover unchanged. - The overloaded schedule test no longer sums its own steps array, which couldn't fail; the per-retry ranges carry the schedule. - afterTurn's "held" reads the agent's providerAdmission.cooldownReason from the public healthSnapshot() in place of the private providerAccelerationCooldowns map. Every hold sets a reason, so the two agree. "an acceleration 429 a stream throws after its answer installs no hold" and the later-event comparisons read it this way now.
…s, not the steps it takes - "settling a held write whose landing is unknown, not found, persists the queue without its mark, as an edit does" reads the durable queue from its documented place, <storePath>/recovery/deferred-writes.json, and checks the entry there has no landing mark, in place of counting calls to the private persistDeferredWrites. - "at a tool boundary, a sharded body stored whole is carried as the next compile renders it" no longer counts calls to the private storedCopiesOf, which is the boundary's own work; that the injection equals the next compile's rendering is the claim, and stays. - "a module's edit or removal by a stored id settles the held write that row is a copy of, even while its store can't be searched": removed. "a module's edit or removal by a stored id, while the store can't be searched, settles the write through the copy it names" has the same setup and calls and the same two assertions, and also checks the log line. Cover unchanged.
…f the first test - "the published traces are emitted when the channel's server takes no stream" no longer asserts that no chunk was sent: the harness's streaming: false registry has no chunk method, and its proxy answers any missing method with a no-op that logs nothing, so h.chunks() stays empty whatever the framework does. The traces assertion that follows is the claim, and stays. - "a published trace carries no message id when the server returned none": removed. The first test runs the same harness and script and compares the traces with node:assert/strict's deepEqual, which fails on any messageId key, so it already shows both inference:published traces carry none. Cover unchanged.
"recipe private routes zero same-round prose for the exact Tilde shape" ran
the same membrane script (a tool round with the Tilde preface, then an empty
answer), the same createFramework({ policy: 'private' }) and the same
trigger as "private keeps the preface in Chronicle while withholding it from
channel routing", and asserted the same deepEqual(routed, []). The kept test
also checks the preface stays in the compiled context. Cover unchanged.
…vers "a room smaller than the dump's frame shows no message and counts them all" checked room 10 over the five-message backlog. "at every room, over six backlogs, ..." runs that backlog at every room from 0, room 10 included. With the frame longer than the room, its branches allow no line there: a cut line must fill the room, and whole lines must leave the dump within it. So it shows no line, and it checks that truncated= counts all five and that the report is told of all five. Cover unchanged.
…entries and a typing stop's form - "cleans logical-turn state when an ephemeral startup watchdog fires" and "conversation disposal reclaims logical-turn state": removed. Each asserted only that an entry was gone from logicalTurnToolCalls, a WeakMap keyed by the agent, whose entries live as long as the garbage collector lets them; no caller can tell when one leaves. The startup watchdog's rejection is pinned in other files. - "returns empty speech to ephemeral callers for a contained wrapper" keeps its claim, the empty speech returned, and drops the same WeakMap checks. - "successful ephemeral completion stops typing and finalizes outgoing after caller disposal" accepts a typing stop for the run's channel in either form: a stop with no channel, which covers it, or one naming world:test. Which form the framework uses is its own choice.
…-API spy and the tests' own limit wiring - "the bin load hook goes in through registerHooks where Node has it, and through register where it doesn't": removed. It spied on which node:module call installs the hook, not on what the hook does. What the hook delivers, that a bin's own code never runs and an ES module bin's imports load, is pinned by this file's bin tests, and CI's packed-release job runs the check on 22.12.0 (register) and 24 (registerHooks), where a hook that didn't go in would let the bin start. Preferring registerHooks where a Node has both is the script's own choice (its comment gives Node 26's deprecation of register as the reason). - "check() runs the script's npm under the tests' npm limit, short of the runner's own timeout" becomes "an npm pack that fails without timing out fails the check, pointing to npm's own output": it keeps the only assertions of that report (exit code 1 and "npm pack failed (its output is above)."), and drops the checks of the tests' own wiring (the limit the shimmed npm was started under, and that limit against npm test's timeout).
…ll sites in src "every state the framework registers goes through ensureStateRegistered" walked src and counted the lines calling registerState, expecting the one in module-registry.ts. That checks the code's shape, not what it does. What a registration written around the helper would break, a failed registration taken for "already exists" or lost, is what the rest of this file pins for each state the framework registers today. A registration site written outside the helper later would meet no test here.
…xt grant admits McplServerConnection is exported, and on(), establishGrant() and ready() are public. Where a test read the private bufferedEvents to show what a transport boundary kept, it now opens the planes under a grant and checks what the flush admits, as "readied between transports, as a gate readies every connection, ..." already does. A new helper, admittedIds, opens both planes and returns the admitted messages' ids in order. - "keeps the kept bound where boundaries join with no flush between, keeping the earliest": 10,000 admitted, m100001 first and m110000 last. - "starts the next pause's counts afresh after a flush": the second flush admits 1,000, the oldest (m30001) gone. - "keeps notifications out of the kept bound where boundaries join": 10,005 admitted, 5 of them with no responder. - "a control-plane flush keeps what a boundary kept out of the next transport's budget": nothing is admitted on the control plane alone, and all 10,001 are admitted once the data plane opens. - "holds the next transport's notifications beside 10,000 kept requests, dropping none": 10,005 admitted. - "frees the budget of the requests a transport boundary drops: ...": only the next transport's message is admitted, and none of the dropped inference requests. - "a transport boundary crossed during a flush holds the rest of the snapshot to the boundary rule": after the reconnect, the next grant admits the push and nothing else. - The admission-order test no longer calls the private static admissionOrder on hand-built records: a removal named by an object whose string form throws is sent through the file's kept-message setup, and the flush still moves the registration ahead and admits the kept message.
The shared helper named the policy an event matched by diffing the private eventGate.getStatus() match counts, and two tests read sampling and rate-limit state from it. The gate emits a gate:decision trace for every event it weighs, naming the matched policy and whether it triggers, and the framework passes it to onTrace, where an operator sees it. The helper now waits for that trace, checks there's one decision and that a policy matched, and returns the policy's name. - "a channel's message, a reaction and a voice report there share one sampling counter under keyBy channelId": with every: 3, the third event in the channel fires, so the triggers read false, false, true. - "a rate limit keyed by channelId counts a channel's message and reaction in one bucket, whatever its policy matches": with one token, the message passes and the reaction is denied, so the triggers read true, false.
… ops:alert trace Eleven tests replaced the private opsAlert to capture what it was called with. opsAlert emits an ops:alert trace, with the alert's kind, agent, message and data, on every call, before the webhook's cooldown, and the framework passes it to onTrace. These tests now listen for that trace: "routes a maintenance failure through the ops-alert pipeline", the four tests of one agent's failure staying that agent's, "a failure that lasts is logged and alerted once, and again once the agent has recovered", "a failure that lasts is alerted again when its whole text changes, ...", the two readiness-refusal tests, and the two tests of a pass under a surgery hold. "a maintain begun from an alert a pass raises while it prepares joins that pass" starts its maintain from such a listener, as its own comment already described it. The trace's message is bounded, as a webhook's is, so the test of a long text alerted again after the cooldown compares the repeat with the change's own alert rather than with the unbounded text. - "drains without a new message and refreshes tools before ticking" no longer pins the number of tools a tick receives (six, which moves with the framework's own meta-tools); it checks the module's tool is among them, that every tool the agent may call (getAllTools(), under allowedTools: 'all') is too, and that both ticks receive the same set. - "a pass that ends before its agent is ready records what is still pending" no longer pins the pass's fixed tick count; the pending state is the claim.
…floors Seven places skipped the shared-inheritance and reader-fork tests when the installed chronicle lacked JsStore.view or context-manager lacked ContextManager.derive: compression-after-release, dendrite-derive, dendrite-fork-view, the reader-fork rig (and the three tune-out-reader files that took its skip), tune-out-reader-forks-e2e, tune-out-reader-epoch-shared and turn-inputs-pin. The floors are chronicle ^0.5.0 and context-manager ^0.24.0, and between them the floor versions, 0.5.0 and 0.24.0, declare view, both selective forks (createBranchWithStates, createBranchAtWithStates), derive and supportsDerivation. Installed at those versions, supportsDerivation holds for a fresh store, every guard reads supported, and the eight files run 52 tests with none skipped, the same with the guards forced on. turn-inputs-pin, whose guard came later, runs its 16 tests at those versions with none skipped once its guard is gone. A rig that lacked them would be an unsupported install, where a failure says more than a skip. The guards, the skip options, the header lines that described skipping, and the imports only the guards used go. Refs anima-research#886
- XML tool mode: a round's persisted text is its roundPreamble alone. The
fallback to the cumulative preamble served membrane before 0.5.79, which
sent no roundPreamble; the floor is 0.8.0, whose XML path always sends
it. The test that pinned the fallback ("legacy membrane without
roundPreamble keeps the cumulative fallback (pinned)") goes with it, as
its own comment said it would once the floor passed 0.5.79, and so does
its mock's switch for leaving roundPreamble out, which only that test
turned off. The branch's comment now says it is XML tool mode's, and keeps
why it reads roundPreamble (membrane's XML preamble is cumulative)
without the incident's history; xml-round-persist's header says the
framework persists roundPreamble alone, and its stream declares
supportsInjectedMessages false, as the XML path does. The turn-end
routing comment and agent-allowed-tools' header, which named an older
membrane's stream with no roundContent, now name the XML tool path.
- Discord awareness outbox: a version 1 ledger no longer gets a refusal of
its own. Only an unreleased commit wrote version 1, and any released
build rewrites such a file as version 2 at its next write; one that
remains is refused as any version but 2 and 3 is. Its test goes, and
the README's sentence on it no longer says its error tells what moving
it gives up.
- ReferenceTestimony.truncatedFields is gone: the parser set it to an empty
list and nothing ever added to it, and the type isn't exported from the
package. The test lines that checked it was empty go with it.
- deriveAgent's doc names the check that decides shared inheritance,
ContextManager.supportsDerivation, in place of "branch-bound store
handles", which no longer said everything derive needs.
…private rates The expected stamps embedded the estimator's own rates (2.9 and 2.3 characters per token), private constants of the module, and some checks compared object identity where the claim is about content. - "stamps the residual of output tokens over visible blocks on the signed block": with nothing visible beside it, the signed block carries every output token; with text beside it, the text is taken out, and the rest of the output tokens are the residual (a thousand fewer output tokens stamp a thousand fewer). The text block carries no stamp, and the input isn't mutated. - "splits the residual across several carriers by signature length": with nothing visible, 4,000 splits 3,000 and 1,000 by the signatures' 3:1; with a tool_use between them, the tool_use takes no share, its estimate comes out first, and the carriers still split 3:1. - "leaves unsigned thinking, already-stamped blocks and non-positive residuals alone" and "drops the stamp from each top-level block, ..." compare by content, not by object identity; the blocks handed in keep their stamps. - "stamps each round from that call's own output tokens (membrane usage is cumulative)" computes each round's expected stamp through stampThinkingTokenEstimates from that call's own output tokens, and checks the cumulative count would have stamped more.
…est mock declares true membrane's yielding stream, the only one the framework continues across tool rounds, declares on every tool round whether it carries messages injected with the round's results: true on its native path, false on its XML prefill path (membrane 0.8.0, the floor). The framework still gave an undeclared stream its own mix: it drained and injected at the tool boundary (!== false), didn't mark the injected messages read (=== true), and counted the round as presenting them (?? true). No stream the framework continues across tool rounds takes that path. For a boundary with a stream, all three now read the declaration as === true: a stream that doesn't say is taken as one that doesn't carry, and what is queued for the agent waits for the turn's end. The README's paragraph on unread messages says so, and the comments and docs that described a stream that doesn't say (the AgentState field, the consumed watermark, isUnread, the deferred-writes window, releasedWindow and the module context's note on writes to a run or fork) now don't. The direct API (Agent.runInference) reaches the framework's tool boundary with no stream and no declaration. That boundary drains as before (=== true || !stream): no stream carries what it stores, and the re-inference that follows compiles from the store. A round whose result ends the turn runs no re-inference, so without it the clock row due after the round's results, and a write queued while the host was quiesced, wouldn't be stored there; clock-rows and deferred-window-only each have a test of that route, which fails with the condition taken out. The test mock was the one stream that didn't say: MockMembrane and MockYieldingStream left the declaration out unless a test passed it, so most of the suite's mid-turn injection tests ran the undeclared mix rather than the native path production takes. The mock now declares true by default, as membrane's native path does; a test that passes false keeps the XML path, and 'undeclared' leaves the field out, which deferred-window-only's test of a stream that doesn't say uses, and so does prose-drafts' test that such a stream's held reply gets no mid-turn notice and is named in the turn's receipt. The tool rounds tests build by hand now declare the path they model: true where the round carries roundContent, as membrane's native path does (end-turn-usage, framework, same-round-think-policy's paused stream and tool-wrapper-prose-guard-integration), and false on the XML path (skip-reply-wake-xml-mode). same-round-think-policy's stream with neither roundContent nor roundPreamble, a shape no membrane round takes, stays undeclared. exhausted-turn-rearm's hand-built tool round declares true for the same reason: its test pins the native path's re-arm. On membrane's XML path (supportsInjectedMessages: false), a message said while a turn runs is stored when the turn ends, after its exhaustion decision, so an exhausted turn isn't sent again; the message's own inference request then runs a turn that carries it. Changed or removed with the reading, since they asserted the undeclared mix itself: - deferred-window-only: "a stream that doesn't say whether it carries injections gets them, and they stay unread" becomes "... is taken as one that can't: what is queued waits for the turn to end" - module-query-for-agent: "on a stream that doesn't declare whether it carries injections, an injected write counts as unread, so the primary gets a copy, as for a notice" goes (its true and false siblings stay) - route-failure-targeting: "passes on, by name, a notice injected into a stream that doesn't declare it carries injected messages" goes (its true sibling stays)
…budget's arithmetic or private steps
- "edit refuses a problem in its own input before it looks its path up on
disk" no longer counts calls to the private parseFilePath: an invalid
input on a path that doesn't exist gets the same refusal as on one that
does, and the missing path's own refusal, with a valid input, differs.
- "every entry is set in one record: ..." no longer pins where a long
failure is cut (491 characters, the bound's arithmetic): the refusal
keeps its whole lead and ending, the ellipsis names the failure's whole
1016 characters, and what's kept is a head of the failure.
- The three floor tests ("a call counts occurrences its edits don't need,
...", "an edit matched with quotes read as equal, or read without its
CR, ...", "a crowded edit whose count stopped short of its ask, ...") no
longer pin the floors 43, 85 and 561, which only the count budget's
arithmetic gives: each refusal names the edit by a count that's a floor,
above the 20 shown and below the real count or the ask, and states that
same count as the floor. The at-the-ask case asks for the floor the first
call reported.
- "every path is checked before any entry is set: ..." no longer counts the
private barriers; that neither file is stored, and the error says the call
stopped before storing, are the claim.
…s arithmetic Four tests pinned exact counts that only the budget's current allocation gives (21 free, then 21 more and one: 22, 43, 61, and the 199,997 a crowded problem names). Each now states the requirement those numbers illustrated: - "a call counts occurrences past what its edits need only within its budget, and past it a count is a floor": the first edit's exact 5,000,000 stays; every later count is a floor above the 21 a refusal shows; and 200 such edits together count no more than the budget with a small allowance an edit, not 200 times the text. - "a crowded edit whose count the budget stopped short of its ask, or at it, ...": the third edit's count is a floor above 21 and at or below its ask; the at-the-ask edge asks for exactly the floor the first plan found. - "one count budget raised for a text longer than it is raised once for each length, ...": the second text's count is a floor below its length, since the budget isn't raised again. - "crowded edits whose counts stopped short, where the crowded check names none, ...": one crowded problem names every edit without a count problem, the two name all 200,000 once, and only a few edits get count problems; the line-feed search bound stays.
…ept counter would fail them - "keyBy partitions counters — busy channel does not affect quiet one": A's third message fired and reset a shared counter before B's first, so B's assertion held either way. Now two of A's come first, then B's, which would be the third on one shared counter, and then A's third. - "rate_limit keyBy changed: old keyed buckets do NOT carry over" and "passive_sample keyBy changed: per-key counters cleared": the event after the reload had a different key from the old state's, so a kept bucket or counter would have passed too. The old rule now keys by userId and the new one by senderId, and the event after the reload carries the old key's value, K, as its new key, so only a cleared state passes. The old rule can't key by channelId for this: that keys by the channel the gate routes an event to, not the field's value. - "trace event lists policies whose state was cleared": no policy removed may read as an absent or an empty removedPolicies.
…and look for any file moved aside - "the host's starting rules are read rule by rule: a first start writes them as given ...": the written gate.json parses to the rules given. Its exact serialization (two-space indent, a trailing newline) is the code's choice. - "a gate.json saved with a BOM reads as the same file without one: ...": after the wake_add_rule, the directory holds the same entries as before, so the file was written in place. Looking only for names under the 'gate.json.unreadable-' prefix would miss a file moved under another name.
…hot, and drop a stop() half no source states - The shared continueTurn helper checks that a capacity hold holds the turn through healthSnapshot()'s providerAdmission.cooldownReason in place of the private cooldown map. Releasing the hold at its deadline still goes through the framework's own release, as the hold's timer would. - "a settle closes the open logical turn only when it is the settling stream's own, and stop() clears open turns" keeps its first half, under the title "a settle closes the open logical turn only when it is the settling stream's own": the CHANGELOG says a logical turn stays open from the start that opens it until the turn settles, so a settle from a stream a newer one replaced leaves the newer turn open, and the turn's own settle closes it. Its stop() half goes: no source a caller reads states that stop() clears open logical turns, or that it keeps the live routing state.
…hows the agent - "is told by its own fields, and a resource with a uri of its own stays a reference" read every case through classifyBlock's record, which isn't exported and is the classifier's own. Each case is now read as the push lane shows it to the agent: the embedded text as text, the embedded image as an image, a resource with a uri of its own as a reference stub that doesn't carry the uri, a record with no string uri as the "no link" line, a disposition as withheld, and a media type kept as sent whatever its case. - The empty-link tests drop their classifyBlock records; what each lane shows is asserted beside them. - The marker test no longer compares its own constant with a copy of the same literal.
…assifier's record - "withholds inline data beside a uri that is not a string ..." drops its check of classifyBlock's record (not exported); the history text it shows, a placeholder with no uri, is asserted beside it. - "governs media given a uri that is not a string, ..." shows the bare media block through the push lane, which reads it as it does a block with fields that aren't strings: the "no usable data or link" line. (The history text writes a bare block with no data and no link as its JSON, which carries nothing to withhold.)
The shared heldRestartMeets helper checked that the restart was held by the size of the private cooldown map. It now reads healthSnapshot(), where the held agent's providerAdmission carries the hold's reason: the one agent, assistant, is held, and still held after the event arrives. Releasing the hold at its deadline still goes through the framework's own release, as the hold's timer would. Three tests use the helper: a held restart meeting a message, a channel message, and an ambient one.
…uard's audit record - "settles the tool-result batch and the open tool round a fork still holds when stop() ends it" no longer spies on the guard's abandon. The requirement is that a batch a stopped fork held settles conservatively, withheld rather than admitted; the guard's audit record, which docs/tool-result-guard.md documents for operators, has the fork's batch withheld, with the reason 'aborted' that stop() gives. - "tells the primary of each failure notice a live fork never read when stop() ends the forks, ..." compares the forks' disposal traces as a set: no requirement fixes the order in which stop() ends them. - "resumes past a run that ended while the host was quiesced: ..." reads the quiesced flag through the public getHostModeStatus().
…they show
- "retract('all') takes the refs of every batch not retired and every
reaction the ledger holds, ..." compares the due removals as a set: their
order follows the ledger's entry creation, which nothing states.
- "a bounded text never splits a surrogate pair at either cut" checks that
requirement directly, with no lone surrogate at either cut, a head and a
tail of the text, and the omitted count equal to what lies between, in
place of the exact head width the cut's arithmetic gives.
- "one drain pass per server: ..." no longer checks that the second call
returns the same promise, which is how a join is done: nothing more
leaves while the reply is out, and the first pass delivers both, so the
request made owed joined it.
…and read alert bounds on the trace - "an over-budget phrase quoted inside a classified provider error does not kick the drain" and "a reason that is not a string is described whole, ...": the drain's exact tick count (8) is the drain's own. The claim is that bounding a reason for presentation never changes the decision, so a long reason's ticks equal a short one's with the same phrase, and that one kicks the drain at all. - The two alert-data tests read the bound and projection checks from the ops:alert trace alone. Their 'failures.log' sink was the argument the harness captured before logFailure applies its own bound, so it showed where in the code the bound happens; "logFailure bounds every field it writes" pins the written record. The small records' shapes stay, and the one title that named the failures.log record names the trace alone.
…ccessor "module utilities are callable tools out of the tool list, and utils is gone" popped the array the private getAllUtilities returned and checked the framework's list was unchanged. No caller holds that array, so its copy semantics are the accessor's own. The utilities being callable and out of the tool list are the claim, and stay.
"the interrupt stops a spinning script for good" asserted the interrupt
was asked more than three times. An engine that reported 'interrupted'
without asking its interrupt would fail that, and it fails the file's two
checks === 101 as well ("the slot was asked again after it said stop" and
"interrupt() was asked no more once it said stop"), whose scripts only an
interrupt that answers at its 101st asking stops. The interrupted outcome
is this test's claim.
…s by a restart, and counts what the service took - "re-review anima-research#1: a 404 from /delete ... drops the removals instead of retrying forever": the check after the last pass that nothing was left pending read the module's saved state through a cast; it now stops the module, starts a new one on the saved state, syncs, and checks no /delete was called. The check that a later removal is dropped as it comes stays as it was: that is the stated requirement through the module's state, and a restart at that point would hand the rest of the test to a module that hasn't met the 404. - "a removal before this store's index is made ...": the pass's whole call sequence, stats then upsert, is no doc's; it now checks that /stats was asked and no /delete was called. - "G2#1 pending changes are restored and persisted when start() runs before bind()": the check that a new edit was persisted read the saved state through a cast. It now stops the module, starts a new one on the saved state, binds it and syncs, and checks the index holds the edited text. The edit is of a third message, o, a second after p: q is the newest message, inside the overlap the next pass re-scans, so its text would reach the index whether or not the edit was kept, and p is already in the state the test starts from, so a pending set never saved again would still name it. - "G2#5 queued edits spend the catch-up budget": the changelog makes the budget the most items one pass writes, as the service reports them, each queued edit counted as one. The test read the module's own count of what it pushed, which can't catch a pass that writes more than its budget while counting less. It now counts the items the service holds with edited text after the budgeted pass, at most 100.
… its stated parts CHANGELOG says a conversation fork's or an ephemeral run's sleep is refused, with a result saying that nothing was set, and why: the gate's sleep quiets the whole agent. "a subagent's sleep is refused, and the gate stays awake" matched the refusal's whole text; it now checks the parts the changelog states: one refusal of sleep, saying nothing was set and that a sleep quiets the whole agent. The check that the gate's sleep state stays null keeps: it is the refusal's "nothing was set" through the gate's own encoding.
…'s note record "a send into a channel the agent closed says so, what still reaches the agent, and how to open it, until they turn the note off" checked, after agent_settings turned closed_channel_note off, the stub state's private closedChannelNotes record, a shape no doc names. The checks around it show the setting through agent_settings and the send's answer, and another test shows it kept across a real restart through agent_settings, so the record check goes, and the test no longer takes the harness's state reader.
…hangelog states - The coordinator's refusals are matched by what the 0.22.3 entry on them states, in place of their whole sentences: each says what didn't happen and what to do instead; tune_out on a channel already tuned out gives its settings, how to change them, and its end time where it has one; a cancel of a channel that isn't tuned out names the connected channels that are, how a tune-out ends by itself, and whose window its backlog goes to, "you" to the resident and "the resident" to the subconscious; a fork-bound channel says why tune_out won't divert it, and that calling again won't change that until its conversation ends. A refused cancel with no connected channel tuned out names no channel. - The refusals of an id two servers' channels share, and of the turn's channel once its server withdrew it, are matched by what the 0.27.0 entry on shared channel ids states: a shared name is refused with each candidate named by its server-qualified id, and that form is the one to call again with; within a turn, once that channel's server no longer registers it, the call is refused. Each also says what didn't happen, and the withdrawn one until when. The subconscious's wake reasons are checked for naming a:general, which the same entry states. - The alreadyTunedOut marker leaves the two comparisons that held it. It is the coordinator's signal to the framework's dispatch, whose stated outcome, that tune_out called again for a channel already tuned out tries the open again where its transport may not be open (0.24.0's entry), tune-out-transport pins through the tool itself. - cancel_tuneout inside the subconscious's turn runs the coordinator's own cancel, where it ran a stub put in its place, and the test reads which tune-out ended and which stands from the registry's getTuneOutState. - The file header no longer says the refusal is marked for the dispatch, which the file no longer checks. Checked by mutation: with the marker gone from enter's refusal, tune-out-transport's "... tries the open again, and opens it" fails waiting for the open, which the dispatch never tries; with one tuned-out channel left out of the cancel's list, the end time dropped, the turn's id read as the first-registered server's channel, the cancel wake's reason naming the bare id, the withdrawn refusal reworded, the shared-id refusal naming no candidates, or the backlog always going to "you", the rewritten checks fail.
… the close trace and refused connects - "a hold releases only its own claim": the checks of each hold's previous promise, and the setImmediate wait for the second to run, go. How a hold chains to the one before is the code's own shape. What the changelog states stays: after the first release, a connect is refused while the second disconnect's hold is held, then admitted. - "a modern connect is refused while a legacy connect ... is still handshaking": the loadHostStates wrapper and its "the refused connect read no trees" check go. That is the order of the connect's steps, not something a caller meets. The refusal and "the modern server was never launched" stay. - "modern, without reconnect: a server lost on its own holds its id until its child is reaped, then leaves": the reads of the private server map go. The refused connect shows the id is held while the reap runs; the test then waits for the server's tools to leave the list, checks its child had exited and that nothing launched beside it, and shows the id is free by connecting the server again. - "stop() closes every connection, keeps the ones it cannot reap registered ...": in place of the private server map and registry, the test reads mcpl:server-closed. The failed stop confirms neither close, and the retried stop, which the changelog says retries the reap, traces one close for each server. - "a server connected while a refresh waits keeps its own tool prefix ...": the count of tools/list calls, and "the dropped refresh started no other", go; one more refresh is one more listing, which no caller is promised against. The gate is lifted by restoring the real listTools. - "a refresh in flight when a server restarts under the same id ...": the check that the private map holds a new connection goes; the new prefix's tools show the restart. - "a disconnect still under way when a connect's tool listing ends fails the connect": that m is still registered and connected is read from listMcplServers() in place of the private map and the connection. The ignore-sigterm child's two-second exit grace in the three tests that use it is left as it is: the grace also bounds the wait after SIGKILL, and setStdioExitGraceForTests is only for a child that can exit by no means. Checked by mutation: with a hold's release freeing whichever claim the id holds, "a hold releases only its own claim" fails at the refused connect; with a lost server leaving at once, the lost-server test fails at its refused connect, and with it leaving before its reap is confirmed, at "its child had exited"; with a failed close dropping the server from the map, the stop() test fails at its retry's traced closes, which lack m.
…the self-wake it left pending "an ephemeral run's sleep and wake are refused, and its end tells the gate" spied on the gate's agentGone to see that the run's end reached it, though the run had set nothing for it to end. The changelog states the outcome: a self-wake a conversation fork or an ephemeral run left pending ends at its end. The test now arms a self-wake for the run once its turn is under way, at the result of its refused sleep, as its own skip_reply would arm one mid-turn (a turn's start cancels a pending self-wake, so one armed before the run would be gone before its end). It checks the arm took, and after the run that none is left for it, read as the file's agentGone test reads it. The gone-names helper's import goes. Checked by mutation: with the run's disposal not telling the gate it is gone, the test fails at its check that no self-wake is left for the run. An earlier form, which armed the self-wake before the run, passed under the same mutant, since the run's turn start had already cancelled it.
…ld, and waits on events
Removed:
- "the drafts tool's example id has the form newId issues": a check of a
private static's description against the id alphabet only newId
gives. Agents resend the ids the tools hand them, so the example's
shape is cosmetic.
- The store's record count after branch moves, a figure only the
journal's layout gives; each draft's state after each move stays.
- Checks of internals only the code gives: an attempt's via label, the
inherited-risk chain's ids, source and reason wording (each re-bounce
test's notice or riskText still says which words may already have
been posted, and where), and the attempt list with its
confirmedDuplicate flag.
Read another way:
- A held-draft notice is found by its "[drafts]" opening, which the
changelog names among the openings the framework writes into an
agent's context, in place of a private metadata kind; the XML-mode
test checks that no text opens with it, in place of a kind count.
- "through the real connection, an error response is unconfirmed":
the resend's tool result carries the connector's words and says a
resend needs confirmDuplicate, in place of reads of the attempt
record. The draft's state check stays.
- "hybrid mode: after a send, a drafts round's envelopes still move the
router": in place of a spy on the private continuation check, the
test waits for the model to be asked again, the continuation turn
that " !" asks for (docs/explicit-prose-routing.md).
Duplicate checks dropped, each where a later check in the same test
fails without what it pinned (see the mutation note): a draft's state
read from the private store after a failed, unknown or unrecorded
outcome, or after a dismissal, where the next resend's, {{unsent}}'s or
dismissal's words, or what is published, show it.
Time:
- "stop() during the wait after a retry's start failed names the
speech" took its error policy's 2 s delay twice, in real time. The
harness takes a delay per retry: 10 ms for the first, and a long one
for the second, which the stop ends. The test waits for the second
wait's inference:waiting trace, which the trace's doc says is said as
the wait begins, in place of reading the private waker set.
- The two heartbeat bounce tests that expect a wake wait for the second
model call, in place of a fixed 200 ms. The absence check in the
heartbeat half keeps its 200 ms.
Checked by mutation, each against the tests whose state checks went:
with an unknown outcome not counted as uncertain, the failed-and-
uncertain resend test, the later-failure test and both possibly-posted
{{unsent}} tests fail; with an attempt that never recorded an outcome
not counted, the after-restart test and the throwing-publish test
fail; with only the latest attempt counted, the later-failure test
fails; with a failed outcome counted as uncertain, the failed-and-
uncertain test fails at its first resend's words; with inherited risk
not making a draft unconfirmed, the re-bouncing test fails when a
draft goes out without confirmation; with a dismissal recording
nothing, the batch test never settles and fails by its timeout; with no
continuation asked for, the continuation test fails waiting for the
model's second call; with an unknown outcome's reason left out of the
resend's words, the error-response test fails at the connector's words.
…te's upcoming wakes, and drops restated premises - "two servers' channels with one id each get a fork of their own": the other server's fork is read from its binding, checked to differ from srv's and to be registered, and looked up by that name, in place of a name built with generation 2, which pins how the generation counter is keyed (a private field's doc says by bare id). The changelog states that each server's channel gets its own fork. - "a disposed conversation agent's name is refused for a new agent" asks for the disposed fork's name through the public createEphemeralAgent, in place of the private createConversationAgent, which only the router's spawn calls. A host can pass any name there, and agent names key Chronicle namespaces and generation identities, so a fork's name stays refused after the fork is disposed, twice over, and no agent takes it. The refusal's whole text is checked. The comment above the test goes. - "a fork's end at the idle sweep tells the gate" spied on the gate's agentGone, though the fork had left no self-wake for it to end. The changelog states the outcome: a skip_reply self-wake a fork left pending ends when the fork does. The test arms the fork's self-wake as its closure turn's stream starts (a turn's start cancels a pending self-wake, so one armed earlier would be gone already), checks the gate's upcomingWakes lists it then, and lists nothing for the fork once it is disposed. - "a fork's end at stop() tells the gate, and leaves the primary's sleep": in place of the spy, a self-wake armed for the fork before stop() is gone from upcomingWakes after it. The primary's sleep check stays. - The stored-injection premise read through storeInjections' return goes; the next check reads the same premise from the trunk's messages. - Three deferred-write tests drop their check of the private addMessage's '' return; each test's later checks of where the write was stored show it was held to the turn's end. Checked by mutation: with storeInjections storing no row, the seed test fails at its trunk-message premise; with a write for an agent whose turn is alive stored at once, the three deferred-write tests fail at where the write was stored, or at the queue the quiesced one keeps; with a disposed agent's release not telling the gate, the idle-sweep test fails at its upcoming-wakes check (the stop() test passes, since the gate's dispose clears every self-wake), and with that dispose's clearing taken out too, the stop() test fails at its own. With createEphemeralAgent checking only live agents' names, or with a fork's name never reserved, the disposed-fork test's first createEphemeralAgent isn't refused.
…ough its call's context, and names ids by value - The refusals of another agent's script and of another host's id are checked for naming the very id they were given, in place of patterns of the id's shape, which only the code that mints ids gives; the check of a run's id against that shape goes with them. - "stop watches its script with the caller's watch ..." is removed. It read the private script record's watch; "a stop watches the script with its own call's watch: a call in flight sees it as the one watching" checks the same through ScriptCallContext.watching, as the host's dispatch is handed it. - Three abandoned-turn tests read whether a script is still watched through the same context, from the script's held call (the file's setup now keeps each slow call's context), in place of the private record: a returned script's call sees no one watching once its turn is abandoned, and one of a live turn's still sees that turn's watch. - Four 50 ms sleeps after a wait() go. A wait takes its watch before its first await, so they waited for nothing; where a test then reads a call's context, it waits for that call to arrive. Checked by mutation: with stop() not taking the caller's watch, "a stop watches the script with its own call's watch" fails; with an abandoned turn keeping a returned script's watch, each of the three abandoned-turn tests fails at its watching check.
…it grace, and drops reads of Node's own fields - Five tests whose child's kill() is a no-op waited out the stdio exit grace three times, about 6 s each. setStdioExitGraceForTests is for exactly such a child, one that can exit by no means, where a short grace races no exit and only brings "could not be reaped" sooner: they now set 50 ms, and the two that match the verdict's text read it from the grace they set. A sixth, a host close() during the escalation, took about 4 s. It sets 50 ms too, and calls close() as soon as the escalation has sent its SIGTERM, from the child's stubbed kill(), in place of polling for the pipe failure and then calling it. The close then joins the escalation's attempt whatever the load: one SIGTERM and one SIGKILL go out, the close rejects as "could not be reaped", and no 'unreaped' comes, which the test checks as soon as the close's rejection is awaited, in place of 50 ms later: the escalation's own failure handler has run by then, since with its closed-guard removed the check fails with no further wait. - Three checks that a child's exitCode is null go. Node sets exitCode, null for a child a signal ended (the close info's SIGKILL already says so) and for one still running a turn after a stderr error, so no change to the transport could fail them. The checks that kill() was never called stay. - "the framework's close handler withdraws the closed transport's channel confirmations first ..." checks that the withdrawal comes first, which is the title's claim, in place of the order of all six steps. - "a legacy connection held as unreaped before its first ready() ..." drops its checks that the stderr line is still in the private buffer and that the data plane flag is unset; that nothing the server sent was delivered is what a caller sees, and stays. Checked by mutation: with the close handler removing the feature sets before it withdraws the channel confirmations, the close-handler test fails at its first step. With the escalation saying 'unreaped' though the host is closing, the host-close test fails at its no-'unreaped' check.
…s checks of the file system and of chokidar - "an atomic save (unlink, then write) comes out as one modified entry" and "a new file written twice comes out as one created entry" are removed, with their comment, and the readyWithPolling helper only they used. Their own comment said they pass with any merge rule removed, and a run bore it out: with each chokidar event flushed alone, with no merge and no debounce, both passed 10 of 10 while the atomic-save rule test failed 10 of 10, and with awaitWriteFinish removed they passed 10 of 10 too. They pin chokidar's polling, not the watcher; the rule tests pin the merges. - Three root-liveness tests waited a fixed four poll intervals for polls that would show a second re-attach, or a re-attach of a gone read-only root; under load that can hold no poll, and the test then passes without reaching its case. They now count the root's polls (a new rootPolls helper over checkRootLiveness, as the link-count test already did inline) and wait for three after the point the test reached. - "read-only mount does not create the watched path" checks at once after start(): a mount makes its path in attach, which start() runs synchronously, and its first root poll is 2 s away. - "holds its root open while it watches ...": the check that the root made again took another inode number goes. Once the test has seen the watcher hold the root, a directory held open keeps its number on any local file system, so that checks the file system, not the watcher. The title now says what the test checks: the root held, and a re-attach to the root made again in its place. - The link-count test no longer checks that the watcher holds no such root, which is how the code gets there rather than something a caller meets; it is retitled to its claim, that its polls leave such a root be. Checked by mutation for the three poll-count waits, each against its test. With every root poll re-attaching, the one-cycle test counts 4 re-attaches for its 1. With a read-only mount re-attaching when its root is gone, it watches the missing root again (5 readies for 1). With the held root's identity no longer matching once the traffic's modified change is flushed, a poll inside the traffic test's wait re-attaches, and the test fails at its count of none.
…running pass, and reads alerts from the trace
- Four tests waited a fixed 50 ms after a move began, then checked that
it had neither settled nor moved the store while a maintenance tick
ran: "an undo's re-initialization waits for the tick before any
manager re-initializes", "a branch switch waits for the tick before it
moves the store", and the two whose rollback runs through
duringMaintenanceWait ("a fork spawned while a rollback waits for
running maintenance ..." and "while a rollback waits for running
maintenance, a fork's channel message is deferred ..."). Under load a
move may not reach its wait within 50 ms, and the checks then pass
whether or not it would wait. A new watchRunningPass helper watches
the running maintenance pass for a waiter, as the drain-kick test's
watchedPromise does for its kick, and each test waits until the move
waits on the pass before it checks. The changelog states both waits:
an undo's or redo's re-initialization waits first for maintenance
already running, and a rollback, suppression or switchBranch waits for
a running pass before it switches or forks the branch.
- "each move withdraws the parked call, and its re-initialization
releases the store" (both variants) read its alerts by replacing the
private opsAlert. It now reads them from the ops:alert trace, which
every ops alert reaches, as the changelog states.
Checked by mutation: with the running pass left out of what a move
waits for, the four tests fail at their new waits (the undo's
re-initialization, the switch, and the rollback in each
duringMaintenanceWait test never waits on the pass), and the two
store-held-long alert tests fail too. With a withdrawn maintenance call
reported as a maintenance failure, both parked-call variants fail at
their alert check, read from the trace.
… into the live one, and compares or gates on what a caller sees
- "leaves another server's DM as it was ...": discord's DM rows are
taken once discord's DM is registered and compared after slack's push,
in place of the literal placeholder label, which only the code gives.
The changelog states the requirement: a push leaves the entry of
another server's channel with that id as it was.
- "a drafts resend from a script ...": the wait for the follow-up read
the private registry's default channel. It now waits for the
follow-up's message:added trace (source mcpl:channel-incoming), which
the framework emits as it places the message, in the same synchronous
step in which it makes that channel the default the old wait read. The
wait stays: without it, the variant whose follow-up must not re-pin the
turn would pass whether or not the follow-up came.
- "a script started in one turn and waited on in a later turn ..." no
longer checks that the two turns' private ids differ. Were they the
same, the first script's call would be the later turn's and would post
through slack, which the test's check of that call already fails.
- "a background script's channel_publish naming no channel ..." no
longer reads the private default channel; "default while shared" going
out through slack is that default's consequence, which the changelog
states: a publish naming no channel posts to the latest message's
channel, on the server it came from.
- The two tests of a gate wake's route on a framework built by hand
(Object.create with private fields set) are removed, and their claims
move into the live conversation-routing test, which already checked
each fork takes only its own server's route: discord's fork is now also
checked not to take slack's route while it is the only fork bound, and
the trunk not to take either server's route once both forks are. The
changelog states that an addressed wake's route applies only to the
fork homed on that server's channel.
Checked by mutation: with a push's registration relabelling another
server's placeholder entry of that id, the DM test fails at its
before-and-after rows ("DM: mallory" for the id). With a script's call
taken as the watching turn's whichever turn started it, the drafts
variant whose follow-up must not re-pin fails at its reply (through
slack), so the new wait lets the follow-up land before the turn goes
on. With every turn given one id, the later-turn script test fails at
its first script's publish (through slack). With the default recorded
on discord whatever server set it, the background script test fails at
"default while shared" (through discord). With a fork taking any
server's route on its home's id, the live test fails at the check made
while discord's fork is the only one bound; with an agent that is no
fork taking a route a fork owns, it fails at the trunk's check.
…nd open state through getDescriptor and isChannelOpen Eleven tests read whether a channel is registered, or whether it is open, from the registry's private channel map, through the file's lookup helper. They now read the same through getDescriptor and isChannelOpen, which the registry offers its callers (the changelog names both among the calls that take a ChannelRef). The helper stays for setup: the tests that force a channel closed or open by hand still use it, and the tests that no longer do drop it from what they take. - "ensureChannelRegistered keeps a DM closed ...": getDescriptor on discord's ref shows the DM registered on that server, in place of the entry's serverId. - "ensureChannelRegistered is idempotent and does not reopen a closed channel" no longer checks that the second call kept the same entry object, which is how the registry is idempotent, not something a caller meets; it is retitled to what it checks, that the second call leaves the closed channel closed, as ensureChannelRegistered's doc states (a push never changes lifecycle state). - "plain speech into a channel its server declared inbound-only ...": both reads of the open state are swapped, including the one after the refusal, which also catches a channel marked open without an open being sent, a case the check that no open was sent can't see. Checked by mutation: with isChannelOpen answering true for any registered channel, each swapped read that expects a channel closed fails at it, so each names the registered channel's server and id (a read naming another server's ref would answer false and pass): the DM, the re-ensured channel, the agent-closed channel, gc-b, the DM that registers closed, and the inbound-only precondition.
…elog states, and drops private reads its public checks repeat - Three tests pinned publishRouteFor's whole sentence, the line a bounce or a resend's refusal adds for a channel shown by its id and server. The changelog states its parts: it gives the channelId channel_publish reaches the channel by, as JSON, or says no channelId reaches it alone. A new offered helper reads the channelIds a route gives, and the tests check those (the qualified name first where it picks the channel, the id where only that does) or, where none is given, that the route says no channelId reaches the channel alone and names its id and server. The test that posted with the offered channelId now takes it from the helper. - "a name one server's two channels answer to is refused ...": the >> target's and the resend destination's refusals are checked for being refusals that name both candidates, which is what the changelog states (a name two channels answer to is refused as a shared id is, naming each candidate so it can be told apart), not for their whole wording. The refusal that says the destination named no server keeps its whole text: no source states its parts. - "a DM is listed by its label only where ...": the <@42> refusal is checked as a refusal with both DMs as candidates, which is what the changelog states of a mention one person's DMs on two servers answer to. - "removing a server stops its channel's typing ..." no longer reads the private message and thread fields after b's removal: incomingFor giving no message, the check before it, fails if the message stays. - "a default channel whose server disconnected ..." reads the default through getDefaultPublishChannel, in place of the private field. - "a fork's turn whose wake names no channel still has its home ..." compares incomingFor's channel and message, where its deepEqual also pinned a threadId key holding undefined. - "a wake naming a shared id without its server ..." no longer reads the turn's private trigger channel and locus: resolveLocus answering null, a check the test already makes, shows the turn has neither, since the framework gives the registry a turn's locus, else its trigger channel. Checked by mutation: with publishRouteFor trying the bare id before the qualified name, the qualified-name-first test fails at the channelIds its route offers. With removeServer keeping the message of a default on that server, the typing test (both orders) fails at incomingFor giving no message. With removeServer dropping that default's ref, the disconnected-default test fails at getDefaultPublishChannel. With a wake's unresolved shared id taken as a's channel, the no-fallback test (both orders) fails at its check that nothing is published.
…pshot, and checks a timed-out stop's error and log line by their stated parts
- "a turn whose retry stop() cancels leaves no agent inferring at the
event gate ...": the agents inferring are read from healthSnapshot()'s
gate, the event gate's own diagnostics, which healthSnapshot() gives
for /healthz and doctor tooling, in place of the private eventGate.
- "a stream that outlasts the budget fails the stop ...":
- the ShutdownTimeoutError is checked for naming what it waited for,
stream:assistant, which is what the changelog states of it, not for
its whole message;
- the check that the note is still in the private queue goes: that it
isn't stored ahead of the stream, and is stored once the stream ends,
are both checked, and the wait for the stream's end now waits for the
note to be stored rather than for the private queue to empty;
- the stop's log line is found by its [deferral] stop: opening and
checked for the parts the changelog states: the agent, that its
writes stay queued, that its stream may still write, and that a
memory-only queue loses them if the process exits.
- "a write deferred behind the entry a retry that failed to compile left
is stored by stop() ...": the private addMessage's '' return no longer
stands for the deferral; the note not yet being in the agent's context
does. The read of the private queue after stop() goes: the reopened
store holding the note shows the final flush stored it.
- "a responder without respondError reaches the handler without one" no
longer checks what the handler saw of the responder's shape, which no
server meets, and is retitled to what it checks: a responder with
respond alone gets the handler's answer.
Checked by mutation: with the event gate's dispose leaving its agents
inferring, the cancelled-retry test fails at healthSnapshot's gate after
stop(). With stop()'s final flush draining a still-streaming agent's
writes too, the outlasting-stream test fails at its check that nothing
landed ahead of the stream. With the final flush doing nothing, the
failed-compile test fails at the reopened store holding the write.
…y their stated parts, and drops an order no doc states - The changelog states what the [inference-rearm] stderr lines say: when each re-send is coming, when it went, and, as "turn not re-armed", why none is (re-arms that are spent, a turn that would have to guess a channel, among others), and that a message said to the agent starts its count again with a line saying so, so a (1 of 3) can follow a (2 of 3). A new rearmLines helper finds the lines with that opening that hold each part given, and the tests check those parts in place of each whole line: the agent, the delay and (n of 3) for a re-send coming; (n of 3) without the delay for one that went; "not re-armed" with "spent" or "guess a channel"; "count" and "message" for the count starting again. The check that no such line came before the message was said reads the same parts. - "a turn whose retries ran out on a server error is sent again ..." no longer checks that each inference:exhausted trace comes before its re-arm shows as held in healthSnapshot(): no doc states that order. Checked by mutation: with the line saying a re-send went losing its (n of 3), the server-error test fails, for each kind of failure, at its check that the operator is told re-arm 1 went. The file also passes with the commit that makes the test's tool round declare that it carries injected messages, as membrane's native path does.
… and checks the surgery refusal by its stated parts - "a resident whose turn a stated wait holds for its retry is mid-turn ...": the hold's end is read from healthSnapshot()'s cooldownUntil, in place of the private hold map, and the rollback's agent-busy refusal is checked for the parts the changelog states it names: the provider hold, its end, its reason, and how to go on sooner (a quiesce with abandon: true, or release-provider-wait), not for its whole wording. Checked by mutation against that test. With the refusal naming an end one second after the hold's, the end read from healthSnapshot isn't in it; with the hold's reason left out of the refusal, or release-provider-wait, the test fails at that part.
…retries, renders and backoff, and drops the host's internal layout
- "a retraction ..." and "a replacement is evaluated by the gate ...":
the wake queue emptying at the retraction, or at a non-qualifying
replacement, is the host's own layout. RFC-006 §4.2 states the outcome,
no inference whose sole pending cause was retracted unread or replaced
by a non-qualifying occurrence, and leaves how to the host ("Internal
timer layout is not part of this contract"). The no-model-call checks
stay; the first test is retitled to what it checks.
- "a replacement while a turn is alive edits the deferred queue ..." no
longer counts the deferred queue: the next request holding the
replacement and not the original is the check.
- G9 read the store's receipt record, its key and its born field. It now
checks what that record is for: an acknowledged acceptance, after a
crash before the snapshot flush, answers its resend from the receipt
and delivers nothing again (RFC-006 §3.1: a host returns an admitted
occurrence's original result without repeating its effects, and this
must survive host restarts). Retitled.
- "a receipt record whose write failed ..." read the host's private list
of records to write. It now checks the consequence that list exists
for: a deferred batch whose receipt write failed is then dropped (its
authority revoked at the assembly), another subject's receipt is
written, and the host crashes before the snapshot flush; after the
restart the dropped batch isn't delivered. A render start or a later
receipt of the same subject would hide what the test is about, so the
batch moves on by revocation. Retitled.
- The fixture's reply-deadline test is removed: REPLY_DEADLINE_MS is the
fixture's own hang guard, not a requirement of the framework. Its one
fact about the host, that a channels/incoming request whose first
commit is held admits all its messages once the commit is made, is now
a test of its own, checked in the request's answer and in the agent's
context. The REPLY_DEADLINE_MS import goes with it.
- "an assembly for an agent outside the audience ..." no longer counts
the settled set of the assembly in the audience. A turn is withdrawn
only when its assembly materialized none of its subjects, so once the
render is materialized the settled set decides nothing. The declined
assembly's settled check stays: that settled subject is what withdraws
a turn the batch alone woke for an agent outside its audience.
- "a batch-only turn that reaches its assembly while another
resident's ...": the joined assembly's settled and materialized checks
go. The withdrawn turn and the one model call are their consequence.
- The two "a turn a wake to read ... held by a provider hold ..." tests
read whether the held trigger kept its read mark. No source a caller
reads says what a held trigger carries; what the mark would do is
stated: a wake whose write a request carried that was answered starts
no turn (InferenceRequest.readsDeferredWrite and readsStoredMessage).
Each test now marks a request that carried the render or the message as
answered while the turn is held, and the retry still has to run.
Retitled.
- "a provider recovery whose released wakes start no turn ...": the
gate's primaryPending and the recovery map are mechanism; the parked
auxiliary call being admitted is what the tests read. Where a wake
beside the gone render's starts the turn, the test now checks that a
parked auxiliary call stays parked, which is what "Context Manager
maintenance waits behind the primary lane" says, where it read the
recovery map. Retitled.
- "an assembly that throws closes the window ..." read the private
assembling set. It now checks what the changelog states: a render that
settles after its turn's assembly failed, while the turn is still
alive, is deferred like any other write during the turn, and is stored
once the turn ends. Retitled.
- "a write deferred for an agent while its assembly awaits a render
...": the private addMessage's '' return no longer stands for the
deferral; the write not being in the agent's context does. The
assembling-set check goes.
- "a batch-only turn whose call a provider's capacity limit held ..." no
longer reads the held record's marks: getActiveTurnTrigger showing the
retry's trigger without them, and the retry running, are the checks.
- R16b counted the wake queue. It now checks that the failed freeze
started no model call, and that after the producer's retry a drain
alone makes the one call, which reads the fallback.
- R17 and R17b checked the backoff's arithmetic: the attempt numbers,
each delay as 40 x 2^n, a cap at 60 x the base and the list [3, 6, 12,
24, 48, 96, 180, 180]. Only the coalescer's own option comment states
them, and the coalescer isn't exported: the framework builds it
without options, so no host sets the base they are stated against.
They now check its properties: under a persistent outage no wake's
delay is shorter than the one before it, and the delays grow; they
stop growing; and a second outage starts again from the first delay,
which also shows the failure series was forgotten on success, where
R17 read the private recovery map. Retitled.
- Two anima-research#157 first-view tests on the coalescer: the snapshot's
firstViewEnded field, the snapshot receipt's keys, and n1's
receiptFirstView are the code's layout. Each test keeps the check of
the consequence: the restored coalescer's next batch isn't a first view,
and a restored receipt has no first view, which follows from "a doubt
counts as shown".
- Two receipt-retry tests on the coalescer: the records' batch fields,
pendingBatchOccurrences and the no-second-write count go. The restore's
delivery checks stay: the rendered notice isn't delivered again, and
the rendering one comes back for its fallback.
- vectors 21/23 stalled the renderer 5.3 s of real time to pass the
host's 5 s render timeout. Mock timers now carry the timeout, the
renderer answers late when the test releases it, and the test waits for
the documented late-render trace with discarded: true in place of a
400 ms sleep.
- vector 41 waited for the reconnect's initialize and then slept 100 ms.
It waits for the documented mcpl:server-reconnected trace instead,
which comes once the reconnect's attempt has settled.
Checked by mutation, each mutant against the tests it names. With a
replaced write left in the deferred queue, the replacement test fails at
the next request. With the receipt record never written, G9 fails at the
copy count (2 for 1). With a failed receipt record left on the host's
list, the dropped batch is delivered after the restart. With only a
request's first message handled, the held-commit test fails at its
answer. With a joined assembly taking the render as its own, the
batch-only turn test fails at its one model call (2). With a retry
keeping the read marks, both held-turn tests fail at the retry (1 call
for 2). With any dropped read wake giving up the pending primary turn,
the provider-recovery test fails at the parked auxiliary call. With the
assembly's window never closed, the late-render test fails at the
deferral. With a live turn's token no longer deferring writes, the
deferred-write test fails at its context check. With a retry keeping the
batch marks, the capacity-hold test fails at the retry (1 call for 2).
With a producer retry not re-waking the batch, R16b fails at the retry's
call (0 for 1). With no cap on the delay, R17b finds no repeat; with a
constant delay, R17 finds no backoff; with the failure series never
forgotten, R17's second outage starts at 320 ms for 40. With a record
carrying the last batch it saw, the rendered notice is delivered again
(2); with a record ignoring a rendering batch, the accepted notice
doesn't come back. With freeze declining an agent outside a rendering
batch's audience without settling the subject, the outside-audience test
fails at its settled count (0 for 1).
…ts and private reads its public checks repeat, and waits on routing rather than a delay - "routes each round's prose live ...": each move waited 25 ms so that the round's prose would be routed before the tool returned, and the test read that order. The robot module now takes a beforeResult hook, and the test's move returns once its round's prose has been routed (or after 10 s, so a prose that routes only after the results fails the order check rather than hanging). The order is read with no time in it. - 27 tests set the robot's toolDelayMs to 25, a fixed wait in each move so that live routing came before the tool returned. Every one of them passed with it at 0, in each of five runs of the file (Linux, load about 3), so the setting goes, and with it the module's delay field. - "a start that continues the turn and throws before it takes the turn's routing ..." no longer reads the private open-turn map after the failed start: the next start resolving a locus of its own, where continuing the turn would have kept the first, is the check. - "a silent start that throws leaves an open prose turn alone" is removed. It builds an open prose turn by hand while a silent start runs, a state the framework doesn't enter (a capacity hold holds silent wakes with the turn's, and a re-inference's wake batches with them), and the test's own comment says so. - "a silent turn's over-budget boundary leaves no arrival on an open prose turn" is removed for the same reason. Its reachable half, a silent turn that crosses its budget restarts silent and routes nothing, is pinned by turn-continuation, explicit-prose-routing and prose-drafts between them. - "a message deferred in a start that fails before its stream lands then ..." no longer reads the deferred queue after the failed start: had the message stayed queued, it would have landed after the newer one and won, which the routed check reads. - "a failed start's flush stops at a message it can't store ..." no longer reads the deferred queue's order: what the store holds after each start, nothing and then both in order, is the check. - "a send made outside the agent's turn engages none of its channels" waits a setImmediate after the open resolves, as the file's other out-of-turn and failed-open cases do, in place of 20 ms: the open's outcome is handled on the microtask queue. - The sleep-announcement test loses its middle leg, a silent start's routing taking over while a prose turn is open, the state the two removed tests built. Its explicit, locus and disabled legs stay. Retitled. - "passes no injection options when nothing arrived mid-turn" checked that the tool-result options argument was undefined, which is the code's shape. It now checks that no message is injected. Retitled. Checked by mutation, each mutant against the test it names. With a start that throws outside its stream leaving its turn open, the next start keeps the first locus, and the failed-start test fails at it. With the failed start's flush waiting on any agent's tool cycle, the deferred message lands after the newer one and wins. With a write the store refused requeued without itself, the store ends up holding the second message alone. With any caller turn resolving to the latest turn, the outside-turn send engages its channel within the one setImmediate, and the test fails there.
…e walk's place, the pin and a patched set, drops checks of mechanism, and holds the mount on gates rather than sleeps - "a failed durability barrier is reported, and its intent stays pending" no longer counts the fsync calls: the skipped reason naming the file fsync is the point the test reaches, and whether a later barrier ran is no requirement a caller states. - "this branch agreeing with disk leaves another branch's pending adoption ..." no longer reads the agreement after the other branch's write: main resolving its own adoption to B is the check. - "the ctime trust gate ..." keeps only its untrusted half: no NFS, FUSE, SMB2 or 9p filesystem is trusted, since a ctime trusted where it isn't reliable lets a fingerprint vouch for a changed file. The exact list of trusted local filesystems, the bigint form and the platform check go: they follow the code's current table, and leaving a filesystem off it costs only a rehash. Retitled. - "a recheck of an unchanged file records nothing" counts the store's workspace/disk-agreement records, where the changelog tells hosts the evidence lives, before and after the two syncs, in place of a patched private set. - "the walk takes files by name, whatever order they were written in" is removed. Node's readdir already returns these names sorted on Linux, as its own comment said, so it can't fail there if the walk stops sorting; the next test is the one that does. - "names that sort apart by UTF-8 byte and by UTF-16 code unit ..." checks that each name is taken once across the three syncs, not the walk's exact resume order, which no caller-read source states. Retitled. - "a directory sync leaves the mount root's place as it was" reads what the next bare sync takes, c.txt after b.txt, in place of the private place. - "an accepted new root starts with no place" reads the first sync's take, a.txt and b.txt, in place of the private place, and its last place read goes: a walk that resumed after the old place would have taken c.txt, which the test refuses. - "a pass that records only a place binds no root ..." no longer reads the private root identity: the replaced root read as it is, with no disagreement reported, is the check. - "a pass that moves only its place makes no store sync" is removed. No doc promises that a pass which only moves its place skips the store sync, and what it saves is time alone. - "a scan that outlasts its deadline ..." holds the mount on a gate the test releases after its checks of the late scan, or as soon as one of them fails, so a failure is reported rather than leaving the mount held through teardown. It replaces a 400 ms sleep that could run out before the checks under load. - "batches that come while a scan waits share it ..." holds the mount on a gate released once the three batches have called in, in place of 200 ms: each batch queues its scan before its first await. - "a materialize between initStore and start keeps its pin ...", "a materialize whose push is refused on a branch change leaves the pin ..." and "a first materialize refused on a branch change leaves an unpinned mount unpinned ..." read the pin and watermark from status (lastMaterializedBranch, lastMaterializedSeq), in place of the mount's private fields. - "takes up only what the evidence says disk owes ..." no longer records which paths each push was handed: what the forced and bare materializes report, and what disk holds, are the checks. The helper goes with it. Checked by mutation, each mutant against the test it names. With another branch's pending adoption settled before this branch's write, main reads a workspace draft rather than synced, and the test fails at main's own resolution. With every pass re-recording its evidence, the disk-agreement record count moves from 6 to 8. With the walk taking entries in the order readdir lists them, the three syncs take b.txt and U+FF21 but never the emoji name. With a directory sync recording its place under the mount root's scope, the next bare sync doesn't take c.txt. With an accepted root keeping its walk places, the sync after it takes c.txt. With every pass binding the root, the replaced root is reported as not the directory disk last agreed with. With each batch queueing its own scan, three scans run for the three batches. With the saved state applied over a materialize made before start, status reports the saved watermark, 3 for 9. With a push that planned nothing moving the watermark, or setting the pin, both refused-push tests fail at status's pin and watermark. With the deadline's reason worded otherwise, the deadline test fails at its reason check while the mount is held, and ends at once.
…l-start wait, restore the stub in finally - The header said any other event pushed to a stopped framework still throws to its caller. That holds for pushEvent(), which its test checks; a module's other events are logged and dropped (pushModuleEvent), so the header, and the test's title, now say so. - frameworkWithRunningTool awaited module.started with no bound, so a tool that never started ended the test only at the runner's timeout, naming nothing. It now waits with the file's own until(), 5 s, naming the slow tool. - The render test put pushCoalescer back after its asserts, inside try, so a failed assert left the stub in place for stop(). It's restored in finally now.
…te before opening it "starts once the watcher is ready, …" and "a re-attached watcher runs its pass at its own ready, …" hold chokidar's directory reads at a gate, then check that some read waited there (`scan.held > 0`). The gate counts only reads made while it is closed, and each test opened it after a fixed run of awaits: `env.open()` or `reattach()`, then `ended()`, which with no pass started is one `setTimeout(0)`. Nothing in that run waits for chokidar. Its first `readdir` comes only after an `lstat` of the mount's root (chokidar 4.0.3, `esm/handler.js:577`), so when that `lstat` took longer than the awaits, every read came after the gate opened, `held` stayed 0, and the check failed. The start test failed that way in a run that began at a 1-minute load of 31.7. Each test now waits, before `ended()` and the gate's opening, until a read has reached the closed gate, bounded at 15 s like the tests' other waits on chokidar. The check after the gate opened can no longer fail, so it goes, and its message is now the wait's. Red-first, on Linux with Node 24.22.1, at 1-minute loads between 27.7 and 36.6 where each run began and ended: the two tests ran by name eight times at `main` and eight with this change. In three of the eight, a preload that isn't committed delayed each of chokidar's `lstat`s by 200 ms. It has to replace `lstat` before chokidar loads, since chokidar copies it into `statMethods` then (`esm/handler.js:29`), so a patch made inside the test never reaches it. Delayed, both tests failed every time at `main`, at the held check, and passed every time with this change. Unslowed, at `main` the start test failed once and the re-attach test once in five runs, and with this change both passed all five.
…e fixture may still be appending Tests that wait on a fixture by polling its JSONL log parsed every non-empty line, so a read that landed while the fixture was appending could meet a half-written last line and throw from JSON.parse instead of waiting. One such read threw "Unterminated string in JSON" in a test that polls a log for a minute while its fixture writes several hundred lines. test/helpers/json-lines.ts gives jsonLines(path): the whole lines of the file parsed, [] for a file that doesn't exist yet. A line still being written has no newline yet and is left for the next read. Its readers now, each called while its fixture can still be writing (in a wait or poll loop): - batched-wake-locus: sent, publishes; - discord-awareness-delivery: jsonl; - host-quiesce: records; - mcp-modern-outcomes: wire; - mcpl-down-for-good: legacyPid, which waited for the file to be non-empty and then parsed its first line, which could still be partial; it now waits for a whole first line; - mcpl-reconnect-registration: serverEvents; - mcpl-tool-lifecycle-e2e and mcpl-tool-lifecycle-script: readLog; - prose-drafts: publishes, sendMode's count, sends; - reply-server-routes: lines (four describe blocks) and via; - stop-ends-awareness-drains: untilCalls; - surgery-marks-receipt: jsonl; - tune-out-e2e: statusEvents; tune-out-transport: events; - helpers/reader-fork-rig: published. Read only once their writer is done, so left as they are: - continuing-start-silence's lines, after the turn has ended and the framework is idle; - failed-create-releases, after create has rejected; - liveness-watchdog, after the child has exited; - mcpl-down-for-good's first line in the version-verdict test, which the raw fixture writes before it reads its input; - pack-installs-alone, after npm has exited; - tune-out-reader-epoch and tune-out-reader-epoch-shared, after the reader's calls have settled. Given a file whose third line is half-written, jsonLines returns its first two. A torn read can't be produced on demand, so no test fails without this change.
…ke finds the agent at rest Under Bun, "a resident's left script wakes it with wake_agent(), and its failure wakes it too" failed on every leg of this branch's CI, waiting for the wake's inference request; under Node it passed. The test releases the script's first slow call as soon as the script is waiting on it, while the first turn may still be at its code_execution tool round. Under Bun the script reached wake_agent() before that round's tool boundary, so the wake was written while the turn's stream was live, and the boundary injected it into that stream. With the mock now declaring supportsInjectedMessages true, as membrane's native path does, the injected wake counts as carried and the turn that carried it as answered, so the wake starts no turn, which is what a live native-path stream should get. Before this branch the mock left the declaration out, and the undeclared mix injected the wake but left it unread, so a second turn sent it again; under Node the wake came after the boundary either way. So the test passed or failed by which side of the tool boundary the script's wake landed on. The test's own comment says the script yields and the agent rests, and that turns 2 and 3 are the wakes. It now waits for the first turn's inference:completed before releasing the slow call, so the wake always finds the agent at rest and has to start a turn. Runs, on code-execution.test.ts's test alone, bun 1.4.4 and node 24: - at this branch's previous head: bun failed 3 of 3 at the wake's request, with the wake injected into the first turn's stream and no second stream; node passed with the wake in a second turn's request; - at main 48f26ce: bun passed 3 of 3, the wake injected into the first turn and also sent by a second turn; - with this change: bun passed 10 of 10 and node passed; at main with the same change, bun passed 3 of 3; - with this change and scriptWake storing its message but requesting no turn: bun failed 3 of 3 at the wake's request.
nissa-seru
force-pushed
the
test-slimming
branch
from
October 30, 2026 12:35
e53cd6b to
662c6cd
Compare
1 task done
tel-0s
added a commit
that referenced
this pull request
Oct 30, 2026
…g pass (#934), an undeclared supportsInjectedMessages read as false main at a687fa6 is one merge past c530669, #934, with no release in it, 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). #934 slims the suite file by file (161 commits: tests or checks that pinned how the code works, repeated another check or couldn't fail, removed or rewritten; fixed sleeps replaced by waits), makes an unscripted stream or call fail its test in six files, adds test/helpers/json-lines.ts for fixture logs, and removes the derive skip guards (#886). In src: 93cdb7d reads a stream that doesn't declare supportsInjectedMessages as one that doesn't carry injected messages (=== true at the tool boundary, with `|| !currentState.stream` for the direct API's, and for presentsInjections), MockMembrane and MockYieldingStream now declare true; bbba75b drops XML tool mode's preamble fallback, the version-1 awareness ledger's own refusal and ReferenceTestimony.truncatedFields. 20 paths conflicted (2 in src, 18 in test). src (5 regions): - src/agent.ts (2, docs): the shown watermark's doc and markContextShown's keep this branch's text (a ReadMark; the window, or in legacy mode everything stored; presented-mode marks) with main's change: the shown mark moves at a boundary that injects into its stream, no longer "whether or not the stream says it carries injected messages". - src/framework.ts (3): - the deferred-write mirror's doc, where main reworded its clause on a stream that doesn't declare it carries injections: this branch's importer doc (deferred-import), as the queue is gone (#291). - the tool boundary: this branch's presentation (presentAtBoundary with `injects`, the clock row gated by it, heard rows rendered after they are presented), with main's reading: `injects` is now `supportsInjectedMessages === true || !currentState.stream`, where it was `!== false`. Its comment says so: only a stream that declares it carries injected messages hears what arrived; the XML path's false and an undeclared stream hear nothing, and what arrived waits for the turn's end; a boundary with no stream, the direct API's, presents it. On this branch that clause gates the direct-API boundary's clock row (clock-rows' new direct-API test) and its presentation of arrivals. - releasedWindow's doc: this branch's "presented without sending", with main's clause for the direct API's boundary, which has no stream, and without the clause for a stream that doesn't declare it. - The rest of 93cdb7d and bbba75b merged cleanly (presentsInjections's === true, the boundary's mark comments, the XML roundPreamble, the ledger, references, push-coalescer and types docs). Tests (18), by what became of each: - Main's slimming and this branch's adaptation both kept (8): - conversation-routing: main drops the private addMessage's return checks in the three closure-turn tests; this branch's "stored at once, out of the turn's window until its end (#291)" comment stays. - deferred-wake-integrity: main's wait for the DM's processing, and its write through the module's context, which here is stored at once (a stored id, not a held: handle) and out of the window. Internals keeps addMessage, which this branch's first test still writes through. - deferred-window-only: the header takes main's reading of an undeclared stream with this branch's "what arrived" and #291 paragraph. The can't-carry test is this branch's; main's new undeclared test takes this branch's wording and body (no queue to check at the boundary). Main's new direct-API test: here a write during a direct-API tool cycle enters the window at once (no turn is alive), so it is made while quiesced, as main's is: the boundary presents its results and nothing of another's, and resume presents the write after them, still unread. The queue-mechanics region main slimmed stays deleted. - mcpl-coalescing (8 regions): the replacement-while-alive test is this branch's (store holds the replacement alone, the window neither). The two held-provider tests take main's "a request that carried it was answered" form, with readsRow, and mark answered the consumed watermark, checked to cover the row (coverTest). Main's rewrite of the assembly-throws test reads here as: a render that settles after its turn's assembly threw is stored at once out of the live turn's window, and presented at the turn's end. The write-during-assembly test drops the assembling set and the private return, and checks the store holds the write at once. - prose-drafts: both imports; the notice is found by its [drafts] text, as main does, in this branch's window. - puppet-turn-reservation: main's reservation check through getHostModeStatus; this branch's check that the pair is in the window stays. Main dropped the deferred-queue check because the stored pair shows nothing was stranded; here a stored row may still be out of the window, so the window check is the one that says so for this parked-admission case. - save-recent-image: main's snapAfterCall hold on tool:started, with this branch's onSnap hook, awaited first. - stored-injections: main's readsAt form of the reads-by-id check, with presentAll after the rows each framework writes past the framework; the rewind test reads the window, since on a presented window the shed is the window's and the record keeps the row. - Main's deletion taken (2): - tool-wrapper-prose-guard-integration: "conversation disposal reclaims logical-turn state" goes, with this branch's stand-in context manager. - batched-wake-line: the wake line's metadata is checked for its source alone; this branch's audience-stamp check goes with the rest, as presentation-points' "each resident a batched wake was for writes its own line into its own window, once" pins the stamp. - This branch's deletion stands, main having changed a test of the queue or of held rows (7): deferred-write-failures (the file; the short abandon grace main gave it is in host-quiesce's unabandonable test already), ephemeral-settle (what is queued for a run), module-held-writes (held rows' matcher), present-while-acting (a failed start's flush), process- response-edit-refusals (an edit of a held write), stop-with-work-in-flight (the write deferred behind a failed retry's entry; and the stop's [deferral] line, where this branch checks the window), tune-out-cancel- held (the held rows the notice no longer carries). - This branch's version kept (1): reinit-after-branch-move's undo test, where an undo here waits for no tick; main's wait on the running pass is for a wait this test says doesn't happen, so its watcher is left out. Read onto this branch past the conflicts: - turn-inputs-pin: JsStore and ContextManager as ContextManagerClass imported again; main removed them with its derive guard, and this branch's walkAfterTheSwitch opens an earlier release's store with them. - tune-out-reader-framing-presented: its derive guard goes, as #886's removal did for the rest (reader-fork-rig no longer exports supported). - present-while-acting: this branch's addressed-from-elsewhere notice test drops module.toolDelayMs, which main removed from the robot module. - tune-out-messages: main's rewritten cancel_tuneout test passes the coordinator a getDeferredMessages hook, which this branch removed from TuneOutFrameworkHooks; the line goes, as no-deferral-residue asks. storeSees: no new site; main opens no context manager. agentSees: no new view decision. Notice tags: none new. Changelog: no release in main's range, so no fragment was consumed; main's two new fragments come in, injection-declared rewritten for this branch (what arrived waits out of the window for the turn's end; the direct API's boundary presents it), as is README's sentence on an undeclared stream. Tests: built with zero errors; no stale compiled test; fixtures copied. The 18 conflicted files and main's other changed test files this branch also changed, with tune-out-reader-framing-presented (53 files): 2698 tests, 2691 pass, 2 fail, 5 skipped; the two failures, the direct-API test and the rewind test, adapted above, then pass. With the built boundary's `|| !currentState.stream` taken out, clock-rows' direct-API test fails; with `!== false` back, deferred-window-only's undeclared test fails. Full suite (npm test): 9659 tests, 9609 pass, 1 fail, 49 skipped (runtime-conditional: Linux-only rules, temp directories that fold case or normalization, and a structured-content case this runtime makes moot). The failure was no-deferral-residue, which found two names of the queue in tests: a held: prefix in deferred-wake-integrity's check, now a read of the stored row, and tune-out-messages' hook, above. Those three files and the two adapted ones then pass, 119 of 119. 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.
This slims the test suite file by file, makes a provider stream or call that a test didn't script fail that test in six test files, and changes
srcin two commits, each with a changelog fragment.Of its 170 commits, 161 slim one test file each, two of them with a helper or fixture beside it. Between them they remove tests, or checks within tests, that we judged to check how the code works rather than what a caller relies on, to repeat another check, or to be unable to fail; rewrite others to check the requirement; and replace fixed sleeps with waits on events, or shorten them where a test-only setter or the test's own timeout allows. One more,
5c8ee8fcb8, has tests that poll a fixture's JSONL log parse it only up to its last newline, and another,662c6cd4c6, makes acode_executiontest wait for the first turn to end before it lets the test's script go on to its wake; under Bun, that test failed in this pull request's CI. In two alternating whole-suite rounds on Linux at Node 22.12.0, run at32da22bfdd, an earlier head of this branch, the suite ran 9,392 tests whered700dce18b, thenmain, ran 9,437, and took 7.2% and 13.0% less wall time thand700dce18band 0.7% and 1.7% less CPU time, so the gain is mostly in wall time; the second round's figure is likely overstated, since its run ofd700dce18bmet the heaviest load of the four (Tests has the figures, and what has changed since). Insrc,93cdb7d628reads a stream that doesn't declaresupportsInjectedMessagesas one that doesn't carry injected messages, andbbba75bc59removes two readings kept for older inputs: XML tool mode's fallback for membrane before 0.5.79, and a version 1 Discord awareness ledger's refusal of its own. It closes #886.Problem
At
main(c5306693e0):test/state-registration-errors.test.ts:319) collects the lines undersrcthat match/\.registerState\(/(:333), leaving out comments (:332), and asserts that there is one and that it's inmodule-registry.ts(:338–:339).outgoing-stream, a provider stream that a test didn't script didn't fail the test.test/outgoing-stream.test.tspasses 57 of 57 tests, and its output logs "unexpected extra inference" three times. Its three tests of a publish the server reports undelivered, "hybrid >>> target", "explicit mid-turn" and "explicit text-only", take about 3 s each.package.jsonaccepts (Derive skip-guards check view/derive alone; derive now needs selective forks (supportsDerivation) #886). Seven places skip shared-inheritance and reader-fork tests unlessJsStore.prototype.viewandContextManager.prototype.deriveare functions, such astest/dendrite-derive.test.ts:31–:32. One of them istest/helpers/reader-fork-rig.ts's exportedskip(:24), which threetune-out-readerfiles take. As Derive skip-guards check view/derive alone; derive now needs selective forks (supportsDerivation) #886 says,derivealso needs chronicle's selective forks, which the guards don't check.package.jsonaccepts chronicle^0.5.1and context-manager^0.24.0(:51–:52), and on October 30 at 13:07 UTC npm has chronicle 0.5.1 and context-manager 0.24.0, 0.24.1 and 0.24.2 in those ranges. With each of the three pairs,ContextManager.supportsDerivationreturnstruefor a fresh store, and the nine files whose guards this change removes run 68 tests, none skipped.srckept two readings for older inputs, and a field set only to an empty list.roundPreamble ?? event.context.preamble(src/framework.ts:25197–:25199). The comment above it givesroundPreambleas membrane 0.5.79's and later, and thepreambleas cumulative in XML mode (:25191–:25193).package.jsonrequires membrane^0.8.0(:53).src/recovery/discord-awareness-outbox.ts:1321–:1330), whose message says that only agent-framework commitb16731b2wrote that version, "before any release".ReferenceTestimony.truncatedFieldsappears insrconly at its declaration (src/mcpl/references.ts:53) and where it's set toout.truncated, an empty list (:185–:186).takeString, whichoutis passed to, types it as{ rejected: string[] }(:84).src/index.tsdoesn't exportReferenceTestimony.srcread a stream'ssupportsInjectedMessagesthree ways.src/framework.tstests it ascurrentState.supportsInjectedMessages !== falseat the tool boundary (:16604), as=== trueat:16967and:17003, and reads it with?? trueforpresentsInjections(:25273–:25274). For a stream that leaves it out, the first and third givetrueand the second givesfalse. The comment at:25222–:25225gives it as absent on membrane 0.5.x. membrane 0.8.0's own tests assert that a yielding stream's tool round declares ittrueon the native path andfalseon the XML prefill path (tests/unit/round-report.test.ts:163and:310at antra-tess/membranev0.8.0). The suite'stest/helpers/mock-membrane.tsdeclared it only when a test passed a value: its default isundefined(:204), and its tool-calls event carries the field only when it's defined (:125–:126).Changes
src, in two commitsbbba75bc59removes these:preamble, so a round's persisted text is itsroundPreamble. The test that pinned the fallback goes ("legacy membrane without roundPreamble keeps the cumulative fallback (pinned)",test/xml-round-persist.test.ts:272atmain), and so does its mock's switch for leavingroundPreambleout.if (version === 1)branch, with its test. README.md's sentence on version 1 now says such a ledger "is refused as any version but 2 and 3 is, until it is moved away".truncatedFields, with the test lines that checked it was empty.It also changes
deriveAgent's doc from "It needs branch-bound store handles" to "It needs a store that supports derivation (ContextManager.supportsDerivation)", the sentence Derive skip-guards check view/derive alone; derive now needs selective forks (supportsDerivation) #886's "Also" quotes. Derive skip-guards check view/derive alone; derive now needs selective forks (supportsDerivation) #886 places that sentence abovederiveSharedContext; atmainit's inderiveAgent's doc (src/framework.ts:10957). Fragment:changelog.d/unsupported-compat.changed.md.93cdb7d628reads the declaration as=== trueat a tool boundary with a stream:main's:16604becomescurrentState.supportsInjectedMessages === true || !currentState.stream(:16609here), and its:25274becomes=== true(:25264here). A tool boundary with no stream, as the direct API's (Agent.runInference) is, still enters that branch, through|| !currentState.stream.MockMembraneandMockYieldingStreamnow declaretrueunless a test passesfalse, or'undeclared'to leave the field out.truewhere the round carriesroundContent(end-turn-usage,framework,exhausted-turn-rearm,tool-wrapper-prose-guard-integration, andsame-round-think-policy's paused stream), andfalseinskip-reply-wake-xml-mode.deferred-window-only's becomes "a stream that doesn't say whether it carries injections is taken as one that can't: what is queued waits for the turn to end".module-query-for-agent's androute-failure-targeting's go, and their siblings with a stream that declares it stay.deferred-window-only's "a boundary with no stream, the direct API's, stores what was queued for the agent there", which also checks that the stored message stays unread,clock-rows' "after a direct-API tool round whose result ends the turn: the clock follows the results", andprose-drafts' "a stream that doesn't say whether it carries injections gets no mid-turn notice: its held reply is named in the receipt".Agent.markContextShown's doc comment now speaks of "a tool boundary that injected messages into its stream". Fragment:changelog.d/injection-declared.changed.md.The derive skip guards
Closes #886.
c58280c59fremoves the seven guards and the skips they set, incompression-after-release,dendrite-derive,dendrite-fork-view,tune-out-reader-forks-e2e,tune-out-reader-epoch-shared,turn-inputs-pinandtest/helpers/reader-fork-rig.ts, whoseskipthe three othertune-out-readerfiles took. #886 proposes one shared guard onContextManager.supportsDerivation. This removes them instead, since every chronicle and context-manager version on npm thatpackage.jsonaccepts can derive (Problem, above). A chronicle without the selective forks, which #886 says a linked tree could give, fails 17 of these tests atmainand here alike (Tests). #886's "Also" asks for the doc line to name the selective forks; it now namesContextManager.supportsDerivation, which returnsfalsewithout them (bbba75bc59, above; Tests).In six files, a stream or call a test didn't script fails that test
d9b8eddcc8changes theScriptMembranemocks inoutgoing-stream,stop-without-streamandtool-result-guard: a stream the framework opens with no script left is recorded before the mock throws, and anafterEachin each file fails the test that opened one, naming each such stream and the last message its request carried. The three undelivered-publish tests inoutgoing-streamnow script the turn their bounce wakes and check that its request carries the bounce notice.062c6fc0c0does the same for the hand-written stubs inconversation-spawn-in-flight,state-read-and-write-errorsandtool-result-well-formed: each records a call it has no response for, and its test checks there was none.Four more commits
20fe9ac6c9answers an internal review ofstop-with-work-in-flight: its header now says which pushes to a stopped framework throw, its wait for a tool to start is bounded at 5 s, and a stubbedpushCoalesceris restored infinally.96445deb6fmakesworkspace-three-way's two gate tests wait until one of chokidar's reads has reached the closed gate before they open it. One of the two is the testd700dce18bfailed once in the whole-suite rounds below. The commit's message gives its red-first runs.5c8ee8fcb8has 14 test files andtest/helpers/reader-fork-rig.tsread a fixture's JSONL log through a new helper,test/helpers/json-lines.ts. ItsjsonLinesparses the file only up to its last newline, and returns[]for a file that doesn't exist yet (Tests). The commit's message lists the reads it changes, which it says are each made while the fixture can still be writing, and the reads it leaves, each with why.662c6cd4c6, this head, makes "a resident's left script wakes it with wake_agent(), and its failure wakes it too" intest/code-execution.test.tswait for the first turn'sinference:completedbefore it releases the script's first slow call, which the script awaits before its wake, as the test's comment "Turn 1: start the script, which yields; then rest." describes. At68db93392a, an earlier head of this branch, the test failed on all four of this pull request's Bun CI legs (Tests). The commit's message gives the cause, which it found by run.For what each of the other commits changes and why, read its message.
This branch was caught up onto
mainc5306693e0on October 30, which gave every commit a new hash. Bygit range-diff, 168 of the 170 commits make the same changes as before. The other two are6bb7b446b0, which slimstest/mcpl-transport-close.test.ts, where #935 had changed one of the lines it removes, and5c8ee8fcb8, whose diff differs only in the unchanged lines around an import it adds totest/mcpl-down-for-good.test.ts.Open pull requests
By
git merge-treeon October 30 at 13:04 UTC, 43 other pull requests were open. Five of them conflict with this head in files where they don't conflict withmainc5306693e0: #844 (next section) and four of the house's own. #690 merges cleanly withmainand conflicts with this head insrc/framework.ts. #850, #256 and #257 already conflict withmain, and this head adds one file to each one's conflicts:test/event-gate-judge.test.tsto #850's, andtest/inference-exhaustion-rewind.test.tsto #256's and #257's. In #256, #257 and seven more, a conflict in a file that conflicts either way has different lines with this head, because lines this change makes fall inside it. The seven are the house's #253, antra-tess's #195 and #201 (below), and#155,#163,#177and#436, from authors outside the house. The other 31 merge with this head as they do withmain, with the same conflicted files and conflict lines.What each part is for
93cdb7d628's one reading ofsupportsInjectedMessages. Atmain, the three places Problem names give a stream that leaves the field outtrue,falseandtrue. Reading it as=== truein each place gives such a stream one answer.MockMembraneandMockYieldingStreamdeclaringtrue, so the tests that use them get the declaration membrane 0.8.0's own tests assert for a yielding stream's tool round on the native path (Problem).|| !currentState.streamat the tool boundary keeps the direct API's boundary, which has no stream, entering that branch. Two new tests cover it (Changes).bbba75bc59's three removals and its doc line each have their reason in Problem's fourth bullet and the bullet forbbba75bc59under Changes.c58280c59f's removal of the derive skip guards: "The derive skip guards", under Changes.d9b8eddcc8,062c6fc0c0): Problem's second bullet.20fe9ac6c9,96445deb6f,5c8ee8fcb8and662c6cd4c6: each one's bullet under "Four more commits".With #844
@tel-0s, by
git merge-treeon October 30 at 13:05 UTC, #844 ataeac830921includesmain'sc5306693e0and merges with it cleanly, and conflicts with this head,662c6cd4c6, in 20 files:src/agent.ts,src/framework.tsand 18 test files. In one of them,test/deferred-write-failures.test.ts, #844 deletes the file and this change modifies it.With #195 and #201
@antra-tess, by
git merge-treeon October 30 at 13:04 UTC, each of these conflicts with this head in the same files as withmainc5306693e0, and insrc/framework.tsone conflict's lines differ: this head's side carries this change's tool-boundary condition,currentState.supportsInjectedMessages === true || !currentState.stream, and the comment above it, wheremain's side has!== false. In #195's conflicts intest/channel-registry-routing.test.tsandtest/present-while-acting.test.ts, this head's side carries this change's slimmed lines.Tests
The whole suite, before and after. These rounds ran at
32da22bfdd, an earlier head of this branch, and atd700dce18b, thenmain. Since then the branch has moved ontomainc5306693e0, after #910, #909, #913, #912, #929, #932, #930 and #935 merged,5c8ee8fcb8and662c6cd4c6have been added, and fixes from a third internal review have changed four commits; the rounds ran none of these.32da22bfddmerged ontoc5306693e0bygit merge-treeconflicts in one file,test/mcpl-transport-close.test.ts, which #935 changed. In the other files it differs from96445deb6f, the commit before5c8ee8fcb8, in six (git diff --numstat):src/agent.ts+1 -1, a doc comment;test/prose-drafts.test.ts+23, one more test;test/turn-inputs-pin.test.ts+2 -9, its guard removed; and, from those fixes,test/deferred-window-only.test.ts+1,test/mcpl-tool-resolution.test.ts+10 -3 andtest/operator-surgery.test.ts+10 -3. Linux x86_64 (kernel 6.12), 12 CPUs, each run pinned to CPUs 0–3 withtaskset; nodejs.org's Node 22.12.0 (npm 10.9.0). Dependencies were resolved once at32da22bfddwith no lockfile, as CI has none, and then installed withnpm cifrom that lockfile on both sides:@animalabs/membrane0.8.0,@animalabs/context-manager0.24.1,@animalabs/chronicle0.5.0,typescript7.0.2,@types/node22.20.5.npm run build, thennpm testwithAF_TEST_CONCURRENCY=4. Two alternating rounds,d700dce18bfirst, each run starting once the one-minute load was under 8. CPU is the run's user plus system time.d700dce18b32da22bfddd700dce18b32da22bfdd32da22bfdd. Wall time 7.2% less in round 1 and 13.0% less in round 2. CPU time 0.7% and 1.7% less, which is about 0.2% and 1.2% less per test.32da22bfdd. Round 2's run ofd700dce18bended at a one-minute load of 15.57, so its 13.0% is likely overstated.d700dce18b's one failure, in round 1, isworkspace-three-way's "starts once the watcher is ready, …", at its check "some of chokidar's directory reads waited at the closed gate", one of the two tests96445deb6fchanges.Single files, red first. Linux, Node 24.22.2 (npm 11.19.1), each commit a clean checkout with dependencies resolved by
npm installwithnode_modulesandpackage-lock.jsonremoved: membrane 0.8.0, context-manager 0.24.2, chronicle 0.5.1, typescript 7.0.2,@types/node22.20.5, except where a pin is named.npm run build, then each file alone withnode --test. These runs are at this head,662c6cd4c6, and atmain(c5306693e0), except the last bullet's, which names its own commits.main,outgoing-streampasses 57 of 57 with three "unexpected extra inference" lines, and the three undelivered-publish tests take 3,051, 3,048 and 3,053 ms.outgoing-stream56,stop-without-stream43,tool-result-guard156,conversation-spawn-in-flight6,state-read-and-write-errors12,tool-result-well-formed16), with no such line, and the three tests take 22, 27 and 26 ms.afterEachcheck "every stream the framework opened was one the test scripted".stop-without-streamandtool-result-guard, a test that runs a turn with no stream scripted; in each stub file, one more inference after the scripted ones, its error caught. Atmainevery test in the five files passes with the addition. Here each addition fails its test at the new check, "every stream the framework opened was one the test scripted" for the two mocks and "every inference the framework asked for was one the test scripted" for the stubs. Intool-result-well-formedthe extra inference is in a helper two tests share, so both fail.dist/src/framework.js, run against ten of the eleven test files93cdb7d628changes, all butframework.test.ts. All ten pass with the build unchanged,prose-drafts254 of 254.main's!== falseback at the tool boundary: onlydeferred-window-only's "a stream that doesn't say whether it carries injections is taken as one that can't: …" fails.|| !currentState.streamtaken out: onlydeferred-window-only's "a boundary with no stream, the direct API's, …" andclock-rows' "after a direct-API tool round whose result ends the turn: …" fail.presentsInjectionsread with?? trueagain: onlyprose-drafts' "a stream that doesn't say whether it carries injections gets no mid-turn notice: …" fails, at "no mid-turn notice: a stream that doesn't say is taken as one that carries no injections".JsStoregivesContextManager.supportsDerivation(store) === true, and the nine files whose guards this change removes run 68 tests: 68 pass, none skipped.createBranchWithStatesandcreateBranchAtWithStatesfrom chronicle'sJsStore.prototype, at both commits:supportsDerivationreturnsfalse, and 17 of the 68 tests fail and none skip. Twelve, incompression-after-release,dendrite-derive,dendrite-fork-viewandturn-inputs-pin, fail withContextDerivationUnsupportedError:Context derivation needs branch-bound store handles (JsStore.view) and branches forked with selected states (createBranchWithStates, createBranchAtWithStates), which this @animalabs/chronicle build does not provide. The other five, one in eachtune-out-readerfile, fail by timing out, three "waiting for: a reader fork is derived", one "waiting for: a reader fork is derived (coalesced wake)" and one "waiting for old reader request and new hold", and each logs a reader fork failing with that same message.5c8ee8fcb8). No test fails without this commit, since a read can't be made to land in the middle of a fixture's append on demand.test/helpers/json-lines.ts, imported as it is at this head into a script run with Node 24.22.2, which strips its types: for a file whose third line stops inside a string ({"a":1}and{"a":2}, each ending in a newline, then{"a":"b),jsonLinesreturns the first two lines, andreadFileSync(path, 'utf8').split('\n').filter(Boolean).map((l) => JSON.parse(l))throwsSyntaxError: Unterminated string in JSON at position 7 (line 1 column 8). For a path that doesn't exist, and for a file holding only{"a":"b,jsonLinesreturns[].tune-out-readerfiles that usereader-fork-rig.deferred-window-only85,mcpl-tool-resolution6,mcpl-transport-close34 andoperator-surgery27.main's newer tests. Built at this head, each of the 15 test files that fix(gate): refusals name fields as the agent wrote them, and file errors give their path and what to do #910, fix(tune-out): a channel's cancel makes its dump durable before it records the end #909 and fix(framework): awaited tool calls and the turn's dispatch share one routing function #913 changed or added passes, and so does each of the 13 test files thatmainchanged betweenfd6d6a362candc5306693e0. Eight of them import this change'sMockMembrane, among them fix(gate): refusals name fields as the agent wrote them, and file errors give their path and what to do #910'sgate-messages-as-written(18 of 18) and fix(framework): awaited tool calls and the turn's dispatch share one routing function #913'sawaited-tool-routes(10 of 10).662c6cd4c6). It iscode-execution's "a resident's left script wakes it with wake_agent(), and its failure wakes it too".68db93392a, an earlier head of this branch (https://github.com/anima-research/agent-framework/actions/runs/41039082681) failed on all four "code_execution under Bun" legs, Linux and macOS at Bun 1.4.0 and 1.4.3. In each,test/code-execution.test.tspassed 98 of 99, and this test failed at "timed out waiting for the wake's inference request", after the line "[wake] prime: dropped 1 wake(s) for writes an answered request carried, …". Every other file the job runs passed. Ate53cd6bade, the branch's next head (https://github.com/anima-research/agent-framework/actions/runs/41044640947), and at this head (https://github.com/anima-research/agent-framework/actions/runs/41056481378), all four legs pass.node_modulesandpackage-lock.jsonremoved and dependencies installed bynpm install --ignore-scripts, as CI's Bun job does, and the test files run from source withbun test --timeout 60000. The runs that printed their dependencies' versions printed chronicle 0.5.1, context-manager 0.24.2, membrane 0.8.0, typescript 7.0.2 and@types/node22.20.5. The three runs at a low load below ran before npm published context-manager 0.24.2.68db93392a, in runs that started and ended at one-minute loads between 12.8 and 17.5,test/code-execution.test.tsfailed this test in two of six runs, each time at the same line after the same[wake]line, and passed 99 of 99 in the other four. Alone, the test passed in each of three such runs, and failed in each of three that started at a one-minute load of 0.10, at the same line after the same[wake]line.e53cd6bade, the file passed 99 of 99 in each of six runs, the test alone passed in each of ten, and each of the other ten files the Bun job runs passed. Built withnpm run build, the file passes 99 of 99 under Node. At this head, the file passed 99 of 99 in each of ten runs.fd6d6a362c, an earliermain, the test alone passed in each of three runs, and the file passed 99 of 99.scriptWake'sthis.pendingRequests.push({ … reason: 'script:wake', … })insrc/framework.tsmade to run only whenagentName === '', the test alone fails ate53cd6badein each of three runs, at "timed out waiting for the wake's inference request".Not verified
main's. This pull request's CI runs give its time, and we haven't compared them withmain's.32da22bfdd, and this pull request's CI ran it at this head, on Linux at Node 22.12.0.dd31225c71, a commit that no branch of this repository holds now: 0.29.0 and 26 commits after it, from October 28, so a test that isn't in the test files at that commit wasn't in it.test/discord-awareness-outbox.test.ts:524–:535).changelog.d/unsupported-compat.changed.mdandchangelog.d/injection-declared.changed.md.Hilda-2580 and Cordelia-2434 judged the flagged tests and made the slimming commits, and Hilda-2580 made the
srccommits, the #886 commit and20fe9ac6c9. Nigel-2405 made the two commits for unscripted streams and calls, and Pippa-2571 the gate-wait fix. Ada-1017 placed the work, set the rules the judging followed, and ruled on the questions put to her, cover-changing removals among them. Effie-2756 showed by run what membrane's XML path does with a message said while a turn runs. Esther-2584 and Effie-2289, who built none of it, read the change by file as it stood before the review fixes: Effie-2289 most of the test files Cordelia-2434 slimmed, and Esther-2584 the rest. Esther-2584 then read Hilda-2580's first round of review fixes, and, word for word, the last round of review fixes and the two test changes after32da22bfdd. Duncan-2410 weighed an earlier version for whether each part is worth what it adds, and Phoebe-2419 read Pippa-2571's commit. Octavia-2412 made5c8ee8fcb8, and Hilda-2580 read it word for word. Ansel-2428 then weighed the branch as it stood before the third review's fixes for whether each part is worth what it adds, and a third run of the house's internal review read what had changed in that version since the second run. Hilda-2580 and Cordelia-2434 made the fixes it led to, in four commits, and Fern-2426 read them word for word. Hilda-2580 made662c6cd4c6, Fern-2426 read it word for word, and a fourth run of the internal review read it. Hilda-2580 caught the branch up ontomainc5306693e0, Ada-1017 ruled on its one resolved conflict, and Fern-2426 read the resolution. Daniel-2612 wrote this description.