Repository navigation
Force band never folds during a long turn of a prefix-bound thinking model (Opus 5.5): context stays at 85-99% until the user writes again #729
Description
Versions: Magic Context 0.49.4 (the same code is in 0.49.12), OpenCode 1.19.1, anthropic/claude-opus-5-5 (one of PREFIX_BOUND_THINKING_MODELS), cache_ttl 59m, execute threshold 65%, so the force band is 85%.
What happened
A primary session ran one long turn. It started with a user message at 22:31 local time, and the next real user message came at 03:25. Everything in between was oh-my-openagent todo continuations and background-task notices. Their parts are synthetic: true, so activeAnthropicTurnStart() counts them as part of the same turn: about 340 steps. Within it the context climbed from 405k to 900k tokens (99% of the usable 911k). From about 23:25 (20:25Z) until the turn's last step at 00:51 (21:51Z) it stayed between 85% and 99%, and the session then sat at 92% until 03:25.
The log still covers 105 of those requests (20:53–21:52Z). Every one of them has transform scheduler: … decision=execute and pp.prefixPreflight foldExecuted=false m1Refreshed=false, and none has an m[0] HARD fold decision line. The other lines repeat (counts for 19:55–21:52Z):
historian redundancy skip: summarizer not needed this pass — force band is 85%; queued/automatic drops are projected to reclaim to 4.0% (target 48.8%) on this pass (ride=explicitFlush) 229x
pending ops held — reason=no originating cache-bust opportunity; scheduler=execute, historianRunning=false 139x
emergency tiered drop skipped: no-candidates; fixed floor ≈761258 already exceeds ceiling 591940: tool drops cannot reach the target 88x
The historian kept working in the force band: historian_runs has incremental runs about once a minute, one compartment each. So the summary existed, but it never reached the context. That happened only at 03:25, on the next real user message: m[0] HARD fold decision: reason=ttl_idle executed=true, 837k → 318k.
Why (from reading dist/index-2mqps3c1.js)
- During an active Anthropic thinking turn on a prefix-bound model,
activeThinkingPrefixHoldsetsfreezeM0M1. ThenfoldDueDecisionandsoftRefreshOpportunityare both false, and the m[0]/m[1] block is skipped on every pass of the turn. - The same hold clears
isExplicitFlushanddeferredMaterialize(if (activeThinkingPrefixHold) { … }). So theexplicitFlushride that the redundancy skip counted on does not exist on that pass, and the queued drops are held. - The only release inside the turn is
releaseThinkingForRewrite(true)at>= 95%when every front tool candidate is thinking-protected (or on a fit retry). It runs after the fold decision, so it can only free tool drops. When tool drops cannot reach the target, nothing is reclaimed.
This looks like a remaining case of #524. The skip counts on a ride that this pass then cancels. The historian publish that the skip suppresses could not land either, because the hold also blocks the m[1] refresh. Because synthetic user messages do not end a turn, a session that works on its own for hours can stay in the force band until the user writes again or the window overflows.
Reproduction
A scratch OpenCode 1.19.1 with the same plugins and anthropic/claude-opus-5-5 set to limit: { context: 200000, output: 32000 }, so Magic Context computes contextLimit=168000. One turn, variant high: "run 22 bash commands, one per step, each prints 150 lines" (about 4.8k tokens per step).
Unpatched 0.49.4 (run A). The context went from 79k to 186k tokens, 110.8% of the 168k limit, inside the one turn. After the first render, every pass (25) logged foldExecuted=false m1Refreshed=false. The historian force-fired at 85% and published 6 compartments from step 19 on; none of them reached the context. emergency tiered drop skipped: no-candidates 9x.
With the change below (run B, same prompt). The first compartment was published at step 24, at 87.9%. On that pass the hold was released, m1Refreshed=true, proactive thinking strip: busting pass froze reasoning of 3 assistant(s), and the prefix trim cut the summarized rows: 147.7k (87.9%) → 99.7k (59.4%). The next step read 99.7k from cache. No provider errors, and the turn finished all 22 blocks.
At B's first force-band pass, MC's own one-time ride=force emergency tiered drop also cut 7 tool outputs (28k). In run A the same step found no candidates, because its tool outputs sat before a signed thinking block. The change did nothing on that pass: there was no compartment yet.
The same anchors are in 0.49.12, and the change applies there unchanged.
What we run locally
Just before const foldDueDecision = …: in the force band, when the session has compartments that m[0] does not cover yet, release the hold once per new compartment, with the same call MC already uses at 95%. The fold then runs on that pass.
if (activeThinkingPrefixHold && forceMaterialization && m0M1EnabledForFold && args.m0M1 && args.directoryPrefixHold !== true) {
try {
const ocForceBand = globalThis.__ocForceBandFold ??= new Map;
const ocLatestSeq = args.db.prepare("SELECT MAX(sequence) AS s FROM compartments WHERE session_id = ?").get(args.sessionId)?.s ?? -1;
const ocCovered = args.sessionMeta.cachedM0Bytes == null ? -1 : args.sessionMeta.cachedM0MaxCompartmentSeq ?? -1;
if (ocLatestSeq > ocCovered && ocLatestSeq > (ocForceBand.get(args.sessionId) ?? -1)) {
ocForceBand.set(args.sessionId, ocLatestSeq);
releaseThinkingForRewrite(true);
sessionLog2(args.sessionId, `oc-patch force-band fold: released the thinking hold at …`);
}
} catch (error) {
sessionLog2(args.sessionId, "oc-patch force-band fold: release failed, hold kept:", error);
}
}The cost is one prefix rebuild plus the current turn's signed thinking, instead of reading 800k+ cached tokens on every step for hours. You may prefer another shape, for example ending the hold in the force band only when the fold would bring the context under the execute threshold. Per the contributing guide we have not opened a PR.
Activity
magic-alfonso commentedon Oct 21, 2026
Thanks again for the detailed trace and for testing a patch locally. It made the cause easy to confirm.
We're changing the behaviour. Today, Magic Context holds every compaction for the whole current turn on prefix-bound thinking models such as Opus 5.5, and synthetic continuations never end that turn. That's why your session sat at 85-99% for hours.
The new rule removes that blanket hold. A long turn compacts at the normal execute threshold like any other model, stripping the turn's signed thinking once when it does. The one exception is a short, bounded hold right after heavy reasoning: if one of the last 15 steps used more than 1,500 reasoning tokens, compaction waits until that step leaves the window. The wait never extends past the execute threshold plus 10 points, so a turn can't get stuck in the force band again.
We also fix the summarizer skip you found, which credited a reduction that the same pass then cancelled.
A fix is being built for all hosts now and will go through a cache review before it ships. We'll post here when it's released.
magic-alfonso commentedon Oct 22, 2026
Fixed in 0.49.18. Thanks @moynzzz for the detailed report and logs.
On Claude models whose thinking binds to the conversation prefix (Opus 5.5, Sonnet 5.5, Fable 5.1, Haiku 5.5), Magic Context used to hold every history fold and queued drop for the whole turn. A long autonomous run, continued by todo and background notices rather than user messages, could therefore sit at 85-99% of the window for hours.
The hold is now bounded. It applies only while one of the turn's last 15 steps used heavy reasoning, and never at or above the execute threshold plus 10 points. Outside it, the turn compacts mid-run like any other model and strips that turn's signed thinking once. We verified live that all four model families accept that.
Update with
npmor your usual plugin update, then restart the host.