Repository navigation
v0.43.0
Warning
Moving existing OpenCode 1 sessions to OpenCode 2? Run doctor --fix before you upgrade OpenCode. Stop OpenCode, then run npx @cortexkit/magic-context@latest doctor --fix. Compaction markers written by earlier Magic Context versions lack the time.completed field OpenCode 2's conversion requires, so OpenCode 2 drops them, and a long session then loads its entire history on open, using extreme memory and CPU. doctor --fix stamps the field on Magic Context's own markers only. If OpenCode 2 has already converted your store, see "Converting an OpenCode 1 store (and back)" below for the recovery.
Magic Context now supports OpenCode 2. Sessions on a 2.x host get the same context management, memory, history, search, notes and /ctx-* commands as on OpenCode 1. An OpenCode 1 store converts when OpenCode 2 first opens it, and converts back if you downgrade. The last two releases had a failure that stopped turns on OpenCode 2: 0.42.5 does not answer at all on a 2.0.11 host, on a fresh data directory or an aged one, and 0.42.6 refuses every turn on any data directory that has ever held OpenCode 1 sessions, and every turn of any session once it passes its context threshold. If you run OpenCode 2, this is the first release to use.
This release replaces the 0.42.7 and 0.42.8 builds that were prepared but never published. Everything they contained is included here.
Upgrading
-
The schema fence moves from 85 to 90. Coming from 0.42.6, five migrations run on first open:
- v86 adds a per-session
tags_versiontosession_meta, maintained by triggers ontags, so a write from another process no longer forces a full tag-map reload. - v87 decides each session's harness label from the OpenCode store, read-only. Newer
session_message/session_v2activity meansopencode2; newermessage/sessionactivity meansopencode; a session absent from both is left alone. It replaces migration v85 from 0.42.6, which rewrote everyopencode2row toopencodeand so mislabelled a real OpenCode 2 install (#475, qoole). v85 is now inert (its version row is still recorded) and v87 repairs what it did, in whichever direction the store supports. An unreadable store changes nothing, logs once, and lists the skipped sessions indoctor. - v88 adds
session_meta.coordinate_generationandcompartments.rebase_statusfor the store-conversion rebase described under OpenCode 2. - v89 adds an indexed
message_time_msto the message search index forctx_searchdate ranges. Existing messages are dated by a background backfill after the upgrade; a date-bounded search skips messages the backfill has not reached yet. - v90 records the owner process id on historian leases, so a lease left by a process that has exited is reclaimed right away instead of blocking the historian until it expires.
All migrations are idempotent.
- v86 adds a per-session
-
Restart every process that shares the database at the same time: OpenCode servers, Pi and OMP processes, and instances in other projects. A new build will not migrate while an older process holds the database. Once the migration has run, an older build (0.42.6 or earlier, fence 85 or lower) refuses the database at its next open instead of running without context management. With the default
fail_closed_blocking: trueit blocks the primary-session prompt with a recovery error. Update pinned installs too. Downgrading the plugin does not downgrade the database. -
Changing the historian and dreamer models no longer needs a restart. Keys marked Live in the configuration reference now reload on their own: historian and dreamer models, fallbacks and variants/thinking levels for each harness, dreamer task lists and schedules,
mural.model,historian_timeout_ms,historian.two_pass, the commit-cluster trigger, git-commit indexing,memory.auto_promoteandtoast_duration_ms. A change applies from the next historian run, dreamer run or dream-timer tick; a run already in progress keeps its settings. The same applies on OpenCode 1, OpenCode 2, Pi and the Rust module's historian chain. If the edited file is malformed, the last good configuration stays active and/ctx-statusreports the error. Everything else still requires a host restart. See Changing config without a restart. The dashboard marks live keys with a badge. -
Expect one prompt-cache rewrite per session on the first turn after the upgrade. The guidance block and the
ctx_*tool descriptions changed (see "What the agent sees"). This happens once at the upgrade and does not recur. -
Local embeddings re-embed once. Both plugins now bundle Transformers 4.3.0 with optional
onnxruntime-node1.30.0, which clears the high-severityadm-zipadvisories in the published packages (#480, ice-fortress). The native runtime produces different vector bytes from the same model file (the WASM runtime does not), so the local embedding identity now includes the runtime versions, and existing vectors are rebuilt instead of mixed with new ones. -
Moving an existing install to OpenCode 2: stop the host and run
magic-context doctor --fixbefore you upgrade OpenCode. See "Converting an OpenCode 1 store" below for why and for the recovery if conversion already happened. -
Rust transform mode fixes ship in two places: some in the npm package (take effect after an OpenCode restart) and some in the
ck-mcmodule binary (take effect after a module restart). Each item below says which.
OpenCode 2
What works
Built against the OpenCode 2.0.5 plugin API and exercised on real 2.0.5, 2.0.11, 2.0.12, 2.0.14 and 2.0.15 hosts. The host-behavior test suite runs against OpenCode 2.0.15 in CI next to OpenCode 1, Pi and OMP, and every difference the host imposes is declared in PARITY.md rather than patched over. On 2.x you get:
- the transform, tagging,
ctx_reducedrops, historian compartments, memory, notes, search and emergency handling, as on OpenCode 1; - all six
/ctx-*commands, registered with the host (listed by its command API and runnable against a session with no TUI); - the real terminal sidebar and the same
/ctx-statusdialog OpenCode 1 draws; - historian and text-only dreamer runs on their own configured models, in hidden sessions the plugin cleans up;
- per-model
ctx_*tool descriptions (OpenCode 2 carries the tool map with each request).
Rust transform mode is not available on OpenCode 2 in this release; see MC-S06 below.
The new OpenCode 2 page in the docs covers installing on 2.x, moving an OpenCode 1 install, what converts in each direction, the known limits and where the log lives.
Converting an OpenCode 1 store (and back)
OpenCode 2 converts an OpenCode 1 opencode.db in place the first time it opens it and keeps the v1 tables beside its own. Several things had to hold up across that conversion:
- Session coordinates survive the conversion, in both directions (#492 finding 1, null-axiom). When OpenCode 2 opens a 1.x store it splits synthetic content into rows of its own and replaces completed compaction pairs with one native record. Every positional ordinal Magic Context had saved for that session moved by one or more: a compartment covering messages 4–9 could omit its own last message or take in one outside it, a queued
ctx_reducedrop for one fragment applied to the merged text, and the message index carried the last message twice. Downgrading moves them back. Each session now records the projection its coordinates were derived against (coordinate_generation). The first pass after a flip in either direction re-derives every saved coordinate from surviving message ids in one transaction: compartments and their facts, the message index and its watermarks, chunk windows, note anchors and the protected-tail floor. It then pays one priced fold for that flip. A compartment whose anchor no longer resolves is markedunresolved: range recovery refuses it, but it still renders in<session-history>and stays readable by id, and it is re-evaluated on the next flip. Nothing is guessed. Queued drops that would broaden are discarded and reported. A session first seen by this build is stamped, not rebased, unless its store was already converted before this build first saw it; then it rebases from the projection its harness label implies.doctorlists sessions still pending a rebase and unresolved compartments per session, and the log carries one line per rebased session. Verified by driving a session through a real 1.18.30 host, a real 2.0.5 conversion, and 1.18.30 again, with the plugin loaded on every boot. Fresh OpenCode 2 sessions, Pi and project memory were never affected. - Magic Context compaction markers survive the conversion. OpenCode 2 drops a v1 compaction pair whose summary has no
time.completed. Older Magic Context markers omitted that field, so converted long sessions could load their entire history, use extreme memory and CPU, and never reach the plugin. New markers stamp matching created and completed times, andmagic-context doctorreports affected Magic Context rows without touching native summaries. Before upgrading OpenCode, stop the host and runmagic-context doctor --fixto backfill them. Correction (2026-09-25): an earlier version of these notes recommended clearingkv.migration.v1-v2to reconvert a store whendoctorreported missing converted markers. Do not do that: reconversion rebuilds every session from its OpenCode 1 rows and deletes everything added on OpenCode 2 since the first conversion (#493). No recovery is needed. Magic Context on OpenCode 2 keeps its own history boundary, and newerdoctorversions report a missing converted marker as informational. If you already ran it, restoreopencode.db(with its-waland-shm) from a backup. - Long-session reads are page-bounded. The v2 adapter decoded the entire session on every raw read; on long sessions this took minutes and gigabytes before the first turn. Trigger, snapshot, index and historian paths no longer decode the full
session_messagehistory to resolve one id, ordinal, count or short range. - Publishing historian output is bounded too. After a historian run on a 135,000-message converted session, preparing the drop keys walked the whole session from message 0 in 32-row pages, about 4,200 queries and 11 minutes 50 seconds before the first write. It now reads only the newly covered range, and boundary validation checks that the id exists instead of counting a prefix.
- Synthetic rows created by the conversion no longer strand the historian between compartments. Rebase absorbs synthetic-only gaps into the earlier compartment and reports narrative gaps without hiding them. Tail-cached validation fetches an older synthetic-only gap from storage instead of treating the uncached row as missing.
- A converted session already past its window recovers on its first priced pass. Pressure is read against the model on the request draft, and a usage reading from a different model is ignored instead of driving decisions for the current one.
Known limits on OpenCode 2
These come from the host and are documented in PARITY.md:
- Echoed tags can show in the transcript. OpenCode 1 lets a plugin rewrite an assistant's text once it completes, which is where a
§N§tag the model echoed at the start of a reply is stripped before it is saved. OpenCode 2 has no hook after text completion, so an echoed tag is saved and shown as the model wrote it, most often with weaker models. What the model receives is unchanged: the next request strips and re-tags it exactly as on 1.x. The fix needs an upstream hook. - A first start can be killed mid-conversion. Converting a large store can take several minutes, and
opencode service startmay kill the server as unresponsive while it runs. Startopencode serve --port Nby hand for that first conversion and leave it until themigration.v1-v2row reads{"phase":"completed"}. - Seven dreamer tasks cannot run (
map-memories,verify,verify-broad,curate,retrospective,maintain-docs,refresh-primers): they need a tool loop and the hidden-completion carrier has none. Each is named as MC-D08. - Hidden sessions are unparented roots, and one created on a host that registers no service (a plain
opencode serveor--standalone) cannot be deleted by the plugin; it stays retriable as MC-H02. - Command results only reach a connected terminal UI; the work runs on every client. The sidebar is hidden by default (
ctrl+x b).
Fixes for failures that stopped turns
- OpenCode 2 refused every prompt on a data directory that had ever held OpenCode 1 sessions (#493, deltacat). The converted file carries
messageandpartas well assession_messageandsession_v2. The store-generation guard added in 0.42.6 read any store carryingmessageandpartas OpenCode 1 and refused generation-specific access to it; on the v2 lane that refusal became an interrupt before the provider. The prompt was accepted, no transform ran, no provider request was made, the host recorded the turn asidle outcome=interrupted, and the TUI painted nothing after your own message. It was not a slow first prompt: on the reproduction the first turn was refused in 453 ms and the next three in 18, 13 and 13 ms, on every turn of every session. A store carryingsession_messageandsession_v2is now accepted as v2 whatever v1 tables it kept, becausesession_v2is written only by an OpenCode 2 host. Detection is otherwise unchanged, so a live 1.18.x store is still v1 and a downgrade is still safe. Verified end to end on the reporter's shape: an aged root driven by OpenCode 1.18.30 with 0.42.5, then reopened with@opencode/cli2.0.11 in the real TUI. On 0.42.6,export OPENCODE_DB=opencode2.dbgives OpenCode 2 its own store file and avoids the check. - Refusals before the provider are no longer silent (#493). Any failure in the pre-provider context read (an unreadable host store, a non-durable context database, a permission problem) interrupts the turn, and the reason used to reach only
console.warn, which is OpenCode's own server log.magic-context.log, the file support asks for, said nothing, which is why this issue took two rounds of triage from a reporter who did collect diagnostics. Every refusal arm now logs its cause and session in the plugin log before interrupting. This changes whether you can find out why a turn was refused, not whether it is. - A session past its context threshold refused every following turn. The pre-provider admission check in 0.42.6 refused at 95% of the model's catalogue window with no upper bound. Everything that could bring usage back down (the transform, compartment scheduling, the historian) runs after that refusal, so nothing ever un-stuck the session: a reading at twice the window was refused exactly like one just over the threshold. Usage already past the provider's window is now admitted, because the provider's own overflow error is the only place the real limit is reported, and the handling that shrinks history runs later in the same pass. Pressure is measured against the raw provider window, not the window minus the reply reserve: on a 100,000-token model the reserved window is 91,808, so 90,000 tokens is 90% of the window but 98% of what is left, and the tighter denominator refused turns that used to reach the historian. A host compaction newer than the measured reply still admits one request. Both properties are pinned by tests that name each one.
- The emergency refusal at 95% reaches you on OpenCode 2. On 1.x the "Context full — /ctx-flush or /clear to continue." notice goes out through the host client; OpenCode 2 has no such client, so the refusal path itself failed. It now shows as an error toast plus a notice in the session. The emergency historian run is joined, with a bounded wait, before the turn is refused, and emergency reclaim is sized from provider-reported usage.
Parity work
- The terminal sidebar is the real component (#488, from blasphemy's diagnosis in PR 487). The OpenCode 1 sidebar is mounted on the v2
sidebar.contentslot through the host's OpenTUI runtime registry, using the precompiled component rather than a second compile target. The 0.4.2 and 0.5.10 OpenTUI runtime helpers are byte-identical, so no second compile target is needed. The plain-text projection is kept only for a host that registers no runtime modules. A v2 RPC listener backs it, so the numbers it paints are real; the paint itself is proven by a terminal capture. Theme colours come from the tokens the host actually resolves; before, every token fell back to a hex default and section headers painted white on a light theme. A roottui.jsshim lets a directory plugin target load the TUI, and a straytui.ts/tui.mjsthat shadows the shipped shim is named. /ctx-statusis one view on every host. A shared section model owns which rows exist, their labels, order and colours; OpenCode 1 and 2 draw it with the same compiled dialog and Pi with its own line renderer. The mouse-only diagnostics toggle is gone, every row has an explicit theme foreground (collapsed rows were invisible on a light theme), and labels sit in fixed columns with a one-column fallback instead of wrapping mid-word. On Pi the overlay pairs sections in two columns when they fit, using the same width rule as the OpenCode dialog, and falls back to one column in the same order when they do not. Later rendering fixes: values never wrap (one column-width rule for every section), the breakdown bar has no gaps, hygiene figures are whole tokens, the window line drops an internal derivation tag, and the last-response age is humanized.- The
/ctx-*commands exist on OpenCode 2. All six are registered server-side through the host's command transform; the keymap layer is kept for dialog rendering, withctx-flush,ctx-embedandctx-wrapupadded to it, and an app-slot fallback covers the case where the keymap provider is unmounted. /ctx-wrapupworks on OpenCode 2. Its historian runs went through the OpenCode 1 hidden-session path, which has no client on a 2.x host, so the command failed instead of summarizing. It now uses the same OpenCode 2 hidden-session executor as the automatic historian, checked end to end on a real 2.x host. If that executor is ever missing on OpenCode 2, the historian, the memory classifier and the mural cue compressor now fail with an error naming which of them it was, instead of falling back to the OpenCode 1 path.- Dreamer tasks run where their transport allows (#492 findings 2 and 3, null-axiom). Each task's capability is recorded as a transport: single-shot, host-only or tool-loop.
evaluate-smart-notes,review-user-memoriesandpromote-primersrun on OpenCode 2 through the hidden single-shot carrier, and a length-capped or empty completion is refused before parsing. A tool-loop task is refused per task and named MC-D08 in/ctx-dream,/ctx-statusand the run records, instead of the whole scheduler looking idle. Manual/ctx-dreamuses the same capability filter and the scheduler's prune. - Hidden historian sessions are deleted when they are retired (#481 and PR 482, qoole). Under a provider that fails every model (a pool at its weekly quota, say) each failed run retired its hidden session and the next run made a new one: 114 hidden sessions in about two and a half hours. Deletion now goes through the host's own session-delete endpoint, found via the registration file that
serve --servicewrites, using plainfetchwith no runtime dependency on the host package. The retired list is pruned and bounded, and a spaced boot sweep clears an existing backlog. Deletion binds to the owning host registration (pid-matched at discovery, path persisted, a 404 counted only from the owner), and a session that cannot be bound stays retriable as MC-H02 rather than being dropped (#492 findings 5 and 6). A run that failed only on settled provider errors keeps its session and reuses it; that is decided by a typed provider error and a finish check, not a message prefix. Every other failure still retires it. Withkeep_subagents: true, retired sessions are kept by the OpenCode 1 rule instead: any session that ever completed a run, and every historian session, with only a dreamer session none of whose runs completed still deleted. - Image attachments work on OpenCode 2.0.15 (#517, scookiem). 2.0.15 stores an attached image as a typed media object and validates it on every request. The plugin's copy of the conversation turned that object into plain data, so the image turn and every turn after it failed with "Schema validation failed"; images restored after a fold failed the same way with a
mediaTypeerror. The plugin now keeps the host's own object, and rebuilds restored images in the host's shape, byte-identical to what the host sends. If a future OpenCode changes that shape again, a restored image is replaced by a short note naming the file rather than breaking the session, and the swap is logged. Earlier 2.0.x hosts are unaffected. - Hidden runs fail cleanly on OpenCode 2.0.12. 2.0.5 marked a settled provider failure with
finish: "error"; 2.0.12 can record only the terminal session outcome, which the plugin read as a run still going. Both shapes now count as a finished, reusable session, an idle one with no reply is detected as a failure, and the host's own error text is carried into the log and the historian's failure reason so the model chain can move on. - Hidden prompts no longer carry an output cap the provider rejects (#483, qoole). Every hidden prompt set
maxOutputTokensto 32,768, which the host sends asmax_output_tokensand the ChatGPT-subscription (Codex) backend rejects, so every historian run under that login failed. The cap and the temperature now go on the wire only when your config sets them, for every provider alike. - An undeclared variant is dropped with a warning. If
historian.opencode.variantordreamer.opencode.variantnames a reasoning variant the host's model catalog does not declare, the hidden run sends none, logs one warning and shows it in/ctx-status, instead of failing.doctorlists configured variants the catalog does not declare. On 2.x that check used to run OpenCode 1'sopencode models --verbose, could not read a catalog, and only warned that it could not verify; it now reads the catalog from the background service withopencode api model.list --param directory=<project>, and if the service is not running it says to start it withopencode service start. transform_mode: "rust"on OpenCode 2 says so (MC-S06). It resolves to the TypeScript transform with one warning and a status row rather than silently half-resolving, and status and sidebar reads no longer ask a module client that was never started.- Smaller gaps closed. Tool definitions are measured from the request draft, with a four-pass byte check proving no served byte moves, and the lane no longer registers a persistent tool transform on every context pass (#492 finding 4). Cold usage recovery reads the v2 message table with generation guards and finite-token math. There are compaction-off rows in the TUI, typed fail-closed refusals and bounded deletion tombstones. Tool-owner backfill checks for the v1 tables it reads rather than asserting a store generation (the capability-gate shape comes from PR 486, DGVY), and the doctor's boundary-table choice combines the resolved host generation with the store schema (#489, DGVY). A host reporting the
0.0.0-betapre-release shape, or the prefixedopencode v2.0.12version string, is treated as OpenCode 2. - PR #476 and #477 (qoole). Cloned OpenCode 2 tool parts map back to tool-call/tool-result, so reduced payloads no longer leak
{type:"tool"}clones into v2 requests; and 1.18-migrated stores are accepted by OpenCode 2 readers, with detection kept conservative for readers expecting v1.
Historian reliability
- Fit is measured with the producer's own calibration. Historian admission counts the assembled source and system prompt under the selected producer model's calibration, reserves output once, and keeps the 3% estimator margin. Source chunk allowances use the producer model, not the main model. For an unmeasured model the fit uses the larger of 2.0 and the largest measured ratio in the table. A known-window prompt that does not fit is refused before any provider call as
producer_prompt_exceeds_window(Rust:producer_prompt_fit_refused), publishes no range and does not advance coverage. That includes the previous 1.02× oversized atomic-source case: clipping source text to a raw allowance does not prove the complete calibrated prompt fits. - The output reserve is derived, not a fixed 32k. The reserve is your configured historian
maxTokenswhen set; otherwise the model's declared output limit, capped at a quarter of its window; otherwise zero when the window is unknown. Before this, an unsent 32k default could make a small model's usable window zero or negative and starve the historian for good. When a known window and its reserve still leave no room, the historian logsproducer window inconsistent for <model>: window=… reserve=…; sending unguardedonce and sends. - An unknown window is sent with a warning, on all three lanes. When no context window can be found for a producer model (the primary or a fallback), OpenCode, Pi and the Rust module log
producer window unknown for <model>: sending unguardedonce per model and send the prompt. The provider's own answer decides. The Rust module does not borrow the primary model's window for a fallback. Pi now resolves producer windows from the host's model registry first, then the configured value, then a supplied value, then the persisted cache, and logs which one it used. - An admission refusal is not a failure. A prompt refused for size releases its drain budget and does not stamp failure state or back off, so the next pass can cut a smaller chunk right away.
- A timeout falls back to the next model on both lanes (#507, iceteaSA). The Rust module honours
historian_timeout_ms(it had a hardcoded 600 s + 60 s wait) and, after a timed-out attempt, re-checks once for a finished run and otherwise cancels and moves to the next model in the chain. Abort and context overflow still stop the chain; the last model's timeout still fails the run. The waits that join a running historian cover every attempt. The TypeScript lane already did this and is now pinned by a test. - A timeout is recorded as a timeout (#508, iceteaSA). On the TypeScript lane a prompt timeout that resolved instead of rejecting was treated as success: the hidden session was deleted instead of archived and the ledger recorded
completedwith 0 tokens. It now throws, aborts and archives; the ledger recordstimed_outoremptyafter the output check; and a length stop is read from OpenCode's finish fields. - Historian prompts that overflowed by a few tokens (#510, Mte90). On 0.42.6 a historian prompt could go out slightly over the model's window: the chunk was sized with our local tokenizer, which under-counts some models by more than the 3% margin, and fallback models were not checked against their own windows. The complete prompt is now checked against each model in the chain in that model's calibrated units, and a model we have not measured is counted at twice our local count or more. A configured
limit.contextalso takes precedence over a larger catalog window, which a new test pins with the reporter's numbers (120,000 configured, 262,144 advertised). - A historian lease held by a dead process is reclaimed as soon as the owner is confirmed gone (migration v90). A legacy lease, or one whose owner cannot be checked, still waits for its expiry.
- The historian sees the same project-memory block on every lane. Since the compact memory format, the OpenCode and Pi historians received project memories as
#id: factlines, the form meant for the main agent, while the Rust module sent the category-grouped- factlist the historian prompt is written for. The historian never refers to a memory by id; it uses the block only to avoid repeating known facts and to report contradictions. All three lanes now send the grouped list, byte for byte, and CI checks the shared prompt golden on every change. - The historian says what actually failed (#491, expiren). The OpenCode 1 historian reader shows the child assistant's saved error and finish fields instead of collapsing provider and auth failures into "Historian returned no assistant output".
- Rust mode: credential refusals keep their cause (#501 and #502, iceteaSA). A missing credential was classified as
model_unresolvableand cached as permanent. It is nowcredential_unavailable, moves on to the fallback model with its provenance, and is retried after a backoff. Every cross-module route the module opens is declared in its manifest through one registry, so the historian's runner route is no longer an undeclared edge.
Tokenizer calibration
What it measures. Magic Context counts tokens locally, and a local tokenizer can be far from what a provider bills. Calibration is a curated table of ratios (provider count ÷ local count) for three classes of content, system prompt, tool definitions and prose, measured with each provider's free counting endpoint where one exists: Anthropic count_tokens, OpenAI responses/input_tokens, Gemini countTokens, xAI tokenize-text, plus adapters for Moonshot, Z.ai and Meta that could not measure in this round. Examples: Claude Fable 5.1, Opus 5, Opus 5.5 and Sonnet 5 at about 1.51 / 1.55 / 1.57, Sonnet 4.6 at about 1.02 / 1.14 / 1.06, GPT-5.5 near 1.0 with tools at 0.85. Models without their own row inherit from a measured relative, a custom provider id for a known model resolves by its model id, and truly unknown models stay neutral. These are content-dependent approximations, not exact tokenizers. The sidebar's breakdown uses the measured prose ratio for the history, docs, memory and profile rows.
Custom provider ids and new model generations (#512, tppawelgrzesik). A provider id with no measurements of its own (a custom Vertex or Bedrock provider, a relay, a local server) used to count as unmeasured even for a model the table knows, so Sonnet 5 under vertex-eu-anthropic was counted about 1.5× low and emergency drops freed less than they planned. Such a provider now resolves by model id, so vertex-eu-anthropic/claude-sonnet-5 uses the Sonnet 5 row, preferring the model maker's own row when several providers list the same model. An unmeasured model that starts a new major generation takes a measured sibling of that generation over its own family's previous generation: claude-haiku-5 now uses Opus 5's ratios rather than Haiku 4.5's. The TypeScript lanes and the Rust module resolve through the same rules and are checked against one shared set of cases.
What it drives.
- History materialization converts the provider-token history budget into local units on OpenCode, Pi and Rust, so
history_budget_percentagemeans what it says. Conversion happens at render time and is not part of cache identity. - The protected tool window uses calibrated mass, keeping whole tag groups and the newest three structurally.
protected_tokensstays a provider-token floor. - Tail hygiene thresholds, cadence and grace, and the figures in reminders use calibrated units. The ratio bands are unchanged. Saved watermarks convert once, at the first priced pass after the upgrade, and are stamped so a restart does not convert them again.
- Historian fit (above), and emergency planning, which separates the mass currently served from the mass a replacement keeps: a retained skeleton is not credited as reclaimed, and cached content is previewed without being changed.
- Raw fallback and last-known-good replay require a calibrated estimate the plugin trusts and a known window for the current model. A byte-size approximation can reject early but cannot prove fit. Recovery never disarms on incomplete or non-finite telemetry.
How it changes. The calibration a session uses is frozen at priced passes and replayed on defers and restarts, so a new table revision is picked up at the next priced pass and never causes one on its own. There is no runtime learning: the table is curated, and nothing is inferred from sessions.
Intentional behavior changes. Fit guards can refuse requests they used to admit. Unknown models fit at max(2.0, largest measured ratio) rather than trusting raw local counts; a family-inherited ratio counts as measured. On Pi, the storage-failure fallback still lacks complete observations and refuses rather than sending on a byte-only estimate, even for a small replay; durable snapshots are still captured, and an explicitly observed, fitting replay is still admitted. On OpenCode, a model whose tool definitions have not been measured yet takes the largest tool set measured in the process, scaled at the unknown-model ratio, instead of zero. protected_tokens and history_budget_percentage values are not rewritten; they now mean their documented provider units rather than local tokenizer units, which can shorten history or protect fewer tool tags for models whose local tokenizer undercounts. On a Claude Fable 5.1 fixture, a 60,000-provider-token history allowance used to be 60,000 local tokens, about 94,307 provider tokens; it is now 38,173 local tokens, within the 60,000 cap. A 6,000-provider-token protected floor over eight 1,000-local-token tool tags now keeps tags 5–8 instead of 3–8, with the crossing tag kept whole.
Calibration adds no configuration key, migration or saved learning. Tokenizer goldens and stored raw token counts are unchanged. Provider framing and later host rewrites are still not measured, historian reclaim projections still use the older byte-ratio accounting, and the size caps on the injected docs, memory, profile and facts blocks have not all moved to calibrated units.
What the agent sees
- The guidance block is rewritten around one picture: context is a desk, every item carries a
§N§tag,ctx_reducestamps an item as no longer needed, stamped items are cleared in one sweep later, andctx_expandbrings anything back. It explains<session-history>headings as pointers into the archive, tells the agent never to invent a tool call or result that is not on the desk, and lists Magic Context's own markings so the agent neither reproduces them nor treats them as instructions. Subagents get their own shorter version. Thectx_*tool and parameter descriptions were rewritten to match, in both the full and the lightprompt_surfacepresets. ctx_notereads as a glance.readwithout ids lists one row per note (#id · age · title), ready smart notes first and parked smart notes included, with stale rows marked and a paging footer.readwithnote_idsreturns full bodies in the order given; an unknown id and another session's id both come back asnot_found.writereplies with the number of active notes. The deferred-note nudge leads with the counts and one note's title, picked so successive nudges show different notes while a replayed nudge keeps its bytes. OpenCode, Pi and the Rust facade render the same bytes.ctx_memoryno longer advertiseslistto the main agent. Listing moved to a separatectx_memory_listtool that only the dreamer receives; older callers that still sendaction: "list"keep working.geton an archived memory no longer prints "Found 1 active memory".ctx_expandlabels tool-result messages astool results(#516, tenshiak). In a verbose range, a message holding only tool results was labelleduser. On Pi, where each batch of tool results is its own message, a range read like a conversation in which the user spoke every other turn, and a model looking for its own work could pass over those messages. A message that contains only tool results is now labelledtool results; your own messages, and any message mixing your text with tool results, keepuser. Ordinals and previews are unchanged, soctx_expand(message=N)still points at the same message. Same rule on OpenCode, Pi and the Rust module (ck-mc).ctx_searchtakesfromandto(YYYY-MM-DD, inclusive) on every source: messages, memories, git commits, primers and notes. The same holds on Pi and the Rust facade, backed by migration v89.- Tool errors say why a session has no project. The refusal agents kept reading as "not a git repo" is the home-directory rule; memory, search and note errors now name the home directory and the
allow_home_projectopt-in.
Subagents and emergency drops
- Subagents on models without prefill support no longer abort (#512, tppawelgrzesik; also #511). At 95% and above, the emergency drop removed a whole tool arc when its assistant had no reasoning. When the newest step was
text + tool, that left a text-only assistant as the last message, and Sonnet 4.6+ rejected the request with "This model does not support assistant message prefill". Reproduced on a real OpenCode 1.18.32 host on 0.42.6 and master; the plugin-disabled control passes. A drop now keeps a call skeleton and placeholder whenever removing the arc would leave the request without a final tool result, and saves it astruncatedso later passes serve the same bytes. Same fix on OpenCode 1, OpenCode 2 and Pi; the Rust module already kept skeletons and is now pinned by a test. A pass that turns a user-terminated array into an assistant-terminated one is logged. A separate cause is in OpenCode itself: with the plugin disabled, a subagent whose agent config setsstepssends an assistant-terminated request on its last allowed step, because OpenCode appends its max-steps notice as an assistant message. OpenCode also re-requests a model stream that ends without a stop reason with the partial assistant message last. Both need an OpenCode change. - The dropped-argument refusal tells the model what to do (#513, tppawelgrzesik). When a model resends a dropped placeholder as a tool argument, the refusal now quotes what it sent, says the value is a placeholder Magic Context renders for an earlier call (not something to send), lists the tool's real parameter names, and mentions
ctx_expandonly for recovering an earlier call's arguments. From the second refusal in a row it says how many there have been. Detection is unchanged. Same text on OpenCode 1, OpenCode 2 and Pi (Pi reads the tool's live schema).
Rust mode
- A module restart costs at most one priced fold. After a module restart, the first tail-delta pass is answered with
need_full_syncand retried as a full array. That retry left out the reasoning variant, so the module recorded a changed identity and paid a fold, and the next ordinary pass sent the variant again and paid a second. Both requests now carry the same identity fields. A rejected delta also no longer re-imports session state, which could change the next pass's identity. A test drives a steady session through a module restart, a timeout, the recovery and twenty passes, and asserts one fold. (npm package.) - Recovery no longer folds twice. The adapter's stall probe launched an overlapping silent resend while the original request was still in flight, so two transforms each folded. The probe now waits on the original deadline: a raw timeout, then one fold, then a normal pass. (npm package.)
need_full_synckeeps the ordinal map. A retry used to clear the adapter's id-to-ordinal map and re-read every row; on a 124,000-row session one such pass took 57 seconds. The map is kept, a removed message triggers a check that rewinds from saved checkpoints instead of starting over, and each rebuild is logged. (npm package.)- Tool-use provider metadata stays byte-stable. When the native attachment cache went over its memory budget, re-encoding an already-served message lost host fields the codec does not model, such as Anthropic's
tool_usecaller: about 200 served blocks lost it at once, rewriting the cached prefix. The encoder now restores those messages from the request before re-encoding, and any re-encode that changes bytes under an unchanged cache key is counted and logged as an error. (ck-mc.) - Last-known-good replay says why it refused (
lkg_fit_untrustedversuslkg_over_context_limit) and uses the calibrated fit described above. (npm package.) - Compaction markers are replaced atomically. Removing the old marker and writing the new one were two transactions; a
database is lockedbetween them left no marker, and the next pass read the whole session and folded. Replacement is now one transaction, a pending marker is retried on every pass (including last-known-good replays), and after three failures or five minutes/ctx-statusshows MC-C11 with the attempt count and last error. (npm package.) - A new mural waits for a fold that is already happening. The adapter cached the mural in memory and missed an artifact updated outside that cache; the cache now keys on the artifact's hash and render time. Adopting a changed mural no longer triggers a fold of its own; it rides the next one. Same rule on Pi.
- Persisted synthetic user rows are real turns. OpenCode marks the parts of a plugin-injected prompt (plugin notices, Channel 2 nudges)
synthetic. The module removed such a row once it was no longer newest, which merged the two assistants around it and rewrote the cached tail on the next pass. Only rows Magic Context composes itself are removed now. (ck-mc.) - A subagent froze its last-known-good replay and never released it. Served-output fingerprints named a frame id, so a subagent's harness notice read as a change to
m0and pinned the session's baseline. They now name a frameless synthetic message, and a session with no served prefix adopts its new baseline. (ck-mc.) - A subagent's late-minted tags were withheld forever. The release gate asked "is this session paying for a cache bust", which a subagent never is. It now asks whether the session holds a cached prefix to protect, which a subagent does not. (
ck-mc.) - A Channel 1 reminder no longer lands on an already-served tool result during a defer pass. It was appended to a tool output the provider had already cached, which rewrote the prefix on a pass meant to change nothing. Reminders are now fixed before replay. (
ck-mc.) classify-memoriesfailed on every run. The engine'ssession.sendreturns either an active run or a pending submission, the producer read only the active shape, and one provider session was reused across the whole fallback chain. Every fallback attempt now gets its own session, a queued send is a named transient error with its submission withdrawn, every attempt's failure is reported, and a scheduled dreamer task that keeps failing shows in/ctx-status.- Workspace-shared memories and embeddings after an update (#478 and #479, iceteaSA). Under module authority the
ctx_memoryfacade sends this project's mirrored rows to the module and serves another project's workspace-shared rows from the host, and the two refusals are distinguishable: "not mirrored yet" is transient, "not owned by this project's module" is permanent. Batches report per id, mappings are accepted only from this project's module, and every module-side change pulls its rows back before the embedding pass, so an update no longer strands its vector. - Completed authority handoffs are recovered after a restart (PR #504 by DivyamTalwar, for #497). A finished module-to-TypeScript handoff could leave its marker behind and keep fencing memory and note writes. Only confirmed, matching domains are reconciled, and the memory epoch is bumped once. No state, migration or protocol change.
- Store-state refusals say which kind they are. An empty store handle becomes
store_opening(retryable, with elapsed time),store_lease_wait(retryable, with a window and a redacted descriptor),store_open_failed(terminal, with a reason) orstore_unavailableonly when no handshake ever arrived. Transform and thectx_*facade wait briefly for an open in flight. The old message named a cause that cannot occur and sent reports to the wrong repository. ctx_noteids reach the note they name. The agent is shown host note ids (in the deferred-notes reminder, for example), but the module resolved an id passed toctx_noteagainst its own rows, which are numbered separately, so an id could read, update or dismiss a different note, including one from another project. Every id now goes through this project's host-to-module mapping: a mapped id reaches its note, your own note that has not been mirrored yet gets a "pending" reply, and anything else isnot_found. Glance rows, bodies and update and dismiss replies name each note by its host id, a write's reply gains its id once the mirror catches up, and compile metadata for an update lands on the note that was updated. Claude Code, where the module's ids are the agent's, is unchanged. (npm package andck-mc.)- Ready smart notes reach Rust sessions the way they reach TypeScript sessions: through the deferred-notes reminder and
ctx_noteread. The module's<new-notes>block is gone; it named notes by module ids the agent could not use withctx_note. A session already served that block keeps it byte for byte until its next cache bust, then loses it. (npm package andck-mc.) - A smart note becoming ready no longer causes a cache bust. Near the execute threshold, any change to the inputs of the second history block is enough to open a bust, and those inputs still included note status although the block renders no notes. A background evaluator marking a note ready could turn a pass meant to change nothing into a bust. Note status is no longer an input, and a revision stored by an earlier build is recognised as equivalent, so the upgrade itself causes no bust. (
ck-mc.) - New memories in the second history block use host ids. On every host except Claude Code,
<new-memories>and<memory-updates>named memories by module ids while<project-memory>in the first block used host ids, so one memory could appear under two ids. Both blocks now use host ids. The change reaches a session with its next cache bust rather than causing one. (ck-mc.) - Changing reasoning effort on Claude Opus 5.5 no longer forces a fold. Anthropic keeps the prompt cache when only the effort level changes on Opus 5.5 (a session read the same 230,000 cached tokens before and after), but Magic Context counted the effort level as part of the render identity and rebuilt the whole prefix (about 99,000 tokens on one session). Opus 5.5 now joins Fable 5.1 and GPT-6 Astra as a model whose effort changes neither fold nor flush pending reductions. A Rust-mode Opus 5.5 session that recorded an effort level pays one fold after the update. (npm package and
ck-mc.) ctx_searchhides the memories already in context by the right ids. The module's search facade decided which id space the visible memories used from a field only Claude Code sets, so on other hosts it compared module ids against memories rendered with host ids and could hide the wrong ones or show ones already in context. It now decides from the host the session is bound to. Claude Code is unchanged. (ck-mc.)- The deferred-notes nudge arms on a real boundary advance, seeded from the saved marker across restarts, so rust-mode sessions get the historian arm and not only the commit and todo arms.
- Fleet logging.
ck-mcwrites through the shared CortexKit log (r2 format), with a fallback when run by hand and a warning when the host and logger disagree on the directory. The CLI's log reader and the dashboard's parser read r2 next to the older formats, including the escaped control characters (\uXXXX) the fleet log format writes; a line carrying one used to fail to parse. - The module store stops writing on passes that change nothing. An idle pass drops from 8.3 KB of WAL to none and an active pass from 7.6 MB to 3.2 MB. (
ck-mc, with its own store migration.)
Rust transform mode remains OpenCode 1 only (see MC-S06).
Pi and OMP
- Pi 0.86.0: tool declarations survive a fold again (#485, aoguai and XWIlluDelu; #490, Micheal107480, the same mechanism). Pi 0.86 moved the system prompt and the tool set into transcript
systementries. Magic Context put its own head in front of them and treated them as ordinary prose, so after the first compaction the request went out with an empty tool array and the model narrated calls or stopped just before making one. System entries are now exempt from every Pi-lane mutation (trim, tagging,ctx_reduce, last-known-good, emergency reclaim, the hygiene count, native replay) and keep their canonical raw ordinals while contributing no foldable prose. The Magic Context head lands after the leading system entries so Pi'sgetInitialSystemMessagestill findsmessages[0], and an initial system entry displaced by an extension is promoted to served index 0 with ordinals unchanged. The deferred marker drain adopts Pi'sappendCompactionsnapshot only when its tools and prompt equal the ones that were folded; otherwise the marker is refused for that pass, the Magic Context fold is served, the pending marker is kept and the divergence is logged. Pi offers no way to retract an entry, so a divergent entry that was already saved is re-checked against the current journal before any later adoption. A differential test pins our mirror of Pi's resolvers against the installed 0.86. OMP 18.2.6 and Pi 0.85 are unchanged. - The dreamer runs on Pi and OMP again (#496, Qiiks). It had not run since 0.42.5: an OpenCode-only sweep threw on a harness it cannot read, inside the tick's single
try, before any task was dispatched. Every tick since 2026-09-15 ended there; the reporter's store showed 605 of 637 task rows past due, 364 of them with no status at all, which is what a scheduler leaves when it never reaches the rows. The sweep now parks and re-probes a day later, message-history maintenance and each project's maintenance fail separately, and the first stage to fail in a tick is shown as MC-D09 ("Dreamer blocked", with the stage and its age) in/ctx-status, as a warning block on hosts without an overlay, and indoctor. MC-D09 means no maintenance is happening at all; MC-D08 (tasks this host cannot run) and MC-S05 (one task failing) mean some is not. - Pi 0.87: the compaction marker is confirmed again (found while checking #515, tenshiak). Pi 0.87 no longer shows system messages to extension context handlers and restores the prompt and tools itself. Magic Context confirmed its saved compaction marker by comparing Pi's checkpoint with the system entries in the pass it had folded, which on 0.87 are always absent, so every confirmation was refused and retried on every turn. A pass with no system entries could not have changed them, so Pi's checkpoint is now adopted as it is. The Pi host-behavior lane runs on Pi 0.87.1.
- The historian's window comes from the host's model registry (see Historian reliability).
- A configured
contextWindowno longer overrides window overlays on Pi and OMP; it was treated as provider-observed truth.
Dashboard
- Model pickers survive OpenCode 2's cold start. The model probe waits up to 45 seconds, a load error is shown instead of an empty list, and an empty catalog is never cached.
- OpenCode 2 sessions get a real context-limit scale on the timeline. A converted session keeps its id, so its rows could sit under
opencodewhile the timeline listed it underopencode2, and it fell back to its own largest prompt as the y-axis. Rows are now matched by session id across the OpenCode 1/2 labels, preferring an exact label when both exist. - Cache Diagnostics shows Broca runs. Runs recorded in Broca's run index appear as their own harness, with the same cache figures and timeline; the index is read read-only. Each Broca run is one point on the timeline, since Broca keeps usage per run.
- Store generation is classified by the absence of 1.x message tables, and live-reloadable config keys carry a Live badge.
CLI
- Setup and
doctorunderstand OpenCode 2'spluginskey. OpenCode 2 reads both the legacypluginarray and its ownpluginsarray and loads both, so a check that looked only atpluginmissed a registration underpluginsand appended a second one, and the plugin loaded twice. Every reader now walks both keys and accepts the{package, options}object form. A new entry goes under the running generation's own key (pluginson 2.x,pluginon 1.x), an upgrade rewrites an entry where it was found, andtui.jsonis written only on 1.x, which is the only generation that reads it. doctorchecks variants against the host catalog (see OpenCode 2), lists sessions pending a store rebase and unresolved compartments, reports compaction markers missing their completion time with a--fix, and hasdoctor list-hidden-sessionsfor OpenCode 2 hidden sessions.
Embeddings
- Local Transformers.js model downloads honour
HF_ENDPOINTin both native and WASM runtimes, so a Hugging Face-compatible mirror can be used without changing cache or vector identities (#459, sisttri). - Shadow embeddings are discarded when their registration is retired mid-embed (#470, Qiiks). If the shadow registration was replaced during a provider round-trip (tens of seconds on a cold Synapse load), the returned vectors were written under the retired lane and read as valid backfill later. The live registration is re-checked after the call in all three scopes and the write is refused with a named reason.
- The bundled runtime update and the resulting one-time re-embed are described under Upgrading (#480, ice-fortress).
Also fixed
/ctx-status, the sidebar and the dashboard count every compartment the model receives (#515, tenshiak). Compartments the historian publishes are served in a second block,<session-history-since>, until the main<session-history>block is next rebuilt. The Compartments figure counted only the main block, so a session whose main block was built before its first compartment showed about 9 tokens however many compartments existed. The summaries always reached the model; only the figure was wrong./ctx-statuson every host, the OpenCode sidebar and the dashboard now count both blocks. It was reported on Pi 0.87.1 and happens on 0.86 too.keep_subagents: trueagain keeps every settled Magic Context child (#500, Cyantix). A 0.42.0 regression made dreamer tasks, smart-note evaluation and compilation, and user-memory review disappear even with the setting on. Unsettled privacy children are still archived and cleaned up by the age-gated sweep. Kept dreamer transcripts can contain memory-pool text and user messages from the same operator's other sessions, and they accumulate until cleared by hand. OpenCode 2 hidden sessions now follow the same rule (see Parity work).- Auto-compaction conflict detection no longer disables the plugin on a config-less
OPENCODE_CONFIG_DIR(#484, tim-mcdonald). That variable adds a config directory; it does not replace the global one. The detector read only that directory, found no file, fell through to theauto: truedefault and switched itself off. It now reads every layer OpenCode reads, in OpenCode's order for the host generation, and acompactionkey absent everywhere is inconclusive. An absent or non-booleanprunenow takes OpenCode's documented default instead of making the ordinary{"compaction": {"auto": false}}shape unreadable. - The retrospective builds a prompt that fits (#473, SK-DEV-AI). Oversize user lines are clamped, the prompt is admitted against the child model's window oldest first, only the configured recency window (30 days by default) is collected, and after a size rejection a smaller budget is retried and the scan moves on. The reporter's 2.19 MB / 683k-token shape now fits at 25 KB / 7.8k tokens.
- Equal-importance memories tie-break on how recently they were confirmed (#474, Qiiks). Once the injection budget binds, ordering a whole
importance = 50band by creation id carries no signal; selection now uses verification and last-seen recency on OpenCode, Pi and the Rust module (one priced fold per rust-mode session; TypeScript picks it up at the next natural fold)./ctx-statusshows a per-band importance histogram with an unclassified count, which showed the reporter's peak at 50 was the unclassified pile: classified memories spread across the whole scale (on our own store, 271 of 7,092 sit at exactly 50) while unclassified ones sit at 50 or NULL. Checking that foundclassify-memoriesfailing in rust mode, fixed under Rust mode. - A reasoning-only assistant could vanish on a pass meant to change nothing. A tool-removal sweep pruned every message left without a meaningful part, so such an assistant survived a priced pass and disappeared on the next defer, rewriting the prefix. Pruning is now limited to the messages a pass actually touched and adopted only on a priced pass. A session that has not adopted it computes both candidate arrays on a defer and serves the one that reproduces what it was last served, read from its saved last-known-good snapshot: the old one when both match, the new one (adopting it) when only that matches, the status quo when neither does, and the new one when there is no snapshot. Each choice is logged under
tool_sweep_lkg_mismatch. A session that was mid-drain when it upgraded from a build without the atomic drain rule loses exactly the rows below that build's committed boundary, once. - The dreamer's
maintain-docstask proposes changes instead of editingARCHITECTURE.mdandSTRUCTURE.md. Both files are injected into every session's cached prefix, and the task only ever added to them, so they grew into change logs (ours reached 57K tokens). The task now runs with read-only tools, receives the commits since its last proposal, and returns replacement sections; Magic Context checks that protected regions are unchanged and that both files stay withindreamer.tasks.maintain-docs.max_tokens(default 12,000), then writes the proposal to.cortexkit/magic-context/docs-update-proposals/with a diff. It never writes the files. With no relevant commits it skips without calling a model. The task is still off by default, andsetupadds.cortexkit/magic-context/to.gitignore. - The compaction-marker drain and the in-pass prefix trim are one decision, so the drain cannot advance while the served window still carries rows below the new boundary.
- Channel 1 nudges no longer starve. An invalidated tail-hygiene baseline is re-measured on the defer pass that finds it instead of waiting for the next cache bust. Same on OpenCode, Pi and the Rust module. That fix changed no band, threshold or setting.
- A legacy database could fail its upgrade on Linux. v86's triggers write
session_meta.tags_version, and an older migration renames tables under them, so a long-stale database died at v49 on SQLite 3.53. The column is healed first. v86's triggers also inflated the change countsbun:sqlitereports; the tag-write guards no longer depend on them, and the trigger creates a completesession_metarow so cloned sessions keep their counters.
Removed
/ctx-session-upgradeis gone, along with its OpenCode 1 dialog and reminder, RPC handlers and runner, and the once-per-project 9-to-5 memory category migration. The dreamer's curate task handles legacy categories as it touches them,/ctx-recomprebuilds legacy compartments, and they render without any upgrade. Databases are untouched: the old guard rows and upgrade columns stay inert. Pi's status line still detects legacy compartments.
Carried from 0.42.5 and 0.42.6
These shipped earlier in the 0.42.x line; neither release works on OpenCode 2, so for OpenCode 2 users they arrive now.
- Local embeddings no longer pin a CPU core after the model loads (#453, Ema-prowler).
ctx_notehas one id field,note_ids(#460, llc1123).- Pi resolves embedding routing before registration (#462, Qiiks).
- Auto-update no longer disables itself after the first update (#466, ICEY16360).
- Historian chunks no longer chain across consecutive tool arcs (#467, Mte90).
- Rust-mode memory ids are host ids in every mode (#468, iceteaSA).
- Image fingerprinting no longer tokenizes image payloads (#452, Qihuanxishini), dreamer
curateruns one memory category per scheduled window (#451, Sugaroverdose), and local embeddings initialize under OMP on the WASM runtime (#441, sisttri).
Performance
- Pi redraws faster with many todo lists in the transcript. Pi re-renders every transcript item on every frame while its spinner runs, and each
todowriteitem re-formatted all its rows each time; on a session with 1,215 todo results that was about 12% of wall time. Each item now renders once per terminal width. - Defer passes on large sessions are cheaper. The post-transform phase no longer re-measures an unchanged tail on every pass (38 → 6 ms on a 2,250-message session), and the rust-mode adapter no longer re-parses the replay document when its source text is unchanged (13 → 2.5 ms). Served bytes are identical.
Community contributions
Merged with thanks. Some landed in 0.42.5 or 0.42.6 and are credited here for the first time.
- #504 (DivyamTalwar): recover completed authority handoffs after restart (for #497).
- #506 (iceteaSA): isolate the real-daemon test rig's data home and stop leaking it.
- #470 and #471 (Qiiks): discard shadow vectors when the registration is retired mid-embed, and a retirement test that records dispose at call time.
- #476 and #477 (qoole): OpenCode 2 tool-part mapping and acceptance of 1.18-migrated stores.
- #456 (kassdinzheng123): resolve Pi session APIs from OMP hosts and make failed discovery pages retryable.
- #458 (G0-0000): document timestamp columns in
ARCHITECTURE.mdand the fresh-install schema. - #463 (TreedsSlop): the dreamer's
last_run_atadvances from run start. - #414 (qsgy-edge): Pi
doctoridentifies Magic Context trees by package identity and names every broken candidate.
Reports and diagnoses that shaped this release: blasphemy (the OpenCode 2 lane gaps in PR 487 and #488), null-axiom (#492), deltacat (#493), qoole (#475, #481, #483), iceteaSA (#478, #479, #501, #502, #507, #508), tppawelgrzesik (#511, #512, #513), tenshiak (#515, #516), Mte90 (#510), DGVY (#486, #489), tim-mcdonald (#484), Cyantix (#500), SK-DEV-AI (#473), Qiiks (#472, #474, #496), expiren (#491), aoguai, XWIlluDelu and Micheal107480 (#485, #490), sisttri (#459), ice-fortress (#480) and thebigcheeseorama-ship-it (#499, a stray lockfile in the repository).
Declined, with the reason:
- #450 (G0-0000): memory injection through markers in task prose. Single-harness as shaped, and it adds agent-facing syntax, a default-on config surface and a fence move, which need the design agreed first (#449).
- #461 (bf-frieder): compartments and history replay for subagents. Subagents are ephemeral workers by design; the answer we want for the underlying need is an explicit reference set handed over at spawn.
- #487 (blasphemy): superseded in-house; the same gaps were reimplemented against master as #488, with the diagnosis credited.
- #457 (G0-0000): comments inside already-applied migrations. Shipped migrations are append-only (SQLite keeps the
CREATEtext verbatim, so a fresh install and a replayed upgrade would differ). Reshaped as #458 and merged. - #464 and #465 (TreedsSlop): the diagnosis is right (
session_projects.updated_atrecords binding time, not activity), but the shape readsopencode.dbfrom the dreamer and covers OpenCode only; the fix we want is a per-session activity mark in our own store, which works on every harness.
Tests and internal
- Host-behavior scenarios are written once and run per host from one manifest (OpenCode 1, OpenCode 2, Pi, OMP); the manifest refuses a host missing from a scenario without a stated reason. All four lanes gate master in CI, OpenCode 2 and OMP also in the Linux container, and the OpenCode 2 conversion and marker scenarios run in CI through a pinned OpenCode installer. The Rust crates have a nightly CI job.
- Wall-clock test budgets that measured the CI runner instead of the code (#472, Qiiks) now assert ratios or same-process comparisons; absolute caps run only under
MC_PERF_GATE. - Contributions go through a design-approval gate:
CONTRIBUTING.md, a PR template with the harness checklist, and a design-proposal issue form. Feature PRs need the design agreed first and must cover every supported harness. The gate is now the one every CortexKit repository shares. Link the approved issue with the template'sApproved issue: #Nline, orRefs #N/Part of #Nfor one step of an issue. Please don't use closing keywords: they close the issue when the PR merges, before the fix ships. An unapproved PR is converted to a draft when it is opened or marked ready, and adding or removing a label re-runs the gate. - Releases pack each plugin, install the tarball into a clean project and fail on any high or critical
npm auditfinding. - Cache-bust instrumentation separates provider cold misses and passes with no usage from real busts, wakes on an unexplained epoch fold with the identity component that changed, and flags self-inflicted epoch folds.