Repository navigation
fix(cap): the speculative cap holds L1 production only while an L1 merge is queued - #243
Merged
nissa-seru merged 1 commit intoOct 19, 2026
Conversation
…rge is queued (anima-research#240) Past maxSpeculativeL1s, tick took no chunk to compress, the background drain stopped, and the compression work count left the queued chunks out, on the premise that a merge would bring the count of unmerged L1s back down. Where no merge could, compression stopped for good, and no tick, message or compile moved it: - a cap below mergeThreshold - 1, since no merge forms below mergeThreshold unmerged L1s; - unmerged L1s that can never merge filling the cap: a lone interior summary, a pruned source, the wide-span quarantine, or the sources of a merge in the merge quarantine, which contiguousMergeCandidates offers again while its run is the oldest that qualifies, so enqueueMerge refuses it and no later L1 merge forms; - any cap under hierarchical: false, where tick runs no merge. speculativeCapHolds() is now the one rule: the cap holds L1 production only while an L1->L2 merge is queued and hierarchical is on, read as tick's merge step reads it. tick's L1 step and the work count read it. The drain no longer checks the cap itself. It reached that check only with no merge queued or with hierarchical off, and the cap now holds nothing in either case. With a merge queued, tick's L1 step comes first and reads the cap. Production waits for a queued L1 merge however long that merge waits, on a hold over its sources or through failing calls, as it did on main. The option's doc said the cap counted queued chunks and held back only the auto-tick. It now says when the cap holds, that a cap below mergeThreshold - 1 lets unmerged L1s rise to the threshold, and that under hierarchical: false it holds nothing. The comments on isAtSpeculativeCap, the drain and the work count, the clearedBy doc in the picker, the design doc's line and the test file's header said the old rule, and now say this one. A changelog fragment says it for hosts. Tests: each of the three states compresses every chunk, through both merge checks (checkMergeThreshold and the recursive one under speculativeProduction), and merges once the newest run reaches the threshold where merges run; the auto-tick drain does the same past the cap; auto-tick fires past the cap with no merge queued, where the old test asserted the stall; past the cap, tick takes no chunk while an L1 merge is queued, also behind a merge above L1 at the head, and takes one once it has run; under the cap, a queued L1 merge holds nothing; a queued merge above L1 holds no L1 production; and the over-budget count names the queued chunks past the cap only when no L1 merge is queued. The in-flight count's test queues an L1 merge that counts nothing, so the cap holds and the work in flight is again all that can count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Required label not found on this PR. |
nissa-seru
pushed a commit
to nissa-seru/context-manager
that referenced
this pull request
Oct 19, 2026
…its as below it anima-research#243, merged from main in the commit before this, makes the speculative cap hold L1 production only while an L1 merge is queued to bring the count back down. Before it, tick took no chunk past the cap, so the retry that would fill a hole among the unmerged L1s never came there, and a waiting L1 run merged at two as a last resort so that the count could fall back under the cap. With anima-research#243's rule the retry comes once the count is back under the cap or no L1 merge is left queued, so the last resort only merged lopsided before a summary on its way. It goes, and contiguousMergeCandidates treats a waiting run past the cap as it does below it. The comment, the changelog fragment's sentence and three tests follow. The first test now pins that past the cap the run still waits and that, with no L1 merge queued, the cap holds no L1 production. The other two keep their cases, with messages that no longer name a last resort. 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.
TL;DR: past
maxSpeculativeL1s, compressing chunks into L1s now waits only while an L1→L2 merge is queued, withhierarchicalon, to bring the count of unmerged L1s back down. Onmainit waits whenever the count is past the cap, so where no merge can come it waits forever: with a cap belowmergeThreshold - 1, with unmerged L1s that can't merge filling the cap (as after a merge goes to the merge quarantine), and underhierarchical: false. The option's documentation also described another rule, and now says this one.Fixes #240.
Problem
On
40987192, three places read the cap as a stop on L1 production:ticktakes no chunk whileisAtSpeculativeCap()(src/strategies/autobiographical.ts:5265), the background drain stops (:5182), and the over-budget work count leaves the queued chunks out (:9516). Each assumes a merge will bring the count of unmerged L1s back under the cap. Three states break that:mergeThreshold - 1. No merge forms belowmergeThresholdunmerged L1s (contiguousMergeCandidates,:8398), and production stops one L1 past the cap, short of that.contiguousMergeCandidatesoffers the oldest run that qualifies, its firstmergeThresholdmembers (:8469), so while that run is the quarantined one it offers the same set each time, andenqueueMergerefuses it (:4385).hierarchical: false.tickruns no merge.Each stops for good, since no tick, message or compile changes the count. On
main, 40 short messages (8 chunks), a merge threshold of 6, a provider that always answers and 50 ticks gave:hierarchical: falseOn
mainwith the merge quarantine, 80 short messages (16 chunks), a threshold of 3, a cap of 4,mergeAttemptLimit: 1, and a provider that refuses the first merge and answers everything else, 80 ticks compressed 5 of 16 chunks, on both merge checks (speculativeProductionoff and on).The option's documentation (
src/types/strategy.ts:1360onmain) said the cap counts queued chunks as well as unmerged L1s, and holds back onlyonNewMessage's auto-tick, leaving a manualtick()or a compile free to compress. The code counts produced, unmerged L1s, andtickitself honours it.Changes
speculativeCapHolds()is the one rule for whether the cap holds L1 production now. It holds past the cap, while an L1→L2 merge is queued, withhierarchicalon.tick's L1 step and the over-budget work count read it.hierarchicalwas off, and the cap holds nothing in either case. With a merge queued, the drain ticks, andtick's L1 step comes first and reads the cap.isAtSpeculativeCap,tick, the drain and the work count, the picker's doc ofclearedBy, the design doc's line on the cap and the cap tests' header say what holds and when. A changelog fragment says it for hosts.What a host will notice
mergeThreshold - 1, unmerged L1s rise to the threshold, where a merge forms, and production waits for that merge. Onmainthey stopped one past the cap, and so did compression.hierarchical: false, the cap holds nothing.shouldCompressPreflightsays no sees the drain compress the queued chunks past the cap when only a merge above L1 is queued, as it already did below the cap. By reading, the drain passes over the preflight for as long as any merge is queued withhierarchicalon, andticktakes one chunk a call before it reaches a merge, so this goes on until the compression queue empties, or until an L1 merge is queued and the cap holds again. Outside its tests, nothing in this repository or in connectome-host overrides the preflight.hierarchicalon, a cap of at leastmergeThreshold - 1and no L1s that can't merge, nothing changes. By reading, past the cap a merge is queued, sincecheckMergeThresholdruns after each L1tickwrites and after each merge, and queues one once there aremergeThresholdunmerged L1s in a run that can merge. Production waits for it as before. One exception by reading: an exact L1tickadopts from the store runs no merge check, so a run it completes waits for the next L1tickwrites, which the cap no longer blocks there. The documented connectome-host setting, a cap of 36 with a threshold of 6, meets the first two conditions.What each part is for
speculativeCapHoldsis one answer to "does the cap hold now", read where the cap used to be read. Its conditions:hierarchicalmust be on. Underhierarchical: false,checkMergeThresholdstill queues a merge thatticknever runs (thehierarchical: falsecase above ended with one queued), so without the condition the cap would hold forever there. It reads the flag astick's merge step does.main, since the rule looks only at what the queue holds.hierarchicalwas off, so the new rule would never hold there.hierarchical: false, and a rejection would turn a host whose compression stopped into one that doesn't start after the upgrade.hierarchical: false.tickcan run now. By reading:holdBlockedIds), so the chunks behind a merge it blocks wait anyway.tickretries one that fails with a rate limit, a network error or a timeout, and moves one that's refused, or keeps failing with another provider error, into the merge quarantine. A merge that never succeeds stops every merge behind it, so bounding it belongs there, not to the cap.Out of scope
tickonmaintakes no chunk, fix(compression): progress past quarantined chunks; transient failures never spend the ladder; pace the lane #138, open, merges an L1 run that waits on a retry once the run holds two. With this change,tickpast the cap with no L1 merge queued takes the next chunk, the retry among them, so the two overlap. Neither branch has the other's change.Other open pull requests
src/strategies/autobiographical.ts, in two places:isAtSpeculativeCap's doc, andtick's first priority, which fix(compression): progress past quarantined chunks; transient failures never spend the ladder; pace the lane #138 rewrites as a loop that checksisAtSpeculativeCap()before each chunk, where this change readsspeculativeCapHolds().main, and gains no conflict from it where it doesn't (git merge-treeof each of the other 18 open heads ontomain40987192, with and without this change).Tests
In
test/speculation-cap.test.ts:speculativeProductionoff and on), and merges once the newest run reaches the threshold where merges run: a cap of 2; a cap of 5 with two L1s whose sources resolve to no message; and a cap of 5 underhierarchical: false;ticktakes no chunk while an L1 merge is queued, and takes one once the merge has run, also with the L1 merge queued behind a merge above L1;In
test/overbudget-diagnostics.test.ts:Checks
On Linux:
Red-first, with Node 24.19.0: with
main's source and these two test files, 10 tests fail. They are the six case runs, the auto-tick drain case, the turned-round auto-tick test, the merge above L1, and the turned-round count. The other new and changed tests pass onmainas well, since there the cap held whenever the count was past it, and never under it.Removals, with Node 24.19.0: each of nine pieces, taken out alone, fails at least one test:
tick's L1 step reading the bare cap (8);hierarchical(2);Taking out the count of the chunk in flight, which is
main's, fails the test of the work in flight.The whole suite, with Node 24.22.0: 1,949 of 1,949.
Not verified
changelog.d/240-speculative-cap-relievable.fixed.md.— Beth-2389 (Claude Opus 5.5). Anarchid reported the defect in #240. Oscar-1005 (Claude Opus 5.5) agreed the design, an internal review read the change, and Abigail-2459 (Claude Opus 5.5) read it and its fix round. Description checked and posted by Fiona-2757 (Claude Opus 5.5) through the shared
nissa-seruaccount.🤖 Written using Anthropic's Claude Opus 5.5 through the Rookery's tooling.