为codebase-memory-mcp打分
给出您宝贵的评分:
手机端可长按上方图片保存到相册,或点击「下载/分享」分享到微信
使用 codebase-memory-mcp,你可以:
高性能代码智能 MCP 服务,将代码库构建持久知识图谱,毫秒完成仓库索引,支持 158 种编程语言,查询 Token 减少 99%,单静态二进制无依赖。
用户评论 (0)
2026年08月11日
2026年03月20日
2026年03月05日
2026年03月10日
2026年09月08日
2026年08月23日
2026年08月21日
2026年08月20日
2026年08月16日
v0.10.8
2026年08月19日
v0.10.8
Note on v0.10.7: this release supersedes v0.10.7, which was lost to a release-pipeline
publishing error (published under the tag0.10.7instead ofv0.10.7; package installers
resolve the v-prefixed tag, and release immutability makes the name unrecoverable). The npm
and PyPI 0.10.7 packages are deprecated/yanked for that reason — their binaries were fine,
their download URLs were not. v0.10.8 carries the identical code content plus the pipeline
hardening that prevents a recurrence.
A community-heavy patch release: 19 pull requests merged since v0.10.6, most from
contributors. Thank you all!
Graph correctness
- Cypher aggregates no longer undercount (#1196). Both truncation mechanisms are fixed:
unlabeled scans stopped collecting candidates at max_rows before aggregation ran
(#1323, @Enferlain), and relationship-expansion buffers were capped so aggregation never
saw rows beyond an internal growth limit (#1698).count()/collect()now see the
complete match set;max_rowslimits only the rows returned, as documented. - Python aliased from-imports resolve to the real definition (#1371, @Joseph-MingEn).
from .gate import execute as bridge_executenow produces a CALLS edge toexecute
itself rather than losing the call, and aliased base-class imports land on the real class
node. On our django benchmark this recovered 26 INHERITS and 42 OVERRIDE edges with zero
losses elsewhere, and left TypeScript graphs byte-identical. - Cross-language suffix matching for qualified-name lookups (#1647, @rudi193-cmd).
- search_graph stops returning semantic-only ghosts — results backed by nothing but a
similarity edge no longer surface as if they were definite matches (#1319, @JJordan0C). - List-valued fields extract correctly instead of collapsing to their first element
(#1325, @Enferlain). - Index responses report persisted coverage truthfully (#1326, @Enferlain): an
incremental run that does not revisit a file with recorded parse gaps no longer reports
those gaps as vanished — the response now composes its skipped/parse-partial summaries
from the stored coverage state, falling back to per-run data only if that read fails.
Windows
- Non-ASCII search content no longer degrades to
?(#1704). The search_code raw scan
pipes PowerShell output, and PowerShell 5.1 encodes that pipe in the console OEM
codepage — so Cyrillic, CJK, or accented content turned into question marks depending on
which console the server inherited. The pipe is now pinned to UTF-8 unconditionally. - search_code understands UTF-8 output paths and gains a context column for matches
(#1253, @anfedoro). - Code search is bounded and cancellable — abandoning a query no longer leaves an
orphaned scan running (#1608, @ertankucukoglu).
Platforms & tools
- FreeBSD support (#1467, @PR9000).
- list_projects pagination: a lean default response plus offset/limit and
include_details for the full view (#1181, @tmonestudio). - Extensionless shebang scripts detect their language (#1203, @ibaldr89).
- Index-mode capability rebuild (#1263, @astandrik).
- is_test detection for tests/ directories (#1308) and a diff-aware clang-tidy
hook (#1310, both @Yyunozor).
Release pipeline (the v0.10.7 lesson, hardened)
- Release gates fail closed on cancelled jobs: a timed-out gate can no longer cascade into
a silently skipped test matrix (#1719). - Malformed version inputs are refused preflight — a bare, non-v-prefixed version can never
again publish an unresolvable tag (#1719).
Internal
- Test harnesses use isolated daemon runtimes so suite runs cannot touch the account
daemon (#1695, @astandrik); LF checkout contracts for shell entrypoints and git hooks
(#1314 @mlandolfi90, #1699 with @xumian520); the stdin-gate regression tests no longer
depend on the registry shipping a zero-argument tool (#1704).
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 61–68).
Every shipped binary was clean.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64 |
cb6dc545a8cb714799461ba7d8c223dbca7d02ca46ea5a4215c1beb450fd8e09 |
clean |
linux-arm64 |
e861b4ae831a24ab69e82bb9c4ca8c70029af55b9a074ef0ed2565a53c0de24c |
clean |
linux-amd64-portable |
1175645cb30560e7e47d78611cd1bcb509478eaf6d4e51f72fe18327ee9c1351 |
clean |
linux-arm64-portable |
0bf1b3cf3659f6dcdb01bb6313a93dcbcb53e2cd1a52820f024df7a0cf48f44e |
clean |
darwin-amd64 |
1d5b79257d91973809ee70482781db3d1a78f62fc1cd5d18a5ab0608451d16d4 |
clean |
darwin-arm64 |
2412e017268bef8f847f38d1b0f79f63185b38c27fe6fba637067bfc87c0eedf |
clean |
windows-amd64 |
b4b403b1d7c4def3785f148b93f345ce8427858f4f5489ce28580c4387a336a6 |
clean |
windows-arm64 |
67b0341ee62f07f850d3954e4f387855f90ea8c6c4b7ed41b8a62d61344373a4 |
clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
详细ChangeLog0.10.7
2026年08月19日
Warning
Broken for npm/PyPI installs — superseded by v0.10.8. A publishing error released this under the tag 0.10.7 instead of v0.10.7. The npm/PyPI package installers resolve their downloads via the v-prefixed tag, which cannot be created retroactively (release immutability tombstones the name). Direct downloads from this page work normally — the binaries themselves are fine and fully verified. Use v0.10.8 or newer for package-manager installs, or scripts/setup.sh.
What's Changed
- fix(extract): converge Function/Method is_test with the tests/ path filter by @Yyunozor in #1308
- fix(hooks): scope pre-commit clang-tidy to staged changes (#1264) by @Yyunozor in #1310
- fix(mcp): isolate semantic-only JSON search by @JJordan0C in #1319
- fix(search): preserve compound requested fields by @Enferlain in #1325
- fix(cypher): scan all unlabeled query candidates by @Enferlain in #1323
- fix(registry): drop suffix_match CALLS across language boundaries by @rudi193-cmd in #1647
- fix: rebuild index when mode adds capabilities by @astandrik in #1263
- fix(daemon): add native FreeBSD process-image identity and /home log-path alias by @PR9000 in #1467
- fix(mcp): restore scalable project discovery by @tmonestudio in #1181
- fix(discover): detect extensionless scripts by shebang by @ibaldr89 in #1203
- fix: preserve UTF-8 in search_code source by @anfedoro in #1253
- fix(build): force LF line endings for shell scripts by @mlandolfi90 in #1314
- fix: unbreak main — stdin-gate #1359 guards + Windows search_code UTF-8 pipe by @DeusData in #1704
- fix(test): isolate harness daemon runtimes by @astandrik in #1695
- fix(python): resolve aliased from-import CALLS to real def by @Joseph-MingEn in #1371
- fix(cypher): expansion materializes every matched row — the cap falsified aggregates (#1196) by @DeusData in #1698
- fix(mcp): bound and cancel Windows code search by @ertankucukoglu in #1608
- fix(build): LF contract for extensionless git hooks + a line-ending guard by @DeusData in #1699
- fix(index): preserve persisted coverage summaries by @Enferlain in #1326
New Contributors
- @Yyunozor made their first contribution in #1308
- @JJordan0C made their first contribution in #1319
- @Enferlain made their first contribution in #1325
- @rudi193-cmd made their first contribution in #1647
- @ibaldr89 made their first contribution in #1203
- @anfedoro made their first contribution in #1253
- @Joseph-MingEn made their first contribution in #1371
Full Changelog: v0.10.6...0.10.7
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 60–68).
Every shipped binary was clean.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64 |
9f24d35e42d511c9d5de8f2f3a2c503bfce7e920901f94d0cff837d1371312cd |
clean |
linux-arm64 |
ee517a1b83cda7232bf9f0e2d02807a25207937796bde26dd1dd518be8fb7dd1 |
clean |
linux-amd64-portable |
e9772b28473c11016e59d4dc272b8290deb1efe633c17eb942676f1595b5011e |
clean |
linux-arm64-portable |
a516c044a59c2e8be40366c84aab2a04c2d112298f496f8204f498cbea7883ba |
clean |
darwin-amd64 |
2d5b042b59189645e6975bd3e9772ec760a8368164223fdb85c55a9c67f1d2d2 |
clean |
darwin-arm64 |
24c8b6ee8711e465db359c2ed3ba87c9c350a105f2e77f7d3e4c72b098cd43af |
clean |
windows-amd64 |
2d55ab63c05011a6c00939fc79e17ae0432c4b1fe7675b6dcc86e242a1be437b |
clean |
windows-arm64 |
c5d26ab2e4f59d7e7bc9fbfaa3e08c92127b5ea75c2b7942087edb8a49828fca |
clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
详细ChangeLogv0.10.6
2026年08月17日
codebase-memory-mcp v0.10.6
Two stories in one release: the 0.10.x indexing slowdown is gone, and the install
failures that survived v0.10.5 are fixed — including every construct from the configs you
sent us. If cbm got slower for you since 0.9.0, or install still refused your Hermes,
goose, OpenCode, or Codex config, this is the release to take.
The indexing regression program (#1669)
Since 0.10.0, indexing large repos had drifted from slower to unusable — java 6× worse,
C# 2.6×, TypeScript 2×. The cause was not one bug but a family: per-file work that scaled
with the whole corpus. All of it is fixed, measured on the same host against the same
corpora as the reports:
- Java 477 s → 86 s (elasticsearch): cross-file resolution rebuilt a registry of every
definition in the corpus per file. It is now built once and shared, with a per-file
overlay for local symbols. - C# 1211 s → 450 s (dotnet/runtime), now with 48% more edges than 0.9.0: three
corpus-proportional scans eliminated, plus an import-context bug that had been silently
suppressing C# usage edges — one 147 KB generated file went from 490 s to 6.8 s. - TypeScript 37 s → ~24 s (microsoft/TypeScript): the expression-type evaluator
re-evaluated shared subexpressions once per enclosing alternative — 2^n on tsc-compiled
spread chains. A per-node memo makes it one evaluation per node; the pathological file
from the TS conformance suite dropped from 20 s to effectively zero. - Linux memory blowup fixed (#1654): v0.10.4 could OOM a 36 GB machine indexing a tree
v0.9.0 handled in 13 minutes. mimalloc's Linux arena-commit default is restored. - It stays fixed: a new complexity guard suite runs in CI on every platform and trips
on counter ratios (never wall time) if any pipeline pass goes superlinear again, and
CBM_PROFILE=1now prints per-pass timings from any production binary.
Install: the constructs from your real configs
Hermes (#1631, reported by @iandol and Galaxy-VN, root-caused with an isolated repro by
@rg6304): four pieces of ordinary YAML made install fail permanently — empty flow
collections (plugins: [], tool_choice: {}), block sequences at column 0, double-quoted
strings continued with a trailing \, and mid-word apostrophes (LET'S GO in a persona
string opened a "quote" that never closed). All four are accepted now; quotes and
anchors are indicators only where a value begins, the same rule the * fix used in
v0.10.4. Both reporters' full configs — 15 KB each — install end-to-end with every
original byte preserved.
Entries we wrote ourselves are recognised again. Older cbm versions wrote command:
unquoted; the goose block gained a required field; your entry was then declared "foreign"
forever. An entry under our key that matches a shape any previous release wrote — or
whose command is our binary at a moved path — is repaired in place. Truly foreign entries
are still refused byte-untouched.
goose (#1673 + #1675): the extension block now carries the name field goose's parser
requires — before this, goose silently dropped the entry and the "successful" install was
invisible. envs: {} in a sibling no longer aborts the write (community fix by
@pcristin — a model contribution, merged as-is). Old blocks repair on upgrade.
OpenCode / annotated MCP entries (#1630, #1582): when a client adds keys to our entry
("enabled": true and friends), repairs now replace only the command value — comments and
client keys survive verbatim. And ownership comparison is separator-insensitive: an entry
storing C:\...\codebase-memory-mcp.exe is the same binary as the installer's
C:/.../codebase-memory-mcp.exe. @gotspatel's posted config was the file that cracked
this one after persistence across four releases.
Windows-authored configs with a UTF-8 BOM (#1656): PowerShell's Set-Content writes a
byte-order mark, and every YAML edit failed on such files regardless of content — the
reporter proved content-independence with a 26-byte file, and those 26 bytes were their
23-byte control plus exactly the BOM. It is treated as a prologue now: skipped for
structure, preserved on write.
Codex (#1633, closing @spam0115's residual from #1580): the hook reconciler accepts
the managed hook pair v0.10.2 wrote (including the Windows CRLF+BOM variant) and rewrites
it in place; refusals now name their reason instead of failing generically.
Windows: the DACL stops fighting you
The runtime directory's security descriptor was re-stamped on every process start,
whether or not anything was wrong. #1601 counted eleven no-op "Security change" USN
records against a single file in a day; #1620 lost atomic publishes to the rewrite window.
The re-stamp now fires only when the directory is not already in the exact owner-only
state — and directories damaged by the pre-v0.10.3 DACL regime (the empty-DACL logs
folder @patxisan found, which explained a month of unreadable 0-byte worker logs across
six reports) are detected and repaired in place. (#1416, #1601, #1620)
Also shipped: CBM_RUNTIME_DIR is forwarded in generated Codex configs (#1664 — a
relocated daemon was invisible to Codex-spawned servers), a missing final newline is no
longer reported as a partial parse, and update no longer names an installer that is not
there (#1653).
Credits
@iandol · Galaxy-VN · @rg6304 · @gotspatel · @spam0115 · @pcristin · @Carnival-z ·
@tmonestudio · @patxisan · @roosteer — the configs, ACL dumps, and minimal reproductions
in this release's issues did most of the diagnostic work. Thank you.
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 60–68).
Every shipped binary was clean.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64 |
749bd2c37c2ad891392e0872b9e1fb0df7432f04c7ca0f49d11d00b24c5f2ec8 |
clean |
linux-arm64 |
e41cba76e58005edd4d21e19cbe1faf2c8f684c56fe78141c9956a9c632c9d26 |
clean |
linux-amd64-portable |
8eea4ec828cd0cfa1c406cda5640a8dfa5381878b4d96b106cdc53aff3733508 |
clean |
linux-arm64-portable |
c783294c009186c1c91c79d3611ef991af4db7816dbad13a922538513af97cac |
clean |
darwin-amd64 |
34851da132369455b35530879c13e95360429251ab5e9604bc301beb25c9bcc5 |
clean |
darwin-arm64 |
8fdbdb2ec81f9526b34e71dffd46589b0e06fd5d60773732fd256c684d0251f4 |
clean |
windows-amd64 |
3099e91c29d8507c94bfb7ed431a1f89459ad9afdcea413cbab23c9ec3cfd972 |
clean |
windows-arm64 |
f5f0585308d7f6130386857e1747a338abe194cb9b082c37d81acf43b8f18e9d |
clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
详细ChangeLogv0.10.5
2026年08月15日
codebase-memory-mcp v0.10.5
An install-and-startup release. If v0.10.4 refused to install for you, refused to write your
config, or told you something that turned out not to be true, this is the one to take.
Several of these are cases where cbm refused its own work — an installer directory our own
binary then rejected, a config entry we wrote ourselves and later called foreign, an error
message that named the wrong directory and sent people to inspect a file that was fine.
Windows: installs that refused themselves
The installer was creating a directory our own binary then rejected. install.ps1 made its
staging directory with New-Item, which inherits whatever %TEMP% carries. The downloaded binary
validates its own directory and refuses inherited cross-account mutation grants — so we handed our
own check a directory we had just made wrong. Measured on a Windows VM: a fresh staging directory
came up unprotected with five inherited ACEs; it now carries a single owner-only entry.
That is the acl-grants-cross-account-mutation to S-1-5-21-… failure. Reported by @RoccoZero,
@aecesr, @Kiborgik and @nasodaengineer, whose corrections stopped us fixing the wrong
thing twice — @Kiborgik warned that keying on the sandbox group's name would miss a synthesized-SID
variant, and @nasodaengineer disproved an off-by-one-RID theory by showing the ACE really was present
on the inherited DACL. @spam0115 independently wrote a working installer whose decisive line was
the same icacls /inheritance:r, and handed it back to us rather than keeping it.
Sandboxed applications no longer block the daemon. The ancestor walk refused any
mutation-granting entry on %LOCALAPPDATA%, including AppContainer identities left by ordinary
installed software. On reported machines the blocking entry resolved to a shipping desktop
application — nothing a user could reasonably remove. Ancestor components now tolerate AppContainer
package and capability identities; the private runtime directory keeps demanding the exact current
user, unchanged. Approach and the ancestor-only boundary follow @mlandolfi90's work.
Startup: when the daemon could not be reached
-
The rendezvous directory can be relocated in a shipped build via
CBM_RUNTIME_DIR. Previously
the only relocation hook was compiled out unless test seams were enabled — so a test build
started where the product build did not, and when the default ancestry was unacceptable
every invocation failed,config listincluded, leaving no way to reach the settings surface
and fix it. (#1537, #1574, #1621)@tmonestudio and @Carnival-z each proposed exactly this, independently, before we built it
— @tmonestudio in #1574 with the ancestry walked and the offending component named, @Carnival-z in
#1621 after finding thatCBM_CACHE_DIRmoves the cache but never the rendezvous. Both had opened
pull requests reaching the same conclusion this change implements; the merged commit credits
@tmonestudio by name for the approach and the variable name. -
Refusal messages now name the directory that actually refused. The ancestor walk validates the
directory it is already in, but printed the name of the child about to be entered — so #1537 was
told.cachewas at fault when the home directory was refusing, and #1621 was told
cbm-daemon-501when/private/tmpwas. Both reporters inspected exactly what we named, found it
clean, and said so. They were right, and we kept pointing at it for weeks. The message now
names the containing directory and says explicitly not to check the one it used to blame.
Errors that name what actually refused
Three fixes, one complaint: our messages did not say enough to act on.
secure CLI coordination could not be created (endpoint)now prints the validation detail —
the directory and the rule that refused. Four separate reporters hit that message; one built an
instrumented binary to discover a single ACE was the cause.- A refused agent-config write says what the target is — whether it exists, and what kind of file
it is. Nine distinct fail-closed conditions previously collapsed into one indistinguishable failure. - Neither prints
errno. It is called from 119 sites and can be stale, which is exactly how an
earlier release handed a reporter a fabricated "file not found" for a permission decision.
Config files we wrongly refused to touch
- An MCP entry your client annotated is no longer treated as foreign (#1630, #1582). OpenCode
writes"enabled": truebeside thecommandandtypewe write; our ownership check demanded an
exact key-set match, so a three-key entry with two recognised keys was classified as somebody
else's and left alone — we refused to touch an entry we had written ourselves. In one reporter's
file every MCP server carried the key, so this is OpenCode's normal shape, not an unusual hand-edit:
anyone who has ever toggled a server on or off in the UI was hit. - YAML values containing
*or&are no longer mistaken for aliases and anchors (part of #1631).
Prose asterisks, a glob inside a description, a kaomoji — all were refused as if they began a node.
The test is now positional. Root-caused by @rg6304, who reproduced it in isolation, read the
source, and corrected our hypothesis: the constructs we had guessed appear nowhere in the failing
file. This is one of four constructs in that report; the rest are still open.
Data loss and diagnosis
uninstall --helpno longer performs a real uninstall (#1038). It removed the binary and every
agent configuration for someone asking what the command does. The guard is checked before argument
parsing, so a-yelsewhere on the line cannot auto-confirm the destruction being prevented.- A crashed worker's log now survives and names its run (#1145, #1130, #1132, #1450, #1133, #1070).
Six reports ended at the same dead end: the supervisor prints the log path and the file is 0 bytes,
so the one diagnostic that would explain the failure was silenced by the failure itself. cli <tool>no longer blocks forever reading standard input (#1359) for a tool that declares no
arguments.--jsonand--progressare stripped before the stdin fallback is reached, so
cli list_projects --jsonhung too — the reporter's correction that "no flags" was not the trigger
was right.
Reliability
- Spawn retries no longer grow without bound (@moffermann). The clamp we shipped could never
fire, while the doubling it was meant to bound ran free — a future budget increase would have meant
a 40-second wait where the retry exists to avoid exactly that. Both current budgets are unchanged. - Two concurrent sessions can no longer be handed the same temporary directory
(@ertankucukoglu) — a process-global buffer in the Windows temp-directory helper. - Instrumented test builds now know they are instrumented (@moffermann). Four places asked
that question four different ways, so some sanitizer lanes measured instrumented code against
native timing budgets.
Correctness
check_index_coverageno longer reports every path as unavailable when a repository has more
than 2,000 ignored files (@SunneeYang). A truncated catalog was treated as proof that no
per-path answer existed, including for paths with a current hash record.- Go routes built by string concatenation now resolve (@AmirF194). A handler registered with
base + "/orders"produced no usable route path; the literal half is now recovered, and a path
assembled from a variable is still left unresolved rather than fabricated. - Cypher queries repeating a variable-length node no longer double it (@jstar0), matching the
unification the fixed-length path already did. - A Windows publish failure says what actually failed. The atomic publish discarded the Win32
error and reported a bareerrno, and one path returnedPERSIST_FAILEDwith no log line at all —
which is how a reporter with an ACL problem was told to check that their repository path existed. - Cross-repo intelligence works again on any project that has ever recorded a parse miss
(@vitaliy-shatskiy, #1609). Indexing writes an internal<name>::missedrow into the same
database, and the validation required exactly one row over all of them — so such a project was
rejected as "not indexed", both as source and as target, with no workaround available to the
operator. The same defect was fixed forlist_projectsin #1044; this site never learned it.
Performance
- Windows code search prefilters simple suffix globs (@ertankucukoglu), so a
*.extfilter no
longer scans and then discards. The pattern reaching the shell stays behind an allowlist, and the
original post-scan filter is retained as a second guard. - A failed search launch no longer leaks its compiled path filter (@ertankucukoglu).
search_codecan report per-phase timings on request (@ertankucukoglu) —scope_ms,
scan_msandenrich_ms, behinddebug: true. Default output is unchanged, and the measurement
itself is skipped unless asked for, so nothing is paid for by callers who do not want it.
Updating from 0.9.x is possible again
If you are on 0.9.x and update has been refusing to install, this is the release that fixes it
(#1134). v0.10.0 consolidated to one archive per platform, so the codebase-memory-mcp-ui-* names
that 0.9.x updaters ask for stopped existing and those updaters 404'd. We fixed that by publishing
byte-identical copies under the legacy names — but that step runs after checksums.txt is
generated and signed, so the aliases never appeared in it, and an updater that verifies the name it
asked for refused what it could not check:
warning: codebase-memory-mcp-ui-darwin-arm64.tar.gz not found in checksums.txt
error: refusing to install an unverified download
We fixed the 404 and moved the failure to verification. Confirmed on v0.10.4: eight ui-* archives
published, none of them listed. Their digests are now emitted before the attestation step, so the
attested checksum file covers both names — no new bytes, and an alias is a copy, so its digest is by
construction the one already computed. Reported by @AmooAti, whose installer was behaving
correctly the whole time.
Also
- The test suite no longer writes into the developer's real agent configuration (@phyrexia).
- A release-gate verdict no longer depends on a live network fetch. The licence provenance audit
downloaded the canonical Apache-2.0 text while running and byte-compared it, so a failed request
reported a licence discrepancy that did not exist — reddening an unrelated contributor's pull
request for over two weeks. It is now pinned by digest. - MCP envelope fuzz coverage for wrong JSON types (@lukiod).
- Release notes now report the verdict for the binary actually shipped, and no longer claim a minimum
engine count that stopped being true.
Still open — not fixed in this release
Being explicit, because several of these have been open a long time and shipping quietly around them
would be worse than saying so:
- #635 — on Windows, clients using the Python MCP SDK never complete the handshake, so no tools
appear. Open 50 days. - #1633 — Codex refuses
config.tomlentries cbm itself wrote in an earlier version. The
OpenCode half of this pattern is fixed above; the Codex/TOML half is not, and we have asked the
reporter for the exact entry shape. - #1631 — three of the four YAML constructs in that report remain unsupported.
- #1620 — on one Windows host, indexing always fails with a generic
Pipeline failed: our own
protected cache-directory DACL breaksMoveFileExW(REPLACE_EXISTING). Only the diagnosis half is
fixed here — the publish failure now reports the real Win32 error instead of a discardederrno,
so the next person is not sent to check that their repository path exists. The behaviour itself is
unchanged: a fix broke daemon startup in testing and was pulled rather than shipped half-right. - #1650 — where another agent's sandbox (OpenAI Codex CLI) has given a local group
profile-wide ACEs, the daemon still refuses to start. The tolerance added above covers AppContainer
identities, which a process cannot escape; an ordinary local group names a principal that really can
be exercised, so it is still refused deliberately.CBM_RUNTIME_DIRis the intended way out — point
it at a directory whose ancestry passes. - #1632 —
updatecan print aninstall.shpath that does not exist, with no fallback. A fix is
in review; it did not make this release. - #1654 — a large tree that v0.9.0 indexed in ~13 minutes fails to index on v0.10.4, reaching
about 1% in 45 minutes and then dying on allocation failures. Reported today and not yet
reproduced by us; nothing in this release targets it. If you are on a large repository, this
release does not change that behaviour. One thing here does help though: a crashed worker's log now
survives, so the next run should produce a diagnostic instead of an empty file. - #1343, #630, #1083 — reproduced by their reporters, not yet by us.
If you are hitting one of these, the issue is the best place to follow it; we would rather point you
at an open ticket than imply it is handled.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1. - Package managers: update through them as usual.
- No index, cache, or config migration.
Full changelog: v0.10.4...v0.10.5
Security Verification
Every binary published below was scanned by VirusTotal before smoke and soak testing, and the verdict for the exact shipped bytes is linked per product (decisive engines: 60–66).
1 shipped binary carried only the documented single Microsoft machine-learning !ml result; no other decisive engine reported malicious or suspicious.
Correction (post-publication). The table above originally reported
linux-amd64as carrying a Microsoft!mldetection anddarwin-arm64as clean. That was taken
from the pre-selection candidate scan. The final scan of the exact shipped bytes, run minutes
later, returned the opposite verdicts for those two files, and the table now reflects that
final scan.Nothing about the binaries changed between the two scans — same bytes, same hashes. Microsoft's
machine-learning engine simply returned a different answer for each of them within the same hour,
in opposite directions. The other six products were stable and clean in both scans.This is worth stating plainly rather than quietly editing: a
!mlverdict is a probabilistic
classifier output, not a property of the file, and we cannot make it stable from our side. Every
hash above is linked so you can check the current verdict yourself — and it may well differ again
from what we recorded.
| Product | Shipped binary | VirusTotal verdict |
|---|---|---|
linux-amd64 |
82750cd15af0165241c6a22e783eaf8c3e57575aed86aaf2a84a866a15de7ab6 |
clean |
linux-arm64 |
f944b83584fd9b6e83f5199fd71bf628218516fe0af2e8ced9376ee1367f19da |
clean |
linux-amd64-portable |
dfd7db016354f223cb8c8a22d8c4ceb418b51b8b4bce541669ff32288731ad03 |
clean |
linux-arm64-portable |
235258163aae1e573ae61933fee31f1e023d593211acc1243507401727f6f9a8 |
clean |
darwin-amd64 |
785862e49b5939b6093ca150d322fb6666a237dec93c2d74f1c632f2edcc3f1a |
clean |
darwin-arm64 |
6d3c5be6412008c039b1dbaadc2a295a032bbcae52671e71ee13233b665495a8 |
Microsoft !ml |
windows-amd64 |
f8aee1683f94f7598ae70640a588f27d51811433f046b39c1faf186c17010739 |
clean |
windows-arm64 |
c3a91dcab960250521a31dd831e2aad6785971db40c013513d37f17eda95b3a0 |
clean |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
详细ChangeLogv0.10.4
2026年08月14日
codebase-memory-mcp v0.10.4
An install-and-diagnostics release. If v0.10.3 refused to install or start for you — particularly on Windows, or on a machine where you installed cbm through a package manager — this is the one to take.
Most of what follows came from people who filed precise reports and, in several cases, root-caused the bug before we did.
Windows: elevated accounts could not install or start at all
Running as an Administrators-group account meant install failed with activation transaction I/O failed: owner-not-current-user, or the MCP server exited during startup with nothing in the log but a closed transport.
The cause was ours and specific: we compared a file's owner against the token's user SID, but Windows stamps newly-created objects with the token's owner SID — which for any member of the Administrators group defaults to BUILTIN\Administrators. So the installer created its own staging file and then refused it, seconds later, as belonging to someone else. No amount of rebooting or closing processes could help, because nothing was running.
We now also accept the SID Windows actually stamps on what we create. This is narrower than "trust any administrator": a file owned by a genuinely different account is still refused. Reported by @spam0115 and @gotspatel, and it is very likely the cause of the long-running #1416 as well.
Installing no longer touches things it doesn't own
Two changes for people whose setup we were trampling:
A binary installed by mise, Homebrew, nix, asdf or cargo is left alone. Previously install dropped a second copy into ~/.local/bin — shadowing the managed one depending on PATH order — and appended to your shell rc file. Now it configures the agents and leaves the binary and your PATH untouched, update refuses honestly and names the command that will work (mise upgrade, brew upgrade), and PATH is only modified when we actually placed the binary. Reported by @SC-CTS.
You can choose which clients get configured. install wrote MCP config, agents, skills and hooks for every detected client, and reverting the ones you didn't want was pointless because the next install recreated them. --clients=claude,codex now restricts it, and --clients on its own lists every supported token. Reported by @PsyTech-Rob.
Errors that tell you what actually happened
Three fixes in a row here, all from the same complaint: our messages were confidently wrong.
- An activation refusal no longer points at evidence it doesn't show. v0.10.3 correctly stopped blaming "active CBM sessions" for failures no session caused, but then told you to check errors that were never printed. It now names the directory and the rule that refused.
- A server that cannot start says why. Client-path startup failures wrote to stderr only, which no MCP client surfaces — so a specific, nameable refusal reached you as "transport closed unexpectedly". Those failures now arrive as a JSON-RPC error carrying the reason.
- The UI listener can be turned off without editing JSON.
ui_enabledgoverns a loopback HTTP port, and the only way to disable it was hand-editingconfig.json; it was invisible toconfig listand rejected byconfig set. Both it andui_portare now first-class config keys. Reported by @PsyTech-Rob, who spent two debugging sessions finding the switch.
Codex, OpenCode and config-file repair
- A duplicated install no longer breaks Codex permanently (@astandrik). An owned inline hook assignment is reconciled into the canonical block instead of being ignored and re-added alongside; foreign hook entries are preserved untouched, and genuinely ambiguous TOML is refused without modifying the file at all.
OpenCode installs land in the file OpenCode actually reads (@iandol).Correction (2026-08-14): this is only half true and we should not have claimed it outright. The targeting is fixed — we now write whichever ofopencode.json/opencode.jsoncalready exists, instead of always creating a.jsonfile OpenCode ignores. But the write itself still fails on v0.10.4 witherror: agent_config agent=OpenCode op=mcp_install, reported on both Linux and Windows. Tracked in #1630. This is the second release running in which we have described this fix as complete; sorry, and thank you to the reporters who kept saying it was not.- An orphan marker we wrote heals itself. An earlier install could leave a closing
# <<<marker with no opener, after which every install failed that client outright. Removal now strips the stray line. A write still refuses on an imbalance — with one marker there is no defensible region to replace.
Query correctness and platform support
OPTIONAL MATCHstopped silently dropping rows (@SEPURI-SAI-KRISHNA). Once the hop buffer saturated, the no-match rows — exactly whatWHERE x IS NULLexists to surface — were discarded. Not a crash, not a memory error: quietly fewer results, with nothing to indicate the answer was incomplete.- FreeBSD resolves its own executable path (@PR9000), which the install and ownership checks depend on, plus a build fix for platforms outside the main three.
Security
The release pipeline now pins and verifies the external tooling it downloads before executing any of it, rather than trusting whatever the upstream project publishes at build time. Verification covers every platform the job may run on, from a single pinned reference.
Reported by Andrew Hundt (#1245).
Reliability
Subprocess spawns survive a busy machine. v0.10.3 started retrying a spawn the kernel refused with "try again", but only for about 30ms — enough for a momentary dip, not for a machine that stays process-starved for hundreds of milliseconds at a stretch, which is exactly when it happens. The retry now backs off exponentially to roughly half a second before giving up. A genuinely exhausted machine still fails fast rather than hanging, and real errors — a missing binary, a permission denial — fail immediately as before.
And the release pipeline no longer publishes its registry entry before the release is public — the bug that made v0.10.3's own registry step fail with a 404 against its own assets.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1. - Package managers: update through them as usual — cbm will now leave that binary alone.
- npm / PyPI: as usual.
- No index, cache, or config migration.
Contributors
Code in this release came from:
- @astandrik — Codex hook reconciliation, with fail-closed handling that leaves an ambiguous config byte-identical rather than guessing
- @PR9000 (Pedro Ramos) — native FreeBSD executable-path resolution, plus a build fix for platforms outside the main three
- @SEPURI-SAI-KRISHNA — the
OPTIONAL MATCHrow-loss fix, including the analysis that separated "in bounds" from "correct" - Andrew Hundt — the release-pipeline hardening above, co-authored
And these reports found bugs we could not have found ourselves, several of them root-caused by the reporter before we looked:
@spam0115 and @gotspatel (Windows elevated accounts) · @SC-CTS (package-manager installs) · @PsyTech-Rob (four separate findings in one install cycle) · @listepo (the diagnostics that identified a refusal we had been guessing at) · @iandol (OpenCode config targeting)
Thank you — particularly to those of you who kept reporting through several releases of install trouble.
Full changelog: v0.10.3...v0.10.4
Security Verification
Before smoke and soak testing, VirusTotal completed 24 executable scans: stripped and unstripped candidates for each of the 8 release products. Every scan had at least 50 decisive engines (observed range: 59–68).
3 candidate(s) had only the documented single Microsoft machine-learning !ml result; no other decisive engine reported malicious or suspicious.
| Product | Stripped candidate | Unstripped candidate | Shipped |
|---|---|---|---|
linux-amd64 |
clean | clean | stripped (09db4f424c1fca97b5d06feb75ca6540629a5b67309e39a9a551e745b081023b) |
linux-arm64 |
Microsoft !ml |
clean | debug-stripped (e0cd04812093d420a889397a603959c9a35ea3c78dea67c44db80e25a04d29b8) |
linux-amd64-portable |
clean | clean | stripped (df1b97b0f5266924072a0c020027a14fa1890fa7457183c2106ff7eab4b62122) |
linux-arm64-portable |
Microsoft !ml |
clean | debug-stripped (8427e22d1445b1e4a9a883034c12c18eb78c30e6ba183235ca79e153641a39e9) |
darwin-amd64 |
clean | clean | stripped (9ae79b267fe60438ffbba046bb5ed9e36b2b0f0c04e32970ee6995d616a8ccc6) |
darwin-arm64 |
clean | clean | stripped (75dadd4f243415c3058a8dd0c47137f4e2fd17861c5e79e70efad150ec02724f) |
windows-amd64 |
clean | clean | stripped (edfd73e60f40c687bc36b88140056838c0f95842f0839ec20cd0cb061d107f50) |
windows-arm64 |
clean | clean | stripped (48f8e9eeff39d4016a10c93084947142dee3cbc356aa5fa8df57c886beb92e0c) |
Selection is tuple-local and defaults to stripped. The selected executable SHA-256 was verified again after packaging; archive containers were not redundantly submitted to VirusTotal. Their hashes remain available in checksums.txt.
Durable evidence: candidate provenance, candidate VirusTotal results, selection decisions.
详细ChangeLogv0.10.3
2026年08月13日
codebase-memory-mcp v0.10.3
A fixes-only release, and most of it arrived as finished work from the community: six contributed pull requests are in here, alongside repairs for two regressions we shipped ourselves in v0.10.2.
Two things worth knowing before you upgrade. If you run more than one MCP client against the same project, this release closes a bug that could silently destroy a healthy index — start with the first section. And if installing or updating has been failing for you, three separate causes are fixed here: a permission rule that refused ordinary home directories, an error message that reported the wrong reason entirely, and Codex losing your cache directory. Several of those failed silently or blamed the wrong thing, which is why they survived multiple releases.
Concurrent instances no longer quarantine each other's databases
The worst bug in this release, fixed by @LynxBay. When two cbm instances raced on the same project — two editors, an editor plus a CLI call, a daemon and a stray session — the loser of the lock race could conclude the database was corrupt and quarantine it: renamed away, rebuilt from scratch, hours of indexing gone. The database was healthy; the integrity check just couldn't tell "damaged" from "busy" (#1206).
The same check had the opposite blind spot too: a database with genuinely torn pages passed as long as its top-level bookkeeping looked right, so real damage went unnoticed until queries misbehaved (#1037).
Integrity now returns a three-way verdict. Only confirmed corruption quarantines; a lock race says "project is busy; retry after indexing" and leaves the data alone; and a deep quick_check catches page-level damage the shallow probe missed. Both directions carry regression tests.
Graph determinism, twice
Two contributions from @Studnicky close two long-standing sources of run-to-run drift — the "same repo, slightly different edge counts" behavior tracked in our known issues since the 0.9.1 RC:
- When the same logical edge is produced twice with different attributes (an LSP-resolved call and a textual match, say), the survivor was whichever arrived last — a function of thread scheduling. The merge is now a total order: higher confidence wins, ties break deterministically, and the outcome is independent of arrival order by construction.
- CONFIGURES candidate collection sorted nothing before applying its cap, so which candidates survived depended on worker merge order. Candidates are now ordered by a pure content key first — notably not by node id, which looks stable but is itself handed out in merge order.
Windows: daemon files created with an empty ACL
@Kiborgik diagnosed and fixed a subtle one (#1351): the daemon's private directory combined a protected DACL with a non-inheritable ACE, which on Windows means children inherit nothing — every file created inside was born with an empty ACL and unreadable even by its owner. Directories now carry a properly inheritable owner-only ACE, so the strict security posture is kept and the files actually work. A companion fix from the same contributor closed a trap where a machine named like its user granted permissions to an empty principal; testing that on a non-domain-joined machine surfaced the mirror-image trap, so our build tooling now identifies the account by SID and stops resolving names altogether.
Repairs for two v0.10.2 regressions
Both reported within hours — thank you for the fast signal.
update --uistopped working (#1544, @ehendrix23). v0.10.2 removed the obsolete ui/standard chooser and the flags that drove it, so a command living in scripts and muscle memory became a hard error. Both flags are accepted again — they do nothing, say so once, and genuinely unknown flags still fail.- The generated Pi extension broke Pi entirely (#1550, @musichen). Pi loads an extension by calling its default export; ours exported a named function instead, and a Pi extension that fails to load takes every
picommand down with it,pi doctorincluded. The extension now default-exports its factory. Fixed independently by @musichen and @henri-edh as well, whose sharper description of Pi's loader is the comment now in our source.
Structured output stops dropping evidence
trace_path(include_evidence: true) promised per-hop strategy and confidence columns, and delivered them — in tree format only. With format: "json" both fields silently vanished, which hit exactly the callers most likely to want them: the ones consuming structured output programmatically (#1542, @asemraza). The JSON path now carries both fields with the same semantics, nulls standing where the tree format prints -.
Subprocess spawns survive a busy machine
Chasing a CI flake uncovered a real robustness gap: when the kernel refuses a process spawn with "try again" — a momentarily full process table on a loaded machine — cbm treated it as a permanent failure. A git probe or LSP server would refuse to start for a reason invisible to the user. Transient spawn failures now retry briefly; real errors (missing binaries, permissions) still fail immediately with unchanged semantics.
Install and activation: the refusal nobody could act on
Follow-up to #1537 (@listepo), and it turned out to be two defects stacked on each other.
The daemon refused to run when any ancestor of its cache directory was group-writable — the same rule we removed on the installer side in v0.10.2, in the sibling check that decision covers but that never got changed. A group-writable ~ or ~/.cache is ordinary (WSL2 ships 0775, so do several distro skeletons and any machine with a shared primary group), so for those users the daemon was permanently unusable, with nothing to close and nothing a reboot would fix. World-writable ancestors are still refused — any local user could swap a path component — and the private directory itself is still forced to owner-only, which is what makes admitting the ancestor safe.
Underneath that, the error message was inventing its own evidence: the refusal path set no error code, so the reporting fell back to printing whatever errno happened to hold, and handed the reporter ENOENT for a permission decision. They went looking for a missing file that was there the whole time. The message now names the directory and the rule that refused it.
Also in this area: the activation guard reported every refusal as "active CBM sessions could not be stopped", including failures where nothing is running at all. Those are now separate messages, the second says plainly that nothing needs closing, and neither remedy assumes a codebase-memory-mcp binary is still on PATH — advice that was unusable at exactly the moment it printed, right after uninstall.
Also fixed
- Codex saw no cbm tools when
CBM_CACHE_DIRwas set (#1562, @anton-matosov). Codex passes only explicitly-listed variables into an MCP subprocess, so the server it spawned read the default cache while the daemon used the configured one; they disagreed and the connection closed during initialization. The generated config now forwards the variable. - The installed skill file failed to load in strict YAML readers (#1554, @xezzon). Its
descriptioncontainedTriggers on:unquoted, and a colon-space in an unquoted YAML scalar means "nested mapping" — so parsers rejected the whole document and the skill silently never loaded. search_codeechoed a negativelimitback as the result count (#1511, fix by @lukiod), which reads to an agent as an answer rather than a rejected argument.OpenCode installs went into the wrong file (@iandol).Correction: this was listed here in error. The fix merged a few hours after the v0.10.3 tag was cut, so it is not in this release — it ships in v0.10.4. If your OpenCode config is a.jsoncfile, upgrading to v0.10.3 will not have helped, and we are sorry for the wasted time.- Release archives are validated before extraction (#1510, @cyphercodes): an exact member set, with symlinked members refused outright.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1— re-running the installer is the update. - npm / PyPI: update through your package manager as usual.
- No index, cache, or config migration.
Full changelog: v0.10.2...v0.10.3
Security Verification
VirusTotal completed 17 distinct extracted byte objects covering 56 exact extracted archive members.
The extraction manifest binds those associations to 14 downloadable archives by SHA-256 provenance. Downloadable .tar.gz/.zip release containers were not submitted to VirusTotal.
16 of 17 scanned objects returned 0 malicious and 0 suspicious verdicts with 57–68 decisive engine results (required minimum: 50).
1 object(s) carry a single Microsoft machine-learning detection (!ml), which this project treats as a known false positive and publishes rather than hides. Every other engine returned clean. See Antivirus False Positives for the evidence and for how to verify these artifacts yourself: objects/scan-c05a4598b3bb23d82efbde5e6bc01989297156ed153dadbd281ba8886c96f2d6--codebase-memory-mcp
Durable public evidence: associations, exact scan set, per-extracted-object results and report links, evidence checksums.
Archive SHA-256 provenance (from the extraction manifest):
| Downloadable archive | SHA-256 provenance |
|---|---|
codebase-memory-mcp-darwin-amd64.mcpb |
b430ec7898c7c857f72672faf1347a3da0b2ec9eac34ffe7940d857a42d62616 |
codebase-memory-mcp-darwin-amd64.tar.gz |
1107fea28285823e1436e4f38a4e00a0b472d8a43c379da7dfd200c914a4b9dd |
codebase-memory-mcp-darwin-arm64.mcpb |
c0f524d28970682b940e2e08f702f7c46c0c4a6d593349ccd1e409e4e11425ed |
codebase-memory-mcp-darwin-arm64.tar.gz |
0ebf02328207d4c3d862c837b5e973de5bac808df92b0941737721d467287f7f |
codebase-memory-mcp-linux-amd64-portable.mcpb |
704549677d608d61fd6164a5a65ab16d1f143732906be28d2bdfb062b56e9e3e |
codebase-memory-mcp-linux-amd64-portable.tar.gz |
254937e3e252c5caa2c507366be8258acea239dc5d1c0dd715ee425259ef492c |
codebase-memory-mcp-linux-amd64.tar.gz |
74997fb0934e70a22f20c2e112fb4d883867dc1f01a7bcdc94cf86d13b5cbd31 |
codebase-memory-mcp-linux-arm64-portable.mcpb |
aff4f741dfe34dbf68649593808abfa3312770b153607b97a0cd7a1a7227bc42 |
codebase-memory-mcp-linux-arm64-portable.tar.gz |
b4ee6aafcc2f3a12e537709e16e42dc49e98ad5c1d620eba1fbad5d83825dac5 |
codebase-memory-mcp-linux-arm64.tar.gz |
967b9eababfdbd2ef1987c571d55bc7c028cd1db7f99279830634c58db311e32 |
codebase-memory-mcp-windows-amd64.mcpb |
a88a2670fdc7b4aeb31b16dc7c37646aad85b93d44d656acb1fcae02a222a6be |
codebase-memory-mcp-windows-amd64.zip |
fa7e53dd5e6b8daf633db7bf37a923061d836a70dd380d6627736d95b998875b |
codebase-memory-mcp-windows-arm64.mcpb |
391262ebf664c0bba4ba1be343fa4fde92d1f30918811a0656dcd711df8d34e8 |
codebase-memory-mcp-windows-arm64.zip |
dd1dfc9039ee431fbdeab1dfb482ec19ea7a5f0fd821b1f04ae1599c9e772342 |
v0.10.2
2026年08月12日
codebase-memory-mcp v0.10.2
An install-and-upgrade release. If v0.10.0 or v0.10.1 refused to install, refused to update, or your MCP client showed a server that connected to nothing — this is the fix, and every one of those failures came from a user report filed in the last 24 hours.
The pattern behind all of them: gates that were right in principle refused ordinary, legitimate setups, and then didn't say why. Each one below keeps the protection that matters, drops the part that was refusing real users, and now names exactly what it refused and how to proceed.
npx (and every ephemeral install path) works again
Running cbm through npx codebase-memory-mcp produced a server that answered nothing: the client waited 30 seconds and exited with zero bytes on stdout, which agents surfaced as -32000: Connection closed. Every npx invocation hit this, deterministically.
The daemon verifies a connecting client's binary image, and it was treating two very different outcomes as one failure: "this image hashes differently than mine" (the tamper case the check exists for) and "this image cannot be examined at all" — which is what an ephemeral npx cache path always produces. The second is now admitted: the handshake immediately before it has already proven semantic version, build fingerprint, and protocol/store/feature ABI, so refusing on top of that traded a real compatibility proof for an unavailable one. A genuine fingerprint mismatch still rejects, hard, and the admission is logged out loud so the weaker check is never invisible.
Separately and unconditionally: a client that cannot reach the daemon now says so in JSON-RPC, naming the reason, instead of exiting silently. Thanks to @wassolles, who arrived with the reproduction, the admission path already read, and three candidate fixes — the one we shipped was theirs.
Installing into an ordinary Linux home works again
install.sh failed with error: failed to stage install candidate: activation transaction I/O failed — a message that sent people hunting disk errors and filesystem types for what was, in fact, a permission policy: the installer required no group-write bit on the install directory and every parent above it. WSL2 ships ~ and ~/.local at 0775, as do several distro skeletons and any site using a shared primary group.
World-writable parents are still refused — any local user could swap a path component mid-install. Group-writable parents are now warned about and accepted, while the install directory itself stays strictly owner-private: that is where the binary lands, and group write there would let another account replace the executable between verification and launch. Refusals now name the directory, its mode, and which rule refused.
Root-caused by @AmirF194 in a clean container, down to the exact predicate and line; independently confirmed by @shochdoerfer and @iandol.
Updating from 0.9.x works again
v0.10.0 consolidated to one archive per platform with the graph UI always embedded, but update still asked which variant you wanted — where "ui" could only 404, and "standard" quietly was the UI build. The chooser and its flags are gone.
Binaries already installed in the field can't be fixed retroactively, so releases now also publish ui-*-named copies of each archive. Every 0.9.x updater that asks for the old name gets the same bytes and completes normally, with nothing required from you. Reported by @iandol.
macOS install diagnostics
install.sh no longer prints No such xattr: com.apple.quarantine — a harmless line (a curl-downloaded archive simply carries no quarantine attribute) that was mistaken for the cause of an unrelated failure, to the point of becoming a bug report's title. And when installation is genuinely blocked by a running session, the error now points at codebase-memory-mcp daemon status to list the processes actually holding it, instead of asserting that sessions exist and leaving you to find them. Reported by @listepo.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1— re-running the installer is the update. - npm / PyPI: update through your package manager as usual.
- From 0.9.x: the old updater works again as of this release; if it already failed for you, re-running the installer is the fastest path.
- No index, cache, or config migration.
Full changelog: v0.10.1...v0.10.2
Security Verification
VirusTotal completed 14 distinct extracted byte objects covering 32 exact extracted archive members.
The extraction manifest binds those associations to 8 downloadable archives by SHA-256 provenance. Downloadable .tar.gz/.zip release containers were not submitted to VirusTotal.
13 of 14 scanned objects returned 0 malicious and 0 suspicious verdicts with 55–66 decisive engine results (required minimum: 50).
1 object(s) carry a single Microsoft machine-learning detection (!ml), which this project treats as a known false positive and publishes rather than hides. Every other engine returned clean. See Antivirus False Positives for the evidence and for how to verify these artifacts yourself: objects/scan-cac31c5358f3a1dcccc4d4ccbe2591f6fbae58949d23bb341429a6197e93a64b--codebase-memory-mcp
Durable public evidence: associations, exact scan set, per-extracted-object results and report links, evidence checksums.
Archive SHA-256 provenance (from the extraction manifest):
| Downloadable archive | SHA-256 provenance |
|---|---|
codebase-memory-mcp-darwin-amd64.tar.gz |
bb6cb47aea9e50e2193cdd917d5dbafa63b7d9c1cbe74bab5ec9bf4faa67e295 |
codebase-memory-mcp-darwin-arm64.tar.gz |
fa3ee085485fd9c16d1c1bd8a102df518862dabd2d6a09e4c6d8dfb3cd2a7eb4 |
codebase-memory-mcp-linux-amd64-portable.tar.gz |
4ead161715fe959ef8a0e38946d63b1728d31e5fb1762a85dbb4b8c4abfbe481 |
codebase-memory-mcp-linux-amd64.tar.gz |
6e3bb7353be21407a78e67b5465e53e3afb1a4a213e7a561606900ac08dcfdd6 |
codebase-memory-mcp-linux-arm64-portable.tar.gz |
dcd6f31f4004c25e7945b355ae23a5a381acb2a620301bca93e1ddb159b672fd |
codebase-memory-mcp-linux-arm64.tar.gz |
b70148686cec55c31673fc0cebc0caf7f664f4ae29f7ba7f07b9617c2e5eaf85 |
codebase-memory-mcp-windows-amd64.zip |
8f08e5c5b480e625adf9d4560765a860d493a690df6ded5b94127283ec5b660a |
codebase-memory-mcp-windows-arm64.zip |
b77b81975021b879ca53cdd94c60b48cc5de102194b52117938a2187a42f89c6 |
v0.10.1
2026年08月11日
codebase-memory-mcp v0.10.1
A fast-follow fix release. If you use cbm through an MCP client — Claude Code, Codex, or any other spec-compliant client — upgrade now: in v0.10.0, the default output format of the core discovery tools rendered as an empty {} in exactly those clients. The graph was fine, the daemon was fine, the data was in the reply — but the part of the reply your client actually reads was empty. CLI usage was unaffected, which is precisely why this escaped the release gates.
Enormous thanks to @Navier8, whose #1522 reported this within hours of the v0.10.0 release — with a reproduction quality we wish every bug report had: same binary, same index, format-by-format call tables that localized the fault to the MCP response path before we had even read the code. The report named two bugs; the verification sweep it triggered found a third. All three are fixed here, and all three shipped in v0.10.0 sharing one failure shape: an empty result with a success status — the worst possible failure mode for an LLM client, which cannot distinguish "empty" from "nothing found" and silently draws wrong conclusions instead of seeing an error.
What was broken, and what changed
1. Tree-format tool replies rendered as {} in MCP clients (the default format — the primary path). A v0.10.0 wire-size optimization stopped duplicating tool payloads into structuredContent, leaving an empty object in its place — while every tool still declared a permissive outputSchema. The MCP spec tells clients: when a tool declares an output schema, structuredContent is the result. So search_graph, trace_path, query_graph, get_architecture, search_code, and detect_changes all returned {} to schema-honoring clients on every platform. The contract is now truthful end to end: no tool declares an outputSchema (tool output legitimately varies by the format parameter, so no static schema was ever honest), JSON-object payloads carry their parsed structuredContent, error envelopes keep their machine-readable structuredContent.error, and text-shaped payloads simply carry no structuredContent key — which also preserves the v0.10.0 win of never shipping the payload twice.
2. Seven or more pipelined requests killed the MCP server outright. Found while verifying the tool surface for this release: writing 7+ requests to the server in one burst — which is exactly what an agent issuing parallel tool calls does — overflowed an 8-frame input queue, and the overflow handler tore down the whole session: exit code 1, zero bytes of output, every already-computed response lost. A full queue is now backpressure: the reader simply stops accepting input until the worker catches up, and every request gets its answer. Verified with bursts of 7, 24, and 64.
3. config get answered every question with an empty string and exit 0. Unset keys printed nothing (while config list happily showed their defaults), and so did misspelled keys — a typo was indistinguishable from a correctly-read setting. config get now prints the stored value or the key's real runtime default, and unknown keys are an error (exit 1, with the known keys listed) on get, set, and reset alike.
Why the gates missed it — and what guards it now
The honest part: our own test for the structuredContent change pinned the broken behavior as correct, at both the unit level and in the shipped-binary smoke suite — a reminder that a test asserting the wrong contract is worse than no test. The release pipeline was green because the pipeline agreed with the bug. That class is now closed from three directions: the unit suite enumerates the entire tool table and binds all three envelope branches (absent / parsed object / error) for every registered tool, the frontend suite asserts that over-capacity pipelined input is answered without loss, and the smoke suite drives the real shipped binary the way a schema-honoring MCP client does — asserting a default-format reply can never render as {}, that no tool advertises an outputSchema, that a 24-deep pipelined burst gets 24 answers, and that config get returns real values. Every one of these was verified red against v0.10.0 before the fix and red again with the fix reverted.
Upgrade
- Binary installs: download below, or re-run
install.sh/install.ps1— re-running the installer is the update. - npm / PyPI: update through your package manager as usual.
- No index, cache, or config migration; v0.10.0 graphs are fully compatible.
Full changelog: v0.10.0...v0.10.1
Security Verification
VirusTotal completed 14 distinct extracted byte objects covering 32 exact extracted archive members.
The extraction manifest binds those associations to 8 downloadable archives by SHA-256 provenance. Downloadable .tar.gz/.zip release containers were not submitted to VirusTotal.
13 of 14 scanned objects returned 0 malicious and 0 suspicious verdicts with 58–67 decisive engine results (required minimum: 50).
1 object(s) carry a single Microsoft machine-learning detection (!ml), which this project treats as a known false positive and publishes rather than hides. Every other engine returned clean. See Antivirus False Positives for the evidence and for how to verify these artifacts yourself: objects/scan-3380cf3b868d749c63f564e7c6b81381a140942ec42253f785e158ab5144064f--codebase-memory-mcp
Durable public evidence: associations, exact scan set, per-extracted-object results and report links, evidence checksums.
Archive SHA-256 provenance (from the extraction manifest):
| Downloadable archive | SHA-256 provenance |
|---|---|
codebase-memory-mcp-darwin-amd64.tar.gz |
4948d92c5b5a906faee0fd11ffcbb8606a72004eece92f4b5a28e1294b306ab8 |
codebase-memory-mcp-darwin-arm64.tar.gz |
9b6350b4e51cecd49126dc1b145ce81ac0df7aea35d87d7e8fe987683ad6b641 |
codebase-memory-mcp-linux-amd64-portable.tar.gz |
97c6580a13d772d040e936584f3c5234586ab03f31a77354af8a763851a39a7f |
codebase-memory-mcp-linux-amd64.tar.gz |
57cb5e5945a5371e43db3f2184d5b08ddd679d65e285cb2c976e665eac2fb22f |
codebase-memory-mcp-linux-arm64-portable.tar.gz |
ad457b306fa5bf5a1b97d160eeb867260a7946018097222a622fb14e896c6c72 |
codebase-memory-mcp-linux-arm64.tar.gz |
5cb8a073c0e31956698a60ed4bd41c5398096ce3270b1438e7919d0cb06f558b |
codebase-memory-mcp-windows-amd64.zip |
b9169d35f7d10485c449a2a8d6900a999ef5184fb93ab89b449dbe6e8f8c72a1 |
codebase-memory-mcp-windows-arm64.zip |
9cedb3c4f01bf0846a712ff246ed1cd29d74ea7f7cda195d67dfaf7424f5a81b |
v0.10.0
2026年08月11日
codebase-memory-mcp v0.10.0
⚠️ Please use v0.10.1 instead. v0.10.0 shipped a critical MCP-client regression (#1522): the default output format of the core discovery tools rendered as an empty{}in spec-compliant MCP clients such as Claude Code. Everything below describes this release accurately — but the fix release supersedes it and is the one to install.
A milestone release since v0.9.0 — cbm's backend has been rearchitected around a shared coordination daemon, tool output was rebuilt into a compact tree format (~99% fewer output tokens on large results), the long-standing Windows memory ratchet is fixed and verified, and a new coverage system makes the graph's blind spots visible — introducing cbm's 15th tool, check_index_coverage, and a missed-code skeleton rendered right beside the code galaxy in the graph UI. Since the release candidate, the graph gained a new evidence-backed CALL_REFERENCE edge class, the Linux binaries' allocator override was found silently inactive and switched genuinely on, and every daemon/installer/Windows issue reported against the RC is fixed. Add three new community-contributed language surfaces, Swift cross-package resolution, a Nix extraction overhaul, and a broad community-driven correctness batch across the query engine, store, and extractors.
781 commits · 183 merged PRs · 42 contributors · 40+ community-reported issues fixed (v0.9.0…v0.10.0)
From release candidate to v0.10.0
This is the final release of what shipped as v0.9.1-rc.1 on 2026-07-30. It graduates as v0.10.0, not v0.9.1: a new backend execution model, a new tool, a new output format, and a new edge class are not a patch release, and the version number should say so. There will be no v0.9.1 final — this is it.
To everyone who ran the RC in their daily workflows: thank you — it worked exactly as hoped. The 1.5 weeks since the RC brought 335 commits across 76 PRs, and the most valuable of them started as your field reports. Daemon conflict handshakes that left a client hanging on "pending" (#1383), an installer that removed existing Claude Code hooks instead of adding its own (#1387), hook-augment silently emitting nothing (#1388), Windows install/uninstall refusing to stop active sessions (#1416), focus-stealing console windows from the detached daemon (#1427), a query response size that could kill the MCP server with no diagnostics (#1375), multi-key ORDER BY dropping LIMIT (#1334) — every one of these was reported against rc.1 by an RC user and is fixed in this release.
The coverage ask stands. The coverage system makes the gap between your code and the graph measurable — help us shrink it. Ask your agent to run check_index_coverage across your repositories (or click the missed skeleton's report-an-edge-case callout in the UI) and paste the resulting summary into a GitHub issue or discussion: miss classes, affected languages and file kinds, counts. That's aggregate numbers, not source code — everything stays on your machine unless you choose to paste it. Every real-world coverage summary points us at a concrete parser edge case, resolver gap, or language quirk we can fix — your repos are the test corpus we can't clone.
⚠️ Behavior changes
-
New edge class:
CALL_REFERENCE— andCALLSnow means exactly one thing. The graph now distinguishes invoking a callable from passing it as a value (app.register(pluginFn, opts)).CALLSis reserved exclusively for invocations;CALL_REFERENCEis emitted only when deterministic parser/LSP/registry evidence proves the exact callable value (occurrence-exact spans, lexical binding state, resolved symbol identity); ambiguous, shadowed, or dynamically rebound occurrences remainUSAGE. Queries that assumed every callable relationship was aCALLSedge should account for the new label — and gain a precision they didn't have before: aCALL_REFERENCEedge is proof, not a guess. -
One archive per platform — the UI is always included. The
ui-*/ non-UI variant split is gone: every platform now ships a single archive with the graph UI embedded, and the npm/PyPI packages include it by default (theCBM_VARIANT=uiopt-in from the RC is obsolete). Fewer artifacts, no wrong-variant downloads, one composition to verify.
Highlights
- The coordination daemon — a backend milestone. Until now every cbm session was an island: each MCP server, CLI call, and hook opened the store on its own and knew nothing about its neighbors. As of this release, all of them coordinate through a per-user daemon — build/version conflict handshakes, indexing admission control, and a shared graph UI. New
daemon start|stop|statusCLI; a warm daemon removes per-command startup cost — measured 40–58% faster CLI commands on an 8.5M-node graph (~2.6 s saved per call). Hardened by RC field reports: every rejection path now answers the client with a reason instead of leaving it waiting. Beyond the immediate wins, this is the foundation the next releases build on: centralized controls for multi-agent workflows and local-only reporting/observability infrastructure — everything stays on your machine; nothing is sent anywhere. - Evidence-backed call graphs. The new
CALL_REFERENCEedge class (see behavior changes) closes a long-standing precision gap: higher-order patterns — plugins registered by value, callbacks, handler tables — are now first-class graph relationships backed by deterministic evidence, whileCALLSstops absorbing guesses. Optionalinclude_evidenceoutput shows why an edge exists: the resolution provenance behind it. - Tree-format tool output — TOON-compact, prefix-grouped rows with exactly-once pagination, real
detect_changesimpact reporting, and graph cycle detection. Measured ~99% fewer output tokens vs v0.9.0 on large query results — and MCP responses no longer duplicate their payload instructuredContent, roughly doubling the usable response budget on top. - Windows memory ratchet fixed (#581) — and the Linux override switched on. The allocator override silently compiled out under MinGW, so the CRT kept freed pages committed and long-lived daemons ratcheted memory for months. mimalloc now genuinely owns Windows allocations, thread heaps release at thread exit, and an out-loud ownership audit makes any future regression announce itself at startup. Verified on release artifacts: a 10-minute pure-query soak that measured 53× committed-heap growth on v0.9.0-era builds now measures 1.06×. The same audit then caught the mirror image on Linux: shipped Linux binaries had never actually routed ordinary malloc through mimalloc — as of this release they do.
- One install, 43 coding agents — the installer automatically (or conditionally, where a client's documented markers are present) configures 43 agent and client surfaces — Claude Code, Codex CLI, Gemini CLI, Cursor, Zed, VS Code, Windsurf, Cline, Aider, Goose, Amp, Copilot CLI, Sourcegraph Cody, and 30 more — with pi and OpenCode extensions now generated straight from the tool registry. Experimental tool profiles (
--tool-profile=analysis|scout) expose restricted inspection surfaces for lightweight scout agents. And it no longer touches what isn't its own: existing hooks are preserved even when a script rewrite is refused. - Honest coverage — the missed graph — what did not make it into the graph is now first-class, headlined by
check_index_coverage— the 15th cbm tool: parse-coverage signals per file, deliberately-ignored files reported as a by-design class, coverage folded intoindex_statuswith notes onget_code_snippet, and a queryable miss graph persisted in the store. In the UI, the missed code renders as a skeleton beside the code galaxy — click-to-focus, with a report-an-edge-case callout. - New language surfaces — Perl LSP-tier semantic resolution, InterSystems IRIS ObjectScript support, and Mojo wiring — all three community-contributed — plus Swift cross-package IMPORTS resolved through
Package.swiftmanifests and a two-part Nix extraction overhaul (function-rooted files no longer drop their definitions; attrpaths modeled with scoped qualified names). - Query engine robustness —
coalesce()and multi-arg scalar functions in WHERE, correct var-length path semantics with an advertised depth clamp, DISTINCT-before-LIMIT, the full multi-keyORDER BYlist honored withLIMITintact, composite/escaped property returns, a wall-clock execution deadline for runaway queries, andtools/listpagination for non-paginating clients. - Store & artifact integrity — a four-report series from one community member exposed torn WAL-mode exports, swallowed
SQLITE_CORRUPT, stale WAL replay over fresh DBs, and invalid JSON on async edges; all four are fixed. Reindex publication is now atomic end to end — staged, verified, and quarantined on failure rather than replacing a good database with a broken one. - CI, completely wrapped up — local CI, PR CI, dry runs, and releases all execute the same canonical leg scripts, enforced by a venue-parity contract that turns any drift into a red build. Parallel per-suite test execution with a zero-loss gate, release-gating soak legs, per-run isolation, artifact attestation, and VirusTotal scanning of the real archive artifacts as a standard pre-release step.
What's changed
Coordination daemon & concurrency
This is the largest backend change since the project began: cbm's execution model moved from independent per-session processes to a coordinated per-user backend. Concurrent sessions coordinate through the daemon (daemon start|stop|status, permanent or session-managed) with authenticated IPC, build-fingerprint/version/cache-root conflict detection, physical-job admission control for parallel indexing, and the graph UI served from the daemon. Because a per-user daemon is a security boundary, its IPC was hardened accordingly — on Windows, pipe clients are validated by process identity rather than impersonation, private-namespace objects are owner-stamped with the token user's SID, and DACLs are strictly validated; on POSIX, socket identity is checked against inode-pinned records. Everything remains strictly local.
RC field reports then hardened the seams: a client whose binary image the daemon rejects now gets an answer instead of an indefinite "pending" (#1383); an oversized MCP reply produces a structured error instead of process death (#1375); hooks surface daemon build conflicts to their caller instead of emitting nothing (#1388); and the admission path that accepts a mismatched build now names its cohort in diagnostics. On macOS, subprocesses spawn via posix_spawn instead of fork+exec.
Preparation for worktree-native multi-agent workflows. The daemon is also the groundwork for how multi-agent setups actually run today: one agent per git worktree. Right now each worktree has to be indexed as if it were a separate repository. With the centralized daemon layer in place, upcoming releases will rework exactly this: worktrees become children of the main repository's graph, sharing everything unchanged and indexing only what differs — making the agent-per-worktree pattern dramatically cheaper in time and disk.
Graph semantics & reindexing
The CALL_REFERENCE work (see behavior changes) runs through the whole stack: extraction, LSP resolution, sequential and parallel publication, persistence, MCP/UI counts, and documentation, with occurrence-exact matching so an LSP result can never promote a different same-named usage, and fail-closed handling for lexical shadowing and ambiguity. Alongside it, warm re-indexing moved to a delta-merge pipeline — unchanged data is copied forward and only the changed slice is patched and atomically swapped in, replacing the previous rebuild-and-repair path. Warm re-index performance at very large scale is still work in progress (see known issues), but the structural rework this release ships is the foundation the remaining wins land on.
Tool output & MCP surface
TOON compact output across the query surface (~10× smaller) and then the tree format on top (prefix-grouped rows, exactly-once pagination cursors, real change-impact in detect_changes, SCC cycle detection) for a combined ~99% output-token reduction vs v0.9.0 on large results. MCP responses stop duplicating their entire payload in structuredContent (#1375). Optional include_evidence exposes edge resolution provenance. detect_changes scopes seed detection to changed line ranges. ADR tools got a community correctness pass: reads stay nonblocking during reindex, misleading section writes are rejected, and coverage scope labels are preserved. index_repository resolves a repo_path from a known project name; an invalid project name can no longer send the daemon littering .corrupt.<hex> files (#1425); CLI activation refusals are attributed to the recorded safety check (#1416); and --help derives its tool list from the MCP registry, so it can't drift.
Windows & memory safety
The #581 chain: route ordinary Windows allocations through mimalloc (--wrap + static-CRT override), release thread heaps from a TLS detach callback, drop an SQLite page-cache slab that faulted on arm64, and audit allocator ownership per size class out loud at startup so "the tuning is decoration" can never happen silently again. New since the RC: the same audit philosophy caught that shipped Linux binaries had never actually routed malloc through mimalloc — the override is now genuinely active (#1360); memory diagnostics report OS-truth RSS on every platform; graph databases under non-ASCII cache paths open reliably on Windows (every maintenance-path SQLite open now routes through the same wide-path normalization as the primary open — previously the second index into e.g. a Cyrillic-named cache failed); stdio runs in binary mode under MCP on Windows; spawned child processes no longer flash console windows (#1427, plus a community fix for the general case); and the codebase compiles cleanly under MinGW GCC.
Windows now ships a single binary — and updates run from install.ps1
Windows previously shipped two executables: a small permanent launcher plus the product binary behind it. It now ships one binary, exactly like Linux and macOS.
The launcher existed so the product could update itself in place: on Windows a running executable cannot replace its own image, so a second resident binary had to perform the swap. That small helper reliably trips antivirus heuristics, and we could not keep it without shipping something that gets flagged — so the update step moved out of the running process and into install.ps1, which runs while codebase-memory-mcp is not running.
What changes for you: on Windows, codebase-memory-mcp update no longer updates in place. It prints the exact command to run instead:
powershell -ExecutionPolicy Bypass -File "<install-dir>\install.ps1"
install.ps1 is idempotent, so re-running it is the update: it stops the daemon, retires the running binary, installs the new one, and cleans up. Installing through npm or pip is unaffected — update with your package manager as before.
Languages & extraction accuracy
Perl LSP-tier semantic resolution; InterSystems ObjectScript language support; Mojo wiring. New since the RC: Swift cross-package IMPORTS resolved through Package.swift manifests; Nix files whose root expression is a function no longer drop every definition, and Nix attrpaths are modeled with leaf names and scoped QNs, with module-level bindings minted as Variables; decorators separated by a comment stay attached; same-named GoogleTest macros in one file get unique names instead of collapsing into a single node and dropping call edges (#1266); JS/TS template-literal URLs flatten to {} placeholders so route rendezvous works; Haskell array handling is alias-safe. Carried from the RC batch: native fetch() as HTTP_CALLS, CommonJS require() shadow fixes, aliased-import resolution, C/C++ #include-to-header edges and #ifdef-split recovery, PHP trait-recursion OOM fixes, Laravel and JAX-RS route composition, env-access CONFIGURES guards, File-node QN collision fixes, UTF-8-safe docstring truncation, and bounded GLR stack-merge recursion.
Query engine (Cypher)
coalesce() and multi-arg scalar functions in WHERE; var-length path semantics fixed with an advertised depth clamp; DISTINCT applied before RETURN limits; whole-value returns for composite and escaped properties; a wall-clock execution deadline; tools/list default pagination. New since the RC: the full multi-key ORDER BY list is parsed and applied with LIMIT kept intact (#1334); OPTIONAL MATCH can no longer fabricate a "no match" row after the row budget fills; and aggregation columns are classified by actual aggregate functions.
Store, artifacts & lifecycle
Atomic cache publish in the SQLite writer; snapshot exports made consistent and corrupt imports refused; destination WAL/SHM sidecars removed before installing a fresh DB; SQLITE_CORRUPT surfaced from row scans; WAL bounded via journal_size_limit; watcher reindexes dirty repos once per distinct state; relative repo_path canonicalization no longer corrupts and auto-deletes project DBs. New since the RC: reindex publication is atomic end to end — staged under private paths, verified, and quarantined on failure; WAL sealing before artifact swap; and label allowlists route through the canonical type-like set.
Agents, clients & installer
Agent integration coverage spans 43 automatic/conditional client surfaces, each changed only when its documented platform, marker, or existing config path is present. New since the RC — mostly straight from RC field reports: the installer never removes existing hooks when a script rewrite is refused, and --dry-run predicts exactly that outcome (#1387); generated Codex agent role TOMLs declare their transport; pi and OpenCode extensions are generated from the tool registry, so new tools appear in them automatically; indexing roots can be declared from the CLI, discovery prunes the cache directory, and compression is size-safe on huge inputs; Windows install/uninstall/doctor no longer fail on sessions they themselves can stop safely (#1416). Carried from the RC batch: the security-hardened Windows install transaction path, unknown-flag rejection with nearest-flag suggestions, honest --dry-run labeling, growing discovery walk stacks, FreeBSD memory detection, and the CBM_MEM_BUDGET_MB strict resolver.
CI & test infrastructure
Every venue — local CI, PR CI, the pre-release dry run, and the release pipeline — runs the same canonical leg scripts, with a venue-parity contract that turns any drift into a red build; parallel per-suite execution with a zero-loss gate (~2.5× faster suites); widened ThreadSanitizer coverage plus native ARM64 Windows UBSan; release-artifact attestation; release-gating soak legs. New since the RC: VirusTotal scanning of the exact archive artifacts is a standard final step of every dry run (with the flagging engine named, not just counted); local-CI runs are isolated per run id; and the release pipeline itself was debugged in public — skip_tests no longer silently skips smoke and soak, and script exec bits are pinned by contract.
Known issues
- Small Windows-only memory growth under sustained query load (~4.5 KB/query; neither macOS nor Linux shows it). This is not the #581 ratchet — ownership audits pass and the 53× failure mode is gone — but it is tracked with a follow-up investigation.
- Warm re-index at very large scale is still slower than v0.9.0-era, but improving. The delta-merge pipeline shipped in this release replaces the rebuild-and-repair path and already cut warm re-index times substantially in our A/B runs; closing the remaining gap to the v0.9.0-era cache-hit numbers is active work in progress. Cold indexing is at or faster than all baselines.
- Multi-threaded edge-count jitter: node counts are byte-stable across runs; edge totals can vary marginally run-to-run on resolution-heavy languages (worst case TypeScript). Tracked as an open determinism investigation.
- On macOS a startup line
mem.allocator.not_ownedmay appear in logs — on macOS this is the designed configuration (the allocator serves the bound sqlite/tree-sitter populations; ordinary malloc stays with the system allocator) and is harmless. On Windows and now Linux it is the tripwire for the #581 class: if you see it there, please file it.
Antivirus false positives — the full story, and a policy change
This release ships with 0 detections on every artifact — every binary archive, both install scripts, and the notices files, each scanned by 60+ engines. The verification table below is generated from those scans, and every VirusTotal link in it is durable: you can re-check the exact bytes yourself at any time.
We owe you the longer story, because it changed how our release gate works — and because it is one of the main reasons the stretch from RC to final took weeks rather than days.
What happened. During the RC and final stretch, some builds intermittently tripped one machine-learning heuristic — Microsoft's engine reporting Trojan:Script/Wacatac.B!ml — while every other engine reported clean. The !ml suffix marks a statistical model's score, not a signature match; the Script token is a generic bucket in that naming scheme and says nothing about script content. We treated it as a potential compromise first: full dissection of a release matrix built from one commit, reverse-engineering of the flagged artifacts, entropy analysis against packed-payload profiles. Nothing. The verdicts split across every axis at once — the static/dynamic link axis inverted between architectures, two macOS binaries with identical segment structure split clean/flagged, and sibling artifacts of one build landed in different variant buckets.
The decisive evidence: one identical file — same SHA-256 — went from 0/62 clean to flagged within about an hour, with no change on our side. And the experiment we ran in the other direction confirmed it: we removed every embedded shell script from the binary and moved the UI bundle and integration templates into separate verified files — the detection count did not drop, it simply moved between artifacts, so we reverted to the simpler single-binary composition. Part of the verdict lives in the scanner's own state, not in our bytes. No code change can guarantee non-detection, because the classifier is not judging only the code.
Why the policy changed. The RC notes said our gate was zero detections, permanently, with no tolerance — and we meant it when we wrote it. But a zero gate against a verdict that flips on identical bytes means our release timing is a function of Defender's cloud-model state, not of our code: each incident cost days of verifying bytes against a live Defender endpoint, filing a false-positive report with Microsoft, and waiting for a classifier revision — with development effectively hard-blocked behind it. That is not a sustainable way to ship, and pretending the absolute rule still held would have been the dishonest option. So we replaced it with a policy we can actually keep, documented and pinned by a contract test in SECURITY.md:
- A release may ship with at most one detection, and only when the engine is Microsoft and the label ends in
!ml— the exact, evidenced false-positive class described above. - Everything else still blocks, hard: two or more engines, any signature-based label, any other vendor, any "suspicious" verdict.
- A tolerated detection is published, never hidden — it appears in the verification table below with its VirusTotal link and a
TOLERATEDmarker, so you can always see exactly what shipped.
This release did not need the tolerance — it scans fully clean — but you should know the rule under which future releases ship.
This is endemic, not specific to us. The same !ml family repeatedly hits large, unsigned, native open-source binaries: llama.cpp (nothing found on reverse engineering), GitHub's own gh CLI, Microsoft's own Go toolchain, Anthropic's Claude Code, OpenAI's Codex, rust-lang, yt-dlp, Godot, PyInstaller, Tauri. A Microsoft engineer on the Go team put it plainly: "we can't exactly go fix something and solve all Go false positives." We deliberately do not reshape the binary to dodge the classifier — that is what malware does, and the same engineer reports it "increases scrutiny rather than avoiding it." We would rather be scannable and explain a false positive than be unreadable and score well.
What we fixed anyway. The investigation turned up real defects, corrected on their own merits and now asserted by release gates: every Linux binary we had ever shipped requested an executable stack (a missing .note.GNU-stack annotation); a test-only fork probe was compiled into production builds; the daemon spawned curl against the GitHub releases API to check for updates — removed, cbm now makes no network request of its own accord; unused extraction and SQLite loadable-extension machinery compiled out; temp-file and symlink-scan hardening; and build timestamps removed for reproducible bytes.
The durable fix is code signing, and it is underway — identity validation and platform enrollment are bureaucratic, not technical, so it lands on its own schedule. Signing will help on Windows; note that no code-signing scheme exists that AV engines honour for Linux ELF binaries, so the transparency above is permanent, not transitional. If you find anything that contradicts our assessment, we want to know: open an issue with the av-analysis label — a concrete finding changes our position. Until signing lands, the gate above, published hashes, build provenance attestation, and source you can always build yourself are the trust anchors for this project.
Thanks to our contributors
This release is substantially community work — features, fixes, and the reports that made the fixes possible. Merged contributions:
@halindrome (Perl LSP-tier resolution, #461/#459) · @isc-tdyar (ObjectScript support #467, extractor fixes #1060) · @aaiyer (Mojo wiring #939, coverage scope labels #1148) · @WarGloom (atomic cache publish #940, structured compact results #1043, alias-safe Haskell arrays #1232, similarity/worker fixes #1368/#1369) · @SEPURI-SAI-KRISHNA (Cypher & store robustness #1173/#1175/#1176/#1177) · @sini (Nix extraction overhaul #1304/#1305) · @gsdali (Swift cross-package IMPORTS #1222) · @AmirF194 (Cypher aggregation classification #1221, project-name resolution #1258) · @harshitaajoshi (Cypher coalesce() #991, UTF-8 docstring boundary #1094, GoogleTest node collisions #1329) · @CharlesQueiroz (JAX-RS route composition #1007, template-literal URL flattening #1008) · @JhohanBustamante (Cypher composite properties #1098, decorator extraction #1097) · @SS-42 (nonblocking ADR reads #892) · @M-Marbouh (ADR write validation #1141) · @lishixiang0705 (detect_changes scoping #1372) · @agn-7 (shared-skills improvements #1373) · @dergachoff (Codex role TOML transport #1408) · @ahmadgamal15-art (Windows binary-mode stdio #1424) · @mlandolfi90 (hidden child-process consoles #1448) · @Oxygen56 (MinGW GCC compatibility #1226) · @lukiod (UI Accept-Language ranking #1313) · @dpersek (TSan CI target #890, tools/list pagination #980) · @blankanswer (C++ line remap #949, CLI docs/tests #947/#948) · @pcristin (Cypher DISTINCT #906) · @muba00 (PHP trait OOM #920) · @apappas1129 (native fetch() HTTP_CALLS #927) · @yangsec888 (memory-budget resolver #954) · @Cosm1cAC (Windows UTF-8 temp files #956) · @LA-10 (aliased import resolution #979) · @Dhruvy0804 (CONFIGURES guards #982) · @Sinjan-Debnath (C/C++ include resolution #983) · @ShiroKSH (ADR clear on delete #985) · @spde (incremental File naming #995) · @metehanulusoy (shadow-row project resolution #1046) · @tmonestudio (test cache isolation #1067) · @PR9000 (FreeBSD memory detection #1093) · @alex-plosceac (.gitattributes ordering #916) · @CooperSheroy (docs #605) · @gandie (docs #1131)
And the reporters whose precise, reproducible issues drove this release's correctness work — including everyone who took the RC ask seriously and ran it in anger:
@dergachoff (#1387, #1388, #1425 — three rc.1 reports, each now fixed) · @artaommahe (#1360, #1375) · @OmerZeyveli (#1383) · @jayeshh-1 (#1416) · @0hirume (#1427) · @b1rdex (#1334) · @masesas (#1234) · @ysyecust (#1266) · @kriswill (the store-integrity series #895–#898) · @KasaharaDefries (#765) · @tmstack-io (#951) · @ericsonjulio1 (#773) · @Phantom-Star829 (#794) · @aitoroses (#797) · @rockywang101 (#856) · @zihaozhaoo (#858) · @trinhan2106 (#871) · @rudi193-cmd (#874) · @bsahafian (#929) · @wymfly (#937) · @paolo-blocklabs (#952) · @ARZF (#958) · @Zeermil (#964) · @squall-cyber (#973) · @LA-10 (#988) · @meszkee (#996) · @KnifeOfLife (#1025) · @mostafnamazy (#1032) · @metehanulusoy (#1044) · @rehanazher (#1077) · @itayost (#1085) · @lg320531124 (#764)
…and the reporters of every other issue resolved in this cycle:
@ahundt · @asemraza · @Carnival-z · @castlemilk · @davidwwu · @ecosuper2025 · @edwardmhughes · @georgejieh · @holsee · @jimeng754-eng · @junk151516 · @kenvqin · @Leon-L-Li-RSP · @LiberiFatali · @materwelonDhruv · @mattepiu · @maxniu1 · @mentallabyrinth · @MrXuanZF · @niksh06 · @oscar6echo · @raksasaa · @regoravalaz · @saitrsh · @skbs-eng · @thunderstumpges-servicenow · @x1051445024 · @YugantM
…and everyone who discussed, re-tested, and nudged. Dependency updates via Dependabot.
Full changelog: v0.9.0...v0.10.0
Security Verification
VirusTotal completed 14 distinct extracted byte objects covering 32 exact extracted archive members.
The extraction manifest binds those associations to 8 downloadable archives by SHA-256 provenance. Downloadable .tar.gz/.zip release containers were not submitted to VirusTotal.
Every scanned object returned 0 malicious and 0 suspicious verdicts with 54–69 decisive engine results (required minimum: 50).
Microsoft returned a decisive clean verdict for all 8 executable objects.
Durable public evidence: associations, exact scan set, per-extracted-object results and report links, evidence checksums.
Archive SHA-256 provenance (from the extraction manifest):
| Downloadable archive | SHA-256 provenance |
|---|---|
codebase-memory-mcp-darwin-amd64.tar.gz |
1efcb2096a21e76a090a2c2c1b103fc705f0445f7107df7ca9bead4eab130f5d |
codebase-memory-mcp-darwin-arm64.tar.gz |
0e559ee1c1170a2925e72e6752d527ff66785ab17efb55efecb225c9a6babc21 |
codebase-memory-mcp-linux-amd64-portable.tar.gz |
3868eecb025c93b0018b2c48f00f2ba435557aaf2ac83edbfe613b7fca9a23b1 |
codebase-memory-mcp-linux-amd64.tar.gz |
ef5ca5c8ded02f700d90b04444bc0cb67d0e9508831ab44c1a4ede02f1fa189c |
codebase-memory-mcp-linux-arm64-portable.tar.gz |
a97089ec5ccf011e9fc936fa4648e728f10f8cde7ba3b3a81be8d9264d7df5b0 |
codebase-memory-mcp-linux-arm64.tar.gz |
d8215279e3e46a65b83e382e26ae87392649aa56f4360dbfba7b9453eddb6b35 |
codebase-memory-mcp-windows-amd64.zip |
f8a50d2baad94ea2c1ef8c045452a0ef35974e0b03e95e75fb42fea743ab86ef |
codebase-memory-mcp-windows-arm64.zip |
925abf7cb23d82130bd1c52eb7c4801f1aee4d824f28d03a69897b7d54dd0a54 |
v0.9.1-rc.1
2026年07月30日
codebase-memory-mcp v0.9.1-rc.1
A milestone release since v0.9.0 — cbm's backend has been rearchitected around a shared coordination daemon, tool output was rebuilt into a compact tree format (~99% fewer output tokens on large results), the long-standing Windows memory ratchet is fixed and verified, and a new coverage system makes the graph's blind spots visible — introducing cbm's 15th tool, check_index_coverage, and a missed-code skeleton rendered right beside the code galaxy in the graph UI. Add three new community-contributed language surfaces and a broad community-driven correctness batch across the query engine, store, and extractors.
438 commits · ~90 merged PRs · 25+ contributors · 30+ community-reported issues fixed (v0.9.0…v0.9.1-rc.1)
Why this one is a release candidate
This release changes cbm at a deeper level than a normal point release: the backend now runs through a coordination daemon (a fundamental shift in how every session, CLI call, and hook executes) and the memory/allocator layer was reworked on Windows. Both are extensively tested — the full CI gate, cross-platform soak legs, and benchmark verification all ran green on this exact build — but changes of this nature earn real-world mileage before a final tag.
Please run the RC in your day-to-day workflows and report anything that feels off — especially in multi-agent setups: several agents or editors hitting the same projects concurrently, long-running agent loops, parallel indexing, daemon start/stop cycles. Those concurrency paths are exactly what the new backend exists for, and field reports from them are the most valuable input for the final release. Nothing is too small to file.
A second ask: send us your coverage summaries. The new coverage system makes the gap between your code and the graph measurable — help us shrink it. Ask your agent to run check_index_coverage across your repositories (or click the missed skeleton's report-an-edge-case callout in the UI) and paste the resulting summary into a GitHub issue or discussion: miss classes, affected languages and file kinds, counts. That's aggregate numbers, not source code — everything stays on your machine unless you choose to paste it. Every real-world coverage summary points us at a concrete parser edge case, resolver gap, or language quirk we can fix, and each one fixed makes graph analysis more precise for everyone — your repos are the test corpus we can't clone. This is the single highest-leverage thing an RC user can send us.
And an apology on timing. We previewed this RC in discussion #1144 targeting mid-July, and it took clearly longer than we said. The reason is the nature of what's in it: the daemon was a genuine architecture shift, not a feature, and the memory work had to be verified rather than assumed — both had to be done right rather than quickly, and for a stretch they consumed nearly all capacity, which also slowed PR reviews and issue responses. A third, less visible reason: recurring antivirus false positives on release artifacts blocked several otherwise-green release runs in the final stretch. They never shipped — our gate requires zero detections — and this release scans clean; the full story is in Antivirus false positives on release binaries below. To everyone who contributed, reported, and waited (and nudged us — deservedly): thank you for the patience. The full explanation lives in that discussion thread.
Highlights
- The coordination daemon — a backend milestone. Until now every cbm session was an island: each MCP server, CLI call, and hook opened the store on its own and knew nothing about its neighbors. As of this release, all of them coordinate through a per-user daemon — build/version conflict handshakes, indexing admission control, and a shared graph UI. New
daemon start|stop|statusCLI; a warm daemon removes per-command startup cost — measured 40–58% faster CLI commands on an 8.5M-node graph (~2.6 s saved per call). Beyond the immediate wins, this is the foundation the next releases build on: centralized controls for multi-agent workflows (scheduling, budgets, priorities across concurrent agents) and local-only reporting/observability infrastructure — everything stays on your machine; nothing is sent anywhere. - Tree-format tool output — TOON-compact, prefix-grouped rows with exactly-once pagination, real
detect_changesimpact reporting, and graph cycle detection. Measured ~99% fewer output tokens vs v0.9.0 on large query results — the difference between a result that fits in an agent's context and one that doesn't. - Windows memory ratchet fixed (#581) — the allocator override silently compiled out under MinGW, so the CRT kept freed pages committed and long-lived daemons ratcheted memory for months. mimalloc now genuinely owns Windows allocations (link-time wrap + static-CRT override), thread heaps release at thread exit, and an out-loud ownership audit makes any future regression announce itself at startup. Verified on release artifacts: a 10-minute pure-query soak that measured 53× committed-heap growth on v0.9.0-era builds now measures 1.06×, cross-checked green on macOS/Linux/Windows.
- One install, 43 coding agents — the installer now automatically (or conditionally, where a client's documented markers are present) configures 43 agent and client surfaces — Claude Code, Codex CLI, Gemini CLI, Cursor, Zed, VS Code, Windsurf, Cline, Aider, Goose, Amp, Copilot CLI, Sourcegraph Cody, and 30 more — so cbm drops into whatever agent stack you already run. Alongside it, experimental tool profiles (
--tool-profile=analysis|scout) expose restricted inspection surfaces so lightweight scout agents get a smaller, cheaper tool set (the first slice of the evidence-tier work previewed in #1144). - Honest coverage — the missed graph — what did not make it into the graph is now first-class, headlined by
check_index_coverage— the 15th cbm tool and the first new tool since the core surface stabilized: parse-coverage signals per file, deliberately-ignored files reported as a by-design class, coverage folded intoindex_statuswith notes onget_code_snippet, and a queryable miss graph persisted in the store. In the UI, the missed code renders as a skeleton beside the code galaxy — click-to-focus to inspect exactly which files and regions the index couldn't fully parse, with a report-an-edge-case callout to send them our way. Agents (and you) can now see the boundary of what the graph knows — and every edge case you report makes it smaller. - New language surfaces — Perl LSP-tier semantic resolution, InterSystems IRIS ObjectScript support, and Mojo wiring — all three community-contributed.
- Query engine robustness —
coalesce()and multi-arg scalar functions in WHERE, correct var-length path semantics with an advertised depth clamp (no more fabricated 100-hop paths from a self-loop), DISTINCT-before-LIMIT, composite/escaped property returns, a wall-clock execution deadline for runaway queries, andtools/listpagination for non-paginating clients. - Store & artifact integrity — a four-report series from one community member exposed torn WAL-mode exports, swallowed
SQLITE_CORRUPT, stale WAL replay over fresh DBs, and invalid JSON on async edges; all four are fixed, plus atomic cache publish on the writer. - CI, completely wrapped up — the entire test infrastructure was unified: local CI, PR CI, dry runs, and releases now all execute the same canonical leg scripts, enforced by a venue-parity contract that turns any drift into a red build. Parallel per-suite test execution with a zero-loss gate, release-gating soak legs, and artifact attestation included. For users this means one thing: faster, more confident iteration — fixes and features can move from patch to verified release-shaped build much quicker from here on.
What's changed
Coordination daemon & concurrency
This is the largest backend change since the project began: cbm's execution model moved from independent per-session processes to a coordinated per-user backend. Concurrent sessions coordinate through the daemon (daemon start|stop|status, permanent or session-managed) with authenticated IPC, build-fingerprint/version/cache-root conflict detection, physical-job admission control for parallel indexing, and the graph UI served from the daemon. Because a per-user daemon is a security boundary, its IPC was hardened accordingly — on Windows, pipe clients are validated by process identity rather than impersonation (and the daemon reads a client's first frame before ever impersonating it), private-namespace objects are owner-stamped with the token user's SID, and DACLs are strictly validated; on POSIX, socket identity is checked against inode-pinned records. Everything remains strictly local. It is deliberately conservative in this RC — coordination, admission control, and lifecycle — but it is the substrate for centralized multi-agent controls and local-only observability going forward.
Preparation for worktree-native multi-agent workflows. The daemon is also the groundwork for how multi-agent setups actually run today: one agent per git worktree. Right now each worktree has to be indexed as if it were a separate repository — a full reindex of what is largely the same code. With the centralized daemon layer in place, upcoming releases will rework exactly this: worktrees become children of the main repository's graph, sharing everything unchanged and indexing only what differs — making the agent-per-worktree pattern dramatically cheaper in time and disk. This lands once the daemon has proven itself in the field, which is another reason your RC reports from worktree-heavy workflows matter. Release paths and verification were hardened after review, TSan-sized wait budgets keep the daemon test suite honest, and a second-index crash (TLS parsers destroyed at allocator-epoch boundaries) is fixed.
Tool output & MCP surface
TOON compact output across the query surface (~10× smaller) and then the tree format on top (prefix-grouped rows, exactly-once pagination cursors, real change-impact in detect_changes, SCC cycle detection) for a combined ~99% output-token reduction vs v0.9.0 on large results. Structured compact results for MCP clients; resources/prompts discovery answered with empty lists instead of -32601 errors; guaranteed valid UTF-8 in tree output and honest totals/complete counts after review polish. The coverage system makes the graph's boundary visible: the new check_index_coverage tool (cbm's 15th) answers "what does the index not know about this repository?" directly; parse-coverage signals per file, deliberately-ignored files reported as a by-design class, a persisted + queryable miss graph, coverage folded into index_status with a note on get_code_snippet, and the UI's missed skeleton with click-to-focus and a report-an-edge-case callout.
Windows & memory safety
The #581 chain: route ordinary Windows allocations through mimalloc (--wrap + static-CRT override — mimalloc's own override is _MSC_VER-gated and never applied under clang/MinGW), release thread heaps from a TLS detach callback, drop an SQLite page-cache slab that faulted on arm64, and audit allocator ownership per size class out loud at startup so "the tuning is decoration" can never happen silently again. Also: wide-path canonicalization (no ANSI _access/_fullpath), wide-safe graph-DB opens for non-ASCII cache paths, UTF-8 reads for search_code temp files, non-ASCII UserProfile dump-phase fix, a CJK repo_path crash regression fix, and Windows hook scripts installed in .cmd form.
Windows now ships a single binary — and updates run from install.ps1
Windows previously shipped two executables: a small permanent launcher plus the product binary behind it. It now ships one binary, exactly like Linux and macOS.
The launcher existed so the product could update itself in place: on Windows a running executable cannot replace its own image, so a second resident binary had to perform the swap. That small helper reliably trips antivirus heuristics, and we could not keep it without shipping something that gets flagged — so the update step moved out of the running process and into install.ps1, which runs while codebase-memory-mcp is not running.
What changes for you: on Windows, codebase-memory-mcp update no longer updates in place. It prints the exact command to run instead:
powershell -ExecutionPolicy Bypass -File "<install-dir>\install.ps1"
install.ps1 is idempotent, so re-running it is the update: it stops the daemon, retires the running binary, installs the new one, and cleans up. (If PowerShell refuses to run the script because it was downloaded from the internet, Unblock-File it first.) Installing through npm or pip is unaffected — update with your package manager as before.
Alongside the smaller install, every daemon start, CLI call, and hook fire now skips a process spawn, a named-pipe handshake, and an stdio relay.
Languages & extraction accuracy
Perl LSP-tier semantic resolution; InterSystems ObjectScript language support (plus identifier node types and a MinHash gate fix); Mojo wiring. Correctness: native fetch() classified as HTTP_CALLS; CommonJS require() bindings no longer shadow call resolution; aliased-import direct hits keep call suffixes (fixing a django-scale CALLS/TESTS edge regression); Python aliased-import call resolution; C/C++ #include resolved to header targets by stem; C/C++ definitions recovered from #ifdef-split brace ERROR regions and remapped to original lines; PHP self-using and alias-colliding trait recursion OOMs fixed (two independent reports); Laravel Route::prefix()->group() routes and facade-style routes minted with composed prefixes; JAX-RS class+method @Path composition; env-access CONFIGURES guards; File-node QNs keep full filenames so sibling files can't collide; .env.* sibling variants no longer collide on qualified name; JSX-via-alias CALLS edges restored in the parallel pipeline; docstring truncation snaps to UTF-8 codepoint boundaries; GLR stack-merge recursion bounded against stack overflow; in-body function-like macro calls no longer flagged as parse gaps.
Query engine (Cypher)
coalesce() and multi-arg scalar functions in WHERE; coalesce(var.prop, literal); var-length path semantics fixed (edge reuse + repeated node variables unified — a self-loop can no longer fabricate 100-hop paths) with an advertised depth clamp; DISTINCT applied before RETURN limits; whole-value returns for composite and escaped properties; a wall-clock execution deadline aborts runaway queries; tools/list default pagination.
Store, artifacts & lifecycle
Atomic cache publish in the SQLite writer; snapshot exports made consistent and corrupt imports refused; destination WAL/SHM sidecars removed before installing a fresh DB; SQLITE_CORRUPT surfaced from row scans instead of silently truncating results; WAL bounded via journal_size_limit with checkpoint-starvation warnings; valid JSON for sequential-path service edges and Route nodes; watcher reindexes dirty repos once per distinct state (no more write amplification); projects with ::missed shadow rows stay visible; project args resolve by unique name tail; relative repo_path canonicalization no longer corrupts and auto-deletes project DBs.
Agents, clients & installer
Agent integration coverage expanded to 43 automatic/conditional client surfaces, each changed only when its documented platform, marker, or existing config path is present — plus coverage-aware integrations and tiered-agent smoke coverage; manual/UI-boundary clients are documented rather than touched. The Windows install path was security-hardened end to end: install transactions staged under a per-user root with private security descriptors, token-user SID stamped as owner on staged files and private-namespace objects, DACL validation that accepts the OWNER RIGHTS ACE and standard/elevated profile ACLs, the first-touch antivirus scan window absorbed on staged-copy opens, MSYS-form profile paths resolved, installer filesystem races eliminated, and legacy Windows hook scripts migrated safely to .cmd form. Also: unknown tool flags rejected with a nearest-flag suggestion (no more silently ignored --max-depth); install --dry-run always states dry-run mode; Aider gets CLI-form instructions; hook-augment deadline raised and env-configurable with a breadcrumb when it fires; discovery grows its walk stack instead of failing very wide repositories; npm/PyPI packages gain the CBM_VARIANT=ui opt-in UI build; FreeBSD physical-memory detection via HW_PHYSMEM; CBM_MEM_BUDGET_MB strict parse/clamp resolver; issue labeler fixed ((?i) unsupported in JS RegExp).
CI & test infrastructure
This release wraps up a complete overhaul of how cbm is tested and shipped. Every venue — local CI on a developer machine, PR CI, the pre-release dry run, and the release pipeline itself — now runs the same canonical leg scripts, with a venue-parity contract (run as step zero of every test leg) that turns any inline harness logic or bypassed wrapper into a red build. On top of that foundation: parallel test execution with per-suite processes and a zero-loss gate contract (~2.5× faster suites, same gates); widened ThreadSanitizer coverage plus native ARM64 Windows UBSan; release-artifact attestation; deterministic QA gates; and the release-gating soak sequence (churn plus a query-leak leg that turns any RSS growth into a hard signal) running identically everywhere. Supply-chain hardening rode along: release artifacts are attested in the build workflow, SLSA provenance scope is documented, CI container base images and package downloads are pinned by hash, and the test VM setup no longer embeds credentials. This is infrastructure work users never see directly — but it is what should make future iterations noticeably quicker: a fix verified locally is now verified in the exact shape the release gate demands.
Known issues (RC)
- Small Windows-only memory growth under sustained query load (~4.5 KB/query; neither macOS nor Linux shows it). This is not the #581 ratchet — ownership audits pass and the 53× failure mode is gone — but it is tracked with a follow-up investigation before final.
- Warm re-index at very large scale is slower than v0.9.0-era (kernel-scale cache-hit ~17 s → ~33 s). Cold indexing is at or faster than all baselines. Tracked.
- Multi-threaded edge-count jitter: node counts are byte-stable across runs; edge totals can vary by ~0.01–1.3% run-to-run on resolution-heavy languages (worst case TypeScript). Tracked as an open determinism investigation.
- On macOS/Linux a startup line
mem.allocator.not_ownedmay appear in logs — on those platforms this is the designed configuration (the allocator serves the bound sqlite/tree-sitter populations; ordinary malloc stays with the system allocator) and is harmless. It is the Windows tripwire for the #581 class.
Antivirus false positives on release binaries
This release ships with 0 detections across all 20 artifacts — every binary, both install scripts, and the notices files, each scanned by 46–68 engines. That is the only condition under which we publish, and it is why this release took as long as it did.
During the final stretch, some builds intermittently tripped one machine-learning heuristic on VirusTotal — Microsoft's engine reporting Trojan:Script/Wacatac.B!ml — while the other 60+ engines reported clean. Those builds were never published. We are documenting the episode anyway, because the policy it exercised is permanent and you should know what it guarantees.
What it is. !ml verdicts are machine-learned heuristics, not signature matches — a statistical model scoring file features, with our large binaries (they embed 159 parser grammars and the graph UI) sitting close to its decision boundary. This is a well-known pattern that regularly hits Go, PyInstaller, Nim, and rclone releases. A close-to-home example: opencode, one of the most widely used open-source coding agents, shipped release after release while Defender's ML repeatedly flagged its artifacts with the very same Wacatac heuristic family (opencode#3415, opencode#7919) — with no compromise ever found — until they closed the class out by code-signing their releases (opencode#12897), the same path we describe below. The pattern is also demonstrably unstable: across our own release attempts the flag appeared and disappeared between builds of near-identical code, on different platforms each time.
Our policy: zero detections. Permanently. Any detection, by any engine, on any artifact is a hard release blocker — including demonstrably false ones. This is not a bar we set for this release and it is not one we intend to relax later: if it does not scan 0/N, it does not ship. The Security Verification section below is generated only after that gate passes, so a published cbm release always means every artifact scanned clean.
We deliberately rejected the two shortcuts available to us. We did not add a tolerance for "just one engine" or "only heuristic verdicts" — a tolerance is how a real detection eventually walks through the gate. And we did not quietly rebuild until the badge looked clean, which would have been the easiest option and the least honest, since a rebuild only changes the hash the scanner is judging. When a false positive blocks us, we verify the exact bytes against an up-to-date Microsoft Defender endpoint (the shipping product, current signatures, real-time protection on), submit a false-positive report to Microsoft for those hashes, and wait. That costs days per incident. We accept that cost, and this release paid it.
Why chasing it from the build side did not work. This was one of the major reasons the release slipped. We spent several cycles attacking it as if it were a code problem — fully stripping symbol tables, removing the in-binary downloader and self-update machinery, eliminating the Windows launcher stub, rebuilding with different metadata — and each change appeared to work until a later build flipped the verdict again. The decisive evidence arrived late: one identical file, same SHA-256, went from 0/62 clean to flagged within about an hour. No source change can explain that, which means part of the verdict lives in the scanner's own state rather than in our bytes. We should have concluded earlier that code signing is the durable fix and that an ML decision boundary is not something you engineer your way around. Signing is more bureaucratic than technical (identity validation, certificates, platform enrollment); it is underway and realistically a few more weeks out.
What we did fix, because it deserved fixing anyway. The investigation turned up real defects, and those we corrected on their own merits rather than as antivirus appeasement — each is now asserted against the shipped binary by a release gate, so it cannot silently regress:
- Every Linux binary we had ever shipped requested an executable stack. One assembly file in the build was missing a
.note.GNU-stackannotation, which makes the linker assume the worst for the entire link. - A test-only probe that forks a
SIGTERM-ignoring child was compiled into production builds. It is now compiled out, and test seams are opt-in so forgetting the flag yields a clean binary rather than a leaky one. - The daemon spawned
curlagainst the GitHub releases API on the first eligible session of every run, purely to report that a newer version existed. Removed: cbm now makes no network request of its own accord, and the archives carry no download URLs at all. - An in-memory archive extractor with no production caller, and SQLite's loadable-extension machinery we never use, are compiled out rather than merely unreachable.
- Temp-file and repository-scan hardening: private exclusive creation, and symlinked directories can no longer lead a scan outside the project root.
- Build timestamps removed, so identical source can produce identical bytes.
Until signing lands, the zero-detection gate above, published hashes, build provenance attestation, and source you can always build yourself are the trust anchors for this project.
Thanks to our contributors
This release is substantially community work — features, fixes, and the reports that made the fixes possible. Merged contributions:
@halindrome (Perl LSP-tier resolution, #461/#459) · @isc-tdyar (ObjectScript support #467, extractor fixes #1060) · @aaiyer (Mojo wiring #939) · @WarGloom (atomic cache publish #940, structured compact results #1043) · @harshitaajoshi (Cypher coalesce() #991, UTF-8 docstring boundary #1094) · @dpersek (TSan CI target #890, tools/list pagination #980) · @blankanswer (C++ line remap #949, CLI docs/tests #947/#948) · @pcristin (Cypher DISTINCT #906) · @muba00 (PHP trait OOM #920) · @apappas1129 (native fetch() HTTP_CALLS #927) · @yangsec888 (memory-budget resolver #954) · @Cosm1cAC (Windows UTF-8 temp files #956) · @LA-10 (aliased import resolution #979) · @Dhruvy0804 (CONFIGURES guards #982) · @Sinjan-Debnath (C/C++ include resolution #983) · @ShiroKSH (ADR clear on delete #985) · @spde (incremental File naming #995) · @CharlesQueiroz (JAX-RS route composition #1007) · @metehanulusoy (shadow-row project resolution #1046) · @tmonestudio (test cache isolation #1067) · @PR9000 (FreeBSD memory detection #1093) · @JhohanBustamante (Cypher composite properties #1098) · @alex-plosceac (.gitattributes ordering #916)
And the reporters whose precise, reproducible issues drove this release's correctness work:
@kriswill (the store-integrity series #895–#898) · @KasaharaDefries (#765) · @tmstack-io (#951) · @ericsonjulio1 (#773) · @Phantom-Star829 (#794) · @aitoroses (#797) · @rockywang101 (#856) · @zihaozhaoo (#858) · @trinhan2106 (#871) · @rudi193-cmd (#874) · @bsahafian (#929) · @wymfly (#937) · @paolo-blocklabs (#952) · @ARZF (#958) · @Zeermil (#964) · @squall-cyber (#973) · @LA-10 (#988) · @meszkee (#996) · @KnifeOfLife (#1025) · @mostafnamazy (#1032) · @metehanulusoy (#1044) · @rehanazher (#1077) · @itayost (#1085) · @lg320531124 (#764)
…and everyone else who filed, discussed, and re-tested. Dependency updates via Dependabot.
Full changelog: v0.9.0...v0.9.1-rc.1
Security Verification
All release binaries scanned with 70+ antivirus engines — 0 detections.
| Binary | SHA-256 | VirusTotal |
|---|---|---|
darwin-amd64 |
661bd7320b4b3ec9b681... |
0 detections ✅ |
darwin-arm64 |
e4fe1cf4f764308f19a6... |
0 detections ✅ |
linux-amd64 |
2fd0687c69d8b47d63d0... |
0 detections ✅ |
linux-amd64-portable |
744395fa9623dd3368cf... |
0 detections ✅ |
linux-arm64 |
c1cd4eb660be91c99fcc... |
0 detections ✅ |
linux-arm64-portable |
0e3e7b2499158314c4cc... |
0 detections ✅ |
ui-darwin-amd64 |
c2095fdc559d19c7aad5... |
0 detections ✅ |
ui-darwin-arm64 |
9f2c23c75ed64b01b852... |
0 detections ✅ |
ui-linux-amd64 |
82667bbfc72a46b8200d... |
0 detections ✅ |
ui-linux-amd64-portable |
34c1fbf018f741bda2ea... |
0 detections ✅ |
ui-linux-arm64 |
7346f618a46cb766fbd1... |
0 detections ✅ |
ui-linux-arm64-portable |
a6e84602845ed38f7e2d... |
0 detections ✅ |
ui-windows-amd64 |
b8bb796a1327956a5aaa... |
0 detections ✅ |
ui-windows-arm64 |
391e34ca40d594b47f48... |
0 detections ✅ |
windows-amd64 |
7d82ca2faedce72113b1... |
0 detections ✅ |
windows-arm64 |
af0e9c551e9d418273b7... |
0 detections ✅ |