Release notes¶
Version-by-version records of what changed and why, newest last. Commands and measurements in older entries describe that release. See Commands for current support and the benchmark methodology for maintained instruments and historical-accounting limits.
Part of the design document
Section numbers (§1–§13) run across the whole design document, split here into Design spec, Implementation notes, Release notes, and Prior art.
v0.11 (2026-08-01): MCP-only focus¶
CLI speculation (exec daemon, Bash hook, workspace shell server) is removed.
Rationale: speculation value scales with upstream latency, and MCP/SaaS reads
(hundreds of ms) dominate local CLI reads (~30 ms, often net-negative after
hook spawn overhead); read-only vetting of arbitrary argv has no
deterministic cross-platform answer short of per-OS sandbox machinery,
while MCP's readOnlyHint gives it for free; and the tier carried the
project's POSIX-only surface (unix sockets, uid checks) plus a Windows
.git/index watcher loop that flushed every prefetch. on/off now clean
up artifacts a ≤0.10 install left behind. Full trail:
.superpowers/archive/specs/2026-08-01-focus-mcp-design.md.
speculate exec survives as a verbatim pass-through (no shell, no rewriting,
the child's exit code) so the ≤0.10 Bash hook keeps working. That hook
rewrites the agent's git status/rg/ls into speculate exec -- … in
every project until speculate on removes it. Compatibility only, for one
release: removed in 0.12.
Windows: npm installs claude as a .cmd shim, which Node refuses to spawn
(CVE-2024-27980), so on/off/status reach the front door through cmd.exe.
Arguments are quoted for the child's CommandLineToArgvW, then escaped twice
for cmd (the shim's %* re-parses one of the rounds), with % stepped
outside the quotes (a caret inside quotes is literal), so a %APPDATA% in an
MCP entry can neither expand nor inject. Two limits are cmd's own: the command
line cannot exceed ~8191 characters (fails loud with "The command line is too
long.", exit 1) and a raw \n/\r inside an argument truncates the line
(JSON-escaped \\n, what mcp add-json payloads carry, is unaffected).
Benchmark re-verified after the removal (the harness was always MCP-only, so the §11 numbers carry forward unchanged): hit rate 71%, waste 0.00/hit, both deterministic across runs; tool-wait cut −64%…−67% over four runs (timing jitter; −66% is the central value). Test suite: 431 tests, 424 passing, 7 skipped on Windows. The §10 caveat still governs: this is a scripted, workflow-shaped ceiling, and §10 item 8's adversarial floor script remains unwritten, so no measured lower bound exists yet.
v0.12 (2026-08-02): auto-wrap¶
speculate on now also installs a second, minimal plugin at Claude Code's
user scope: speculate-autowrap, shipping exactly one SessionStart hook
that runs a new speculate sync command. Unlike the wrap on performs on
the spot, sync targets servers added to any project after the fact, without
a person ever running on there again.
The one-session lag is measured, not assumed. Testing against Claude
Code on Windows with an isolated CLAUDE_CONFIG_DIR established: a
SessionStart command hook fires before auth completes; claude mcp
add-json run from inside that hook succeeds; but the server it adds does
not launch in the current session, only in the next one. Claude Code
snapshots MCP config before running SessionStart hooks, so no hook,
however early, can make a wrap take effect in the session that triggered it.
A server added now runs unwrapped, exactly as if speculation were off, for
one more session, then wraps starting the session after. Both the plugin's
own summary line and speculate status state this plainly rather than
implying instant pickup. One further limit of the same shape, stated here
because nothing else in the product can state it: the hook is registered
with matcher: "startup", so it fires only for a fresh session. Someone
who works entirely in claude --resume or claude --continue never fires
it and therefore never gets auto-wrap at all; for them speculate on
remains the only thing that wraps anything. Widening the matcher to
resume/compact would run the hash check on far more session events for
a wrap that is one session late regardless, so the narrow matcher stands —
but as a choice, not as an oversight.
Sync is cheap on the common path and fails open on every other one.
Before spawning anything, it hashes the project's effective server set
(name, scope, approval state, and canonicalized entry for every server) and
compares it to a stored per-project hash; when they match, which is the
overwhelming majority of session starts since most sessions add no server,
sync returns after a couple of file reads: no subprocess, no lock. Only a
changed hash proceeds to acquire a host-wide lock file, one per state
directory rather than per project, because every session ultimately
read-modifies-writes the same global ~/.claude.json; a session that
cannot get the lock exits immediately and leaves the work for whichever
session next finds the config unlocked, which costs nothing given the lag
already puts everything one session behind. on and off deliberately do
not take that lock — they are interactive, and blocking a person behind a
background hook would be the worse trade — so sync's final write is a
read-merge-write, not a write-back: it re-reads the state file immediately
before saving and touches only this project's own two keys. Writing its
whole in-memory copy back silently reverted an off that completed in
another project mid-sync, erasing the opt-out that off had just recorded
and resurrecting the project record it had just deleted, so the project the
user had turned off was re-wrapped at its next session start. One residual
is recorded rather than fixed: since on/off still take no lock, an off
in the SAME project as an in-flight sync can still have its project record
resurrected by that sync's merge. The opt-out itself now survives, so the
consequence is a stale entry list, and a later off reporting spurious
failures as it chases servers that are already unwrapped — not a project
that gets re-wrapped. Closing it properly means on/off taking the lock,
which trades a silent data race for a person waiting on a background hook. The wrap pass itself runs under
a cooperative deadline, 5 s by default: checked only between servers, never
between one server's remove and its paired add-json, so a session that
runs out of budget mid-list leaves a clean host, nothing deleted without a
replacement, rather than a fully wrapped one. Above that sits a last-resort
process exit for a hang no layer below can end, set at 120 s — the 5 s
budget plus three 30 s execFile timeouts, with slack, because that 30 s is
not a hard bound: execFile SIGTERMs and then waits for stdio to close, so a
child that ignores SIGTERM runs past it. The plugin's own hook timeout
(150 s) and the stale-lock window (180 s) are stacked above that in turn,
since a host-side kill lands wherever it lands, including in exactly the
window the cooperative deadline exists to protect, and a lock holder that
legitimately runs to either cap must not look stale to the next session.
Arithmetic is the weak form of this guarantee, and the code says so: the
strong form is a marker held across the remove→add-json pair, so the
exit can refuse to fire while one is open and the restore is replayable if
the process dies anyway. That is the right long-term fix. Every failure path returns
success and sync prints at most one summary line: a session start must
never be blocked or sprayed with diagnostics on auto-wrap's account, so
speculate status remains the place to look when something needs
attention.
off opts a project out; it does not uninstall the plugin. Running
speculate off records a per-project opt-out that sync's hash check
consults before anything else, so the global hook will not silently
re-wrap that project again, even though the plugin stays installed for
every other project on the machine. off prints the command to remove the
plugin everywhere (claude plugin uninstall -s user speculate-autowrap)
for anyone who wants auto-wrap gone entirely, plus the command to remove
the marketplace registration that supplied it (claude plugin marketplace
remove speculate-mcp), since a host-global registration is exactly the
kind of artifact off's per-project framing could otherwise leave
unmentioned. It also names the limit that framing hides. The servers off
unwraps at USER scope are shared by every project on the machine, while the
opt-out it records covers one project, so any OTHER project's next session
start re-wraps them at user scope — and this project sees them wrapped
again, within a single session, without auto-wrap having disobeyed its
opt-out at all. Whenever off unwrapped anything at user scope it now says
so, and names the plugin uninstall as the only thing that actually stops
it. speculate status closes the same loop from the other side: with the
plugin installed it used to report "installed (new servers wrap at the next
session start)" purely on detection, which is exactly wrong in a project
that has just run off; it now reports the opt-out and names speculate
on as the way back in, and that is the only place the opt-out is visible
at all. What off still does not touch or mention is the staged
plugin copy under <state>/autowrap (the same directory that holds
managed.json) that on wrote in order to install the plugin in the
first place; that copy is inert once the plugin is uninstalled, since
nothing on the host points at it any more, and is safe to delete by hand
alongside the uninstall.
Install repairs itself by uninstalling first. Measured against the
real host: with the plugin already installed, claude plugin install
no-ops ("already installed") and plugin update reports "already at the
latest version"; neither re-copies a cached plugin. So when the staged
hook command or the plugin's own version no longer matches what is
installed, for example after an npm move changed the baked CLI path, or
after a new Speculate release, on uninstalls the old copy first and
immediately reinstalls the current one; a plain install or update cannot
get there, since both treat "already installed" as done. If the uninstall
half of that repair fails, on aborts rather than attempting the install
against an unknown state, and prints the exact recipe to finish the job by
hand: claude plugin uninstall -s user speculate-autowrap, then
speculate on. The two are printed on separate lines rather than chained
with &&, because PowerShell 5.1 is the default shell on stock Windows and
parse-errors on &&, which would leave a stuck user running neither half.
The honest cost of that abort: between the failure and someone
running the recipe, the user has no auto-wrap plugin installed at all,
which is a worse position than the stale copy they started with, and
exactly the reason the message names the fix instead of only the
uninstall half of it.
One correction to the v0.11 record above: it committed to removing the
speculate exec compatibility pass-through in 0.12. That did not happen,
and 0.12 still ships it. Nothing about auto-wrap depends on it either way,
but a ≤0.10 Bash hook can still be sitting in a project nobody has run
speculate on in yet, so the shim keeps earning its place. Removal moves
to 0.13.
Consent, in both directions. Sync wraps only servers
wrapEffectiveServers would already wrap through on, including the
.mcp.json approval gate, so nothing sync does can turn a pending server
into a running one — and, as of this release, revoking an approval takes
the wrapped server away again. The second half was missing, and 0.12 is
what made it matter. Once an approved project server is shadowed by the
wrapped copy registered at local scope, the local entry wins the scope
contest, so the project entry's approval flag stopped reaching the
per-project hash: revoking the approval changed nothing sync could see,
sync made zero calls, and the shadow stayed registered and running at a
scope that has no approval gate at all. That was already true of on in
0.11; 0.12 escalated it, because shadows are now created unattended in
projects where nobody ever ran speculate on. Both halves are fixed: the
hash covers a shadowed project entry as well as the effective one, so a
revoke moves it, and sync/on then REMOVE a shadow whose .mcp.json
counterpart is no longer both present and approved, leaving whatever the
project actually declares — a pending entry, or nothing at all — as the only
thing left. Present matters as much as approved: a server dropped from
.mcp.json by a pull, a branch switch, an edit, or a deleted file is the
commoner trigger, and it used to leave the wrapped shadow running forever
for a server the project no longer declares. Only shadows Speculate created
are removed — the managed state records those with action shadowed, and
the entry must still be a Speculate wrap — so a local entry the user wrapped
themselves is never touched, and neither is one whose record has been lost
with the state file. That last case is deliberately conservative: without the
record there is no proof the entry is ours, which is exactly the stance off
takes in the same situation. The other honest consent-adjacent fact:
because the plugin installs at user scope, opening a brand-new project also
gets its already-approved servers wrapped automatically at that project's
next session start, without speculate on ever having run there.
Full trail: .superpowers/archive/specs/2026-08-02-auto-wrap-design.md.
v0.13 (2026-08-02): prediction quality¶
Five defects in the learner, each found by measurement rather than by reading the code. §13.16 through §13.19 carry the detail; this section is the ledger and the honest reading of the numbers.
The instrument came first, because the old headline was circular.
npm run bench replays a scripted 7-call GitHub session against the mock and
reports hit rate, tool-wait cut and waste. What it measures is prefetch
mechanics: whether a predicted call is issued early enough, completes in
time, and is served rather than forwarded. It cannot measure prediction
quality, because the script and the hand-written GitHub rules that predict it
were authored together, so quoting its 71% as evidence that the learner
predicts well was reasoning in a circle. The sharper form of the same point:
the learner contributes nothing to that 71%, since a learned transition
needs two sightings and the benchmark's workflow repeats none of its calls.
npm run eval is the separate
instrument, and it was built before any change to src/. It scores offline
recall@K over transition pairs: one pair is a consecutive (call i-1, call
i) inside a scored session, a hit requires tool and arguments to match
under canonicalKey (the same key the cache uses, so a right tool with a
wrong id is a miss), and recall@K is hits at rank ≤ K over pairs. It drives a
real TransitionLearner and imports nothing else: no ServerProfile, no
Predictor, so no hand-written rule can contribute a prediction by
construction, and a test asserts no corpus tool name collides with the
bundled github, filesystem or slack profiles. Seeds 1, 2 and 3 are pooled;
the clock is injected and neither Date.now nor Math.random appears under
eval/. Both commands still ship, and they answer different questions.
Where it ends up (seeds 1,2,3; recall@3 is the headline band because 3 is the shipped per-trigger cap, §5.6; recall@5 is visible only because the harness raises the learner's cap to 5):
| archetype | pairs | recall@1 | recall@3 | recall@5 | waste/hit |
|---|---|---|---|---|---|
| list-detail-varied | 300 | 0.373 | 0.727 | 0.790 | 3.24 |
| return-visits | 300 | 0.593 | 0.997 | 0.997 | 1.26 |
| multi-arg | 300 | 0.803 | 0.883 | 0.883 | 0.84 |
| regime-shift | 120 | 0.900 | 0.900 | 0.950 | 2.33 |
| direct-recall | 150 | 0.267 | 0.587 | 0.627 | 2.38 |
| paired-args | 300 | 0.537 | 0.887 | 0.923 | 2.70 |
| WORKFLOW (headline) | 1470 | 0.571 | 0.846 | 0.875 | 2.00 |
| adversarial (floor) | 300 | 0.087 | 0.087 | 0.087 | 9.08 |
The adversarial floor sat at 0.087 through every task in this plan, with its waste per hit pinned at 9.08 and, at several stages, byte-identical counters on both sides of a change. That is the control that makes every other number here mean anything. A learner that bought recall by firing more speculations at noise would have lifted the floor first, since the floor's entity ids are minted once and never repeated, so the flat floor establishes exactly this and no more: no gain below came from firing at noise. It is not the claim that the gains came without firing more, which would be false. The workflow pool went from 1,892 predictions issued to 2,449 over the same span, and that cost is priced in the waste column below. The floor is reported beside the headline, never pooled into it, and the two are only ever quoted together.
The headline is not one number improving in place, and the denominator is
why. The first pooled baseline this branch recorded was 0.6033 over 900
pairs; it now reads 0.8463 over 1470. Three archetypes joined the
corpus in between, each of them added because an existing defect was
invisible without it, and each addition moved the headline on its own:
regime-shift took 900 pairs to 1020 (0.6033 to 0.6363), direct-recall
took 1020 to 1170 (0.8725 to 0.8359), and paired-args took
1170 to 1470 (0.8359 to 0.797, both measured without the coherence check).
The last two moved with no code changing at all; the first is the one place
this paragraph has to qualify itself, because its two endpoints straddle
Task 2's decay change as well as the corpus addition, and 0.002 of that
0.6033 to 0.6363 is code rather than corpus. So the
end state is a harder corpus and a better learner, and the pooled
endpoints cannot separate the two. The attribution lives in the per-task
deltas, each measured against a corpus held fixed across the change:
| change | pairs | before | after | Δ recall@3 |
|---|---|---|---|---|
| Evidence decays, and eviction goes by value (§13.16) | 1020 | 0.532 | 0.636 | +0.104 |
| A template holds evidence, not a latch (§13.17) | 1020 | 0.636 | 0.731 | +0.095 |
| Sources compete, and a transition offers several (§13.18) | 1020 | 0.731 | 0.873 | +0.141 |
| Co-varying arguments are one hypothesis (§13.18) | 1470 | 0.797 | 0.846 | +0.049 |
The five defects those four rows close: lifetime-frequency ranking that never
forgot, paired with FIFO eviction that dropped the best-evidenced entry to
admit a one-off; a single unexplainable value latching a transition off
permanently; one hypothesis per argument, fixed to whichever row index the
first sighting happened to use; one argument set per transition, which pinned
recall@K to recall@1 whatever the budget allowed; and two arguments read off
the same row scored as independent, so the cheapest substitutions in the beam
were pairings that had never occurred. A sixth was introduced and caught in
review rather than shipped: value-based eviction let a brand-new transition
be its own victim, freezing the model silently and, because the score
persists, across restarts. Both eviction sites now exempt the key the current
observation just wrote, with a regression test at the default
minObservations.
Waste is the price and it is visible: the workflow band reads 2.00 wasted predictions per hit against 1.27 at the first baseline, moved by the same mix of corpus growth and model change as the headline, and it is concentrated in the archetypes the recall came from. That column bills every prediction issued at the shipped cap, including the batch fired after each session's last call that nothing can ever claim, so it is a deliberately pessimistic production estimate and not the instrument §10's ≤2 per hit criterion was written against (the bench still reads 0.00). Even read that way it now sits exactly on that bar, which makes it the number to watch next rather than one with headroom left in it.
Calibration against PASTE, read the unflattering way. PASTE (arXiv 2603.18897) reports 27.8% top-1 and 43.9% top-3 predictor recall on Deep Research Bench, SWE Bench and ScholarQA, which are real traces. Our 0.571 and 0.846 are on a corpus we wrote ourselves. These are not comparable numbers, and ours reading higher is most likely evidence that our corpus is easier, not that this learner is better: we authored the archetypes knowing what the learner can derive, and a synthetic workflow is predictable in ways real traffic is not. A real-trace figure for this learner would most likely land somewhere between our floor of 0.087 and our headline of 0.846, and nothing measured here establishes where. Two differences do run in our favour, and they are facts about scope rather than about accuracy: PASTE describes no staleness or invalidation mechanism, and no decay, since its patterns are mined once and applied uniformly. One runs the other way: it has a string formatting and normalization transform as a third kind of argument source, where we have only argument copy, parsed result path and memorized constant, and it launches greedily on utility rather than against a fixed cap. Real numbers for this proxy still come from §9 telemetry, not from either corpus.
What did not ship, recorded because the plan called for it.
- Widening the learnable array-index window (indices 0..2 to 0..7,
pushArrayPaths) is deferred, measured at +0.003 (3 pairs of 900), not the ~0.06 first estimated. Under a per-trigger cap of 3 and a monotonically decreasing index distribution, the top three indices by frequency are always {0,1,2}.src/learner.tsstill readsMath.min(arr.length, 3). - Entity frecency as a separate mechanism is dropped: Task 3's
per-source scoring already implements it generically. The
direct-recallarchetype was authored as a negative control, isolating the case where the target id appears nowhere in the trigger's arguments or result (verified 0 of 180 sessions) while six wrong ids sit at enumerable array positions. It came back positive at 0.835 recall@3 on its common leg, with the transition holding fourconstsources, one per pinned entity, ranked by decayed score. The control on the control, the same shape with entities never reused, scores 0.000 at zero waste. What remains is scope, not memory: constants are keyed per (server, prevTool, nextTool, argName), so the same entities reached from a rarer trigger get nothing (0.733 / 0.667 / 0.389 / 0.000 as that trigger thins from every 2 to every 16 sessions), priced at about +0.03 on the headline. - Utility ranking (PASTE's
p·T, cutting the per-trigger cap on expected time saved rather than probability) landed and was reverted as a measured no-op. Two reasons, both measured: the bundled bench injects one latency for every tool, soscore × msisscoretimes a constant and the ranking is provably identical, and the per-trigger cap never binds on the bundled profile (zero cap-suppression events across a full session, since it offers at most 2 to 3 candidates against a cap of 3). Only a constructed heterogeneous workload at cap 1 moved it, 4.50 s to 4.05 s. The eval was unchanged to every digit. The plan's own rule is that a change which does not move the measurement does not land; it is worth revisiting once the cap starts binding, which the beam makes likelier. - The long-horizon TTL lever ships inert.
speculation.longHorizonTtlFactorexists per server andLONG_HORIZON_TTL_FACTORdefaults to 1, so nothing is shortened unless someone asks for it. Shortening bought zero measured freshness (standing bets are consumed at a lead of exactly 1.000 calls, the same instant as derived ones) and cost about 9% of all hits once inter-call spacing passed roughly half the TTL (1265 to 1150 at factor 0.5 from 16 s spacing, with the standing class going to zero). The instrumentation it was built alongside did ship:ageAtHitreports median, p95 and max age at consumption, the share consumed in the last quarter of their TTL, and mean lead, in both the runtime and the offline replay.
What the numbers still do not cover. The corpus is synthetic and
authored, not sampled traffic. derived/missed are the only evidence in
the learner that does not decay, so a derivation that stops working must
accumulate misses in proportion to its whole history before the rate gate
closes; the §5.6 feedback loop is the production backstop and the offline
harness does not model it. No surviving array-index derivation remains in the
corpus, so if pushArrayPaths broke outright the headline would not move.
Session-start openers (§13.15) do not fit a recall@K-over-pairs frame and are
unmeasured. return-visits has saturated at 0.997 and no longer discriminates
anything.
Two corrections to the record above. The v0.11 note said §10 item 8's
adversarial floor remained unwritten, so no measured lower bound existed;
that is no longer true, and the floor is the 0.087 row. The v0.12 note said
the speculate exec compatibility pass-through would be removed in 0.13.
That did not happen either, and 0.13 still ships it.
v0.18 (2026-08-30): day-to-day utility¶
Speculation now learns the latency of eligible tools across sessions and uses a conservative latency estimate when deciding whether a candidate is worth issuing. The model is time-decayed, numerically stable, bounded, and merged across concurrent state writers. Existing learned transition latency remains as a migration fallback, and a v0.17 state file without the optional latency field still loads normally.
Candidate correctness is evaluated before admission, including candidates suppressed for being too cheap. Those observations feed a bounded, confidence-informed calibration model. Calibrated next-call probabilities now rank candidates while operational feedback remains a separate suppression signal. Live statistics expose calibration Brier scores and reliability buckets without persisting arguments, result data, or cache keys.
The underlying predictor also becomes more useful on varied workflows: learned state is isolated by workspace, upstream, and credential/account scope; bounded recent context can specialize a durable transition; argument sources can use conservative deterministic transforms; alternative argument sets retain separate feedback; and compatible list/detail schemas can yield a cold prediction without a server-specific rule. Mutations and session resets clear pending shadow batches without manufacturing negative observations.
Durable statistics now report conservative added wait and net saving, predictor recall, argument near misses, and optional server/tool breakdowns. They support time and workspace filters plus crash-safe monthly compaction. Terminal accounting includes abandoned outstanding work instead of allowing a final speculative batch to disappear from the waste total.
Validation covered 796 passing tests with 7 opt-in skips, a clean TypeScript build, and an unchanged offline workflow score (recall@3 0.8463, waste/hit 2.00). Default admission stayed quiet on approximately 5 ms Git reads; an unconstrained Git control retained an 80% hit/join rate; the 120 ms filesystem workflow produced 30 useful results from 44 opportunities and about 3.16 s of estimated net saving; and calibrated Brier score beat static confidence on the Git, filesystem, and Hugging Face runs. Hosted latency remains noisy and is reported as per-run spread rather than a release gate.
The deterministic daily-workflow generator and comparison core ship with the release. The common executable daily runner, marginal per-candidate admission controller, durable per-tool latency explanation, and bounded unknown-latency discovery remain follow-up work. Cold discovery stays disabled until a negative-control workflow proves that it becomes quiet on unpredictable traffic.
v0.19 (2026-09-07): learning across intervening calls¶
Argument bindings can now reuse values from bounded earlier calls on the same server. Raw history remains in session memory; only compact learned sources and evidence persist. Queued next-call predictions are retired once the caller advances, reducing obsolete work while preserving already issued and standing predictions.
Local learning remains automatic. The storage boundary filters known credentials and recognizable secret literals across imports, merges and temporary writes. It adds a 30-day learner retention window, an 8 MiB scoped state cap, aggregate memory inventory and generation-safe clearing. Arbitrary private strings cannot always be recognized. Stats now explain prediction coverage, useful calls, unmatched candidates and estimated benefit.
The five-seed full-proxy qualification passed its predeclared gates. Across 800 held-out repeated-workflow requests per enabled arm, useful hits/joins rose from 548 to 670, mean wait fell from 91.78 to 79.96 ms, and terminal waste fell from 220 to 216. Ready hits fell from 119 to 104; joins drove the gain. Two workflows exceeded both 15% mean wait reduction and a 10-point useful-rate improvement. The unpredictable control issued no speculation.
These fixtures inject 120 ms of upstream latency. Cold useful rate stayed unchanged, tail latency did not improve, and existing filesystem, local Git and live Microsoft Learn checks showed little extra benefit or regressions. The qualification report records all workflows, controls, limits and reproduction details. Linux/macOS/Windows CI and Node 18 CLI compatibility passed.
v0.20 (2026-09-07): smaller launch surface and corrected prediction lifetime¶
speculate on and explicit wrap are the supported launch paths. PATH-shim
installation/status, protocol sniffing, and speculate try are retired. Old
wrap --sniff invocations now pass through immediately without speculation.
Existing shim users can run speculate shims uninstall, restart their shell, and switch
to an explicit wrapper configuration. The migration guide
includes saved commands and how to evaluate and undo normal activation.
The trial command's zero-write promise did not match its behavior: it wrote a temporary configuration containing server credentials and left ordinary learning persistence enabled. It also passed HTTP/SSE entries through without wrapping them. Removing it eliminates a separate, incomplete setup path.
Learned transitions now always predict the next call, including transitions
with constant arguments. Their unfired queued work is retired when the real
sequence advances. standing is reserved for startup predictions, so the
existing queue cleanup applies consistently. This lifecycle correction does
not cancel already-issued calls or change cache correctness protections.
Unused custom argument canonicalizers and state-fallback plumbing are removed. Cache keys continue to use stable JSON with exact argument values. Memory inventory uses the persistence loader's validation instead of a separate weaker reader, and admission uses the shared latency model. Outcome feedback and calibration share one scoring path. The expiry ablation demonstrated that correctness calibration alone does not prevent wasted expired predictions, so the operational cutoff remains.
npm run bench now runs the general repeated-workflow harness, comparing off
and on with independent persisted learning in the current checkout. Supplying
--baseline adds a prior-release arm. The former default is available as
bench:mock and is explicitly scoped to GitHub-rule mechanics. The unused
daily-workflow generator and its unused comparison API are deleted. Paired
accounting tests, generic replay evaluation, and real-server measurements remain.
Benchmark methods and historical-result caveats now have one authoritative guide. Broad instant-call and zero-waste claims are removed. Earlier implementation plans/specifications are archived, and unused LinkedIn assets are deleted. Recent-history learning, secret filtering, retention/disk limits, and read-only/invalidation protections remain in place.
The 360-session paired qualification preserved useful calls (204/336), terminal waste (83 calls), and mean tool wait (96.25 ms) versus v0.19.0. The default suite passed 853 tests with seven skips; real filesystem/Git checks and an installed-package Node 18 MCP smoke test passed. These results support a smaller implementation with preserved behavior, not a new speedup claim. The benchmark guide records the methods, feedback ablation, live check, and limitations.
v0.21.0 (2026-09-07): native Codex setup¶
on, off, status, sync, and auth now accept --client codex; Claude
Code remains the default. Codex setup wraps enabled, supported user-level
stdio and Streamable HTTP MCP servers through Codex's configuration API.
Server names, tool policies, and environment settings are preserved. Local
CLI, app, and IDE clients using the same Codex host share that configuration;
restart the client after setup.
Configuration writes use version checks and save recovery records before
changing registrations. off restores the fields Speculate changed while
preserving unrelated later edits. Conflicts retain their recovery records and
are reported instead of overwritten. Learning and OAuth credentials remain.
At proxy startup, Speculate rereads Codex tool policy from base on-disk
configuration and trusted project layers. It excludes disabled or
approval-requiring reads and disables speculation if those policies cannot
be read or understood. Requested calls continue through the ordinary proxy path.
Native setup changes only user configuration. Project-owned or layered
transports, remote executors, header helpers, ChatGPT session authentication,
and custom OAuth settings are skipped. Hosted plugin/app tools are outside
local MCP wrapping. Speculate uses its own OAuth login through
auth --client codex. No Codex hook is installed: rerun on --client codex or
sync --client codex after adding servers. See the
Codex setup guide for scope and recovery behavior.
Session-only --profile and -c overrides in another Codex process are not
visible to these checks. Sessions relying on those MCP restrictions require
explicit configuration or speculation off until that context is supported.
The wrapper now forwards the environment supplied by its host to stdio children, including credentials and server-specific settings. Codex server working directories and literal HTTP headers retain their original meaning. Setup checks Codex's server eligibility before and after registration and undoes changes rejected by managed policy.
Qualification includes real Codex 0.153.4 stdio and authenticated HTTP fixtures, startup policy changes, exact transport restoration, and an installed-package Node 18 smoke test. CI runs the native Codex check on Linux, macOS, and Windows. These are functional checks, not a new speedup claim.
v0.22.0 (2026-09-07): one switch for both clients¶
speculate on now enables Claude Code and Codex together. off, status,
sync, and auth also default to both; --client claude or --client codex
targets one without changing the other. Each client is attempted independently,
and partial failures are reported with a nonzero exit code.
Claude Code activation covers user-scope servers, including entries hidden by a project override, and approved servers in known project directories. Its session-start hook picks up new projects as they are opened. Global off stops automatic wrapping in every project and restores recorded registrations across projects. Restore conflicts keep their recovery records. Interactive global updates and session hooks share a lock to avoid conflicting writes.
Codex setup installs a user-level session-start hook for automatic sync. Codex
requires review and trust through /hooks; Speculate does not grant that trust
itself. Off removes Speculate's hook and disables automatic sync. New server
registrations may take another session to load in either client.
The scope remains MCP servers that the clients can configure. Codex project-owned or layered transports, unsupported authentication/helpers, hosted connectors, built-in tools, and shell commands remain outside this integration. Existing tool restrictions, read-only checks, and prediction behavior are unchanged. See the setup guide for the client scopes and hook requirements.
The full local suite passed 1,015 tests with eight skips. Native Claude Code and Codex checks covered multiple projects, mode changes, newly added servers, independent shutdown, and restoration; stdio/HTTP workflow checks also passed. No model/provider calls were made, and no new speedup is claimed.
v0.23.0 (2026-09-15): experimental context-aware sessions¶
speculate run claude|codex packages the experimental session observer for both
native clients. Hook mode is the default; proxy mode also observes supported
model traffic, and off mode retains ordinary MCP prediction. Session context
feeds the existing permission, admission, execution, and cache path. Observer
feedback is scoped by client, signal source, and tool route, and actual
consumption influences future prediction utility.
The cleanup removes unused profile-era types and constants, an unreachable
parser-miss branch, and a test-only memory wrapper. Existing configuration,
legacy installation recovery, persisted state, and the parserMisses statistics
field remain compatible. Compiled client adapters now locate the packaged
observer hook correctly. Installation and command documentation distinguish
managed setup from experimental session observation.
The observer remains experimental. The original 1,000-record replay failed its speedup and waste gates. A later 60-record synthetic diagnostic showed a warm cross-server benefit under fixed timing, but neither that result nor this package release establishes native day-to-day speedup. See Observer results and Observer compatibility for evidence and limits.
v0.24.0 (2026-09-15): context-aware launch by default¶
speculate run claude|codex now requests the model-proxy route by default and
activates it only after verification. If the native provider route, temporary
controls, or preflight cannot be verified before client startup, the launcher
reports the downgrade and starts with supported native hooks.
Operators can select --observe hooks or --observe off explicitly. Off mode
disables session observation while retaining ordinary MCP prediction.
The launcher continues to preserve native arguments, account, provider, model, effort, permission policy, sandbox, and transport. Speculative work requires host permission for its specific tool route and Speculate's read-only policy; result reuse separately requires an exact argument match. The model relay observes traffic and neither executes MCP calls nor adds model requests.
speculate on remains the persistent MCP registration choice for Claude Code
and Codex. Its session-start hooks synchronize supported registrations; on
alone does not add model or conversation observation. Managed on and wrap
setup default to annotated policy, while the explicit configuration schema
continues to default to strict policy.
This release expands default activation by product direction. It does not add native performance evidence or change the historical replay gates: the 1,000-record observer replay still failed its speedup and settled-waste gates, and native day-to-day speedup, native hook delivery, and live API-key routing remain unverified. The v0.23 hook-default release record and its measured artifacts remain unchanged.
v0.25.0 (2026-09-16): automatic onboarding¶
After installation, run speculate. It detects installed Claude Code and Codex
clients and starts the available client with context-aware observation. If both
are installed, an interactive terminal asks which to launch. Noninteractive
invocations with both clients print explicit commands without reading stdin.
If neither is installed, Speculate prints installation links and next steps.
Onboarding reuses the existing session launcher and automatic MCP wrapping. Native sign-in, trust and permission prompts remain with the selected client. Explicit commands, model proxy defaults and startup hook fallback retain their behavior. No persistent preference or shell configuration is added.
Shared hook handling, launch configuration and package discovery replace duplicated implementations. Unused internal methods and superseded planning documents are removed. Current benchmark evidence and performance limits remain unchanged; this release makes no new speedup claim.