Releases: 11com7/code-review-graph
Release list
2.3.6+11com7.7
Added
pruneCLI command (11com7): removes registry entries whose paths no longer exist on disk (code-review-graph prune). Supports--dry-runto preview stale entries without touching the registry file. Useful after deleting or moving repositories that were registered viacode-review-graph register.CRG_REGISTRYenvironment variable (11com7): overrides the default registry path (~/.code-review-graph/registry.json). Intended for cross-project setups where a secondary project (e.g. a documentation repo) needs to point at the registry of a different project's graph data — without touching the global default. Also used for test isolation as a side effect.
Fixed
- Test isolation:
TestDataDirRegistrywrote into real user registry (11com7): three tests intests/test_incremental.pycalledRegistry()without a path, causing every pytest run to append temp-directory entries to~/.code-review-graph/registry.json. After 31 runs this accumulated 93 stale entries, makingcode-review-graph reposandlist_repos_tooloutput unreliable. Fixed by settingCRG_REGISTRYviamonkeypatchin the affected tests.
2.3.6+11com7.6
Added
--require-repo-rootflag forserve(11com7): prevents silent empty-graph startup when the server is meant to index an external project (e.g. a documentation repo pointing at a source repo viaCRG_REPO_ROOT). Without this flag, a missing env var causes SQLite to silently create a blank database in the current directory — all graph tools return empty results with no indication of misconfiguration. With the flag set, the server exits immediately with a clear message showing how to setCRG_REPO_ROOTon PowerShell and bash/zsh.
2.3.6+11com7.5
Changed
- Multilingual default embedding model (11com7): the local provider now defaults to
paraphrase-multilingual-MiniLM-L12-v2instead of upstream's English-onlyall-MiniLM-L6-v2. Our codebases carry German docstrings, comments, and identifiers, so semantic search needs a multilingual embedding space out of the box. Databases embedded with the old default will report anembedding_mismatchand need a one-timecode-review-graph embedre-run.
Added
.mcp.jsonenv resolution for CLI runs (11com7):code-review-graph embed(and a manualservestart) now adoptCRG_*entries from theenvblock of the project's.mcp.json(mcpServers["code-review-graph"].env) viaos.environ.setdefault. Previously only MCP clients applied that block, so a manualembedrun silently fell back to the default model and produced vectors that mismatched what the server queries against. Precedence:--modelflag > process environment >.mcp.jsonenv > built-in default.
2.3.6+11com7.4
Added
/releaseskill (11com7): project-local Claude Code skill that automates the full fork release workflow — preflight checks (uncommitted changes, ruff lint, mypy, targeted tests, GitHub workflow audit), CHANGELOG completeness check with commit-diff, automatic fork counter increment (+11com7.N+1), three-file version sync (pyproject.toml,__init__.py,uv.lock), annotated tag, push, and GitHub release viamake_releases.py. Supports--dry-runto preview all planned changes read-only without touching any file.- mypy invocation fix (11com7): mypy is not part of the dev extras and not installed in the local venv —
uv run mypyfailed with "program not found". Fixed by usinguv run --with mypy --with types-networkx mypy, which matches how CI installs it (pip install mypy types-networkx) without requiring a permanent dev-dependency entry.
2.3.6+11com7.3
Added
- Enriched embedding text per node (11com7): natural-language semantic search ranked nodes poorly because only identifier metadata was embedded —
"Vortrag Stornierung Referent"rankedVortragsberichtFragebogenfar above the semantically correctStorniertAbgesagtHandler. New_node_to_embedding_text()wraps the upstream_node_to_text()(kept byte-identical for clean future merges) and appends discriminating context: decorators/attributes fromnode.extra, identifier-split trailing path segments, and a compact source excerpt of the definition (declaration line incl.extends, salient body strings; 12 lines / 400 chars cap — the MiniLM models truncate at ~128 tokens anyway). Measured with the production multilingual model on the real classes: correct handler 0.263 → 0.413, related guard 0.136 → 0.262, false positive 0.615 → 0.454. Thetext_hashcomparison re-embeds all nodes automatically on the nextcode-review-graph embedrun.
Fixed
_split_identifiernow splits acronym boundaries (11com7):HTTPServer→HTTP Server,XMLHttpRequest→XML Http Request. Previously only lowercase→uppercase transitions were split, so acronym-prefixed names stayed single tokens.semantic_search_nodeslabels its scores (11com7): the response now carriesscore_type: "rrf"(or"keyword"for the fallback path). RRF scores are rank-based —1/(60+rank+1), so even a perfect top hit caps at ~0.033 — and were repeatedly misread as poor cosine similarities.
2.3.6+11com7.2
Fixed
- Windows:
embed_graphtool hang reintroduced by upstream's provider availability check (11com7): upstream 2.3.4 (42dd2c9) added_check_available()toget_provider(), which does a realimport sentence_transformersto report unavailable local embeddings. On the MCP tool-call path this import runs on an executor thread and pulls in the torch/numpy C extensions — the exact Windows DLL Loader Lock deadlock the fork'sembed_workerarchitecture exists to avoid. Reproduced 2/2 HANG with the MCP harness after the v2.3.6 merge; py-spy showed theasyncio_0thread frozen innumpy._core.multiarrayunder_check_available. Fixed by switching the check toimportlib.util.find_spec()(pure filesystem lookup, no import side effects). Verified 3/3 OK: handshake ~1 s, embed 5–20 s, vector search immediately afterwards.
2.3.6+11com7.1
Changed
- Merged upstream v2.3.6 (11com7): rebases the fork onto upstream releases 2.3.4–2.3.6 (51 commits). Notable conflict resolutions:
prewarm_local_embeddings()(upstream's Windows startup fix) is now worker-aware: on Windows it delegates to the fork'sembed_workersubprocess (loader-lock-free, returns immediately) instead of loading the model in-process; on other platforms it pre-loads into the shared thread-safe model cache. Upstream's lock-free_MODEL_CACHEwas dropped in favour of the fork's_local_model_cache+ lock.- The fork's per-process
GraphStorecache intools/_common.pywas removed in favour of upstream's caller-owns-and-closes lifecycle (upstream tools now close stores intry/finally; a shared cached store would be closed under concurrent callers). - The duplicate
embedCLI subcommand (added independently by both sides) keeps the fork's implementation (--batch-size, chunked commits, progress output). - Upstream's Rust attribute detection was folded into the fork's
_extract_annotation_list()helper. query_graphtarget resolution combines upstream'sfile_summarypath handling and cross-file caller fixes with the fork's non-test tie-break for ambiguous targets.
2.3.3+11com7.6
Added
code-review-graph embedCLI command (11com7): computes (or re-computes) vector embeddings outside the MCP request path. Re-embedding a large repo — e.g. after switchingCRG_EMBEDDING_MODEL— can take many minutes and previously had to go through theembed_graphMCP tool, where client timeouts cut it off. The command embeds in chunks (default 500 nodes,--batch-size) with progress output and per-chunk commits, so an interrupted run resumes where it left off. Supports--model,--provider,--repoand--data-dir.
Fixed
-
semantic_search_nodesno longer mislabels results as "hybrid" (11com7): thesearch_modefield always claimed"hybrid"for non-empty results, even when the vector path never ran (no embeddings, model still loading, provider mismatch) and results were FTS5/keyword-only.hybrid_search()now reports which paths actually contributed via a newdiagnosticsout-parameter, and the tool surfaces an honest mode:hybrid,fts,vector, orkeyword. -
Provider/model mismatch is reported instead of silently degrading (11com7): when stored embeddings belong to a different provider or model than the active one (e.g. embeddings built with
all-MiniLM-L6-v2, thenCRG_EMBEDDING_MODELswitched to a multilingual model), the provider filter matched 0 vectors and semantic search silently fell back to keyword search — while still labeled "hybrid". The search response now includes awarningexplaining the mismatch and how to re-embed (embed_graphtool orcode-review-graph embed). As a bonus the mismatch check runs before the query is embedded, so it no longer triggers a pointless model load just to find 0 matching vectors. -
Windows: MCP server marked as failed on cold start (11com7):
main()calledimport sentence_transformerson the main thread just to check whether the package is installed. On Windows this pulls in PyTorch and all its DLLs synchronously — typically 10–30 s — which blocks the stdio handshake long enough for MCP clients (e.g. Claude Code) to time out and mark the server as failed before any tools are registered. Fixed by replacing the bare import withimportlib.util.find_spec('sentence_transformers'), a pure file-system lookup that takes < 1 ms and has no import side effects. The daemon thread that actually loads the model is unchanged. See: tirth8205#46, tirth8205#136 -
Windows: local embeddings now run in a dedicated worker process — fixes all DLL Loader Lock deadlocks (11com7): importing the numpy/torch C extensions from any non-main thread of the MCP server process deadlocks on the Windows DLL Loader Lock: the importing thread holds the lock during
LoadLibrary, while torch's import machinery spawns helper threads whoseDllMain(DLL_THREAD_ATTACH)callbacks block on that same lock. Stack dumps show the pre-warm thread stuck innumpy._core.multiarray→create_moduleand the event loop stuck inThread.start(). This proved 100% reproducible (10/10 runs on Python 3.12 and 3.13) regardless of pre-warm strategy — every previous in-process mitigation (_prewarm_doneevent polling, model cache guards, thread-count env vars) only moved the deadlock around. Loading the same model on the main thread of a dedicated process works reliably, so that is now the architecture on Windows:- New
embed_worker.py: a small worker process that loads the sentence-transformers model on its main thread and serves embedding requests over a JSON-lines stdio protocol. It inherits the server's stderr for logging and exits when the server's pipe closes. LocalEmbeddingProvidertransparently routesembed/embed_query/dimensionthrough the worker on Windows (override withCRG_EMBED_WORKER=0/1). One worker per model name, requests serialized, automatic one-shot restart if the worker dies mid-session.EmbeddingStore.search()scores vectors in pure Python on Windows instead of importing numpy into the server process (a few thousand 384-dim vectors rank in well under a second).main()no longer starts a pre-warm thread on Windows; it just spawns the worker (process creation is loader-lock-free and instant), andembed_graph_toolis a plainasyncio.to_threadcall again. The_prewarm_doneevent and the_local_model_cacheguard in_embedding_search()are gone — the first semantic search after startup now blocks a worker thread (not the event loop) until the model is ready instead of silently degrading to FTS5-only results.
Verified end-to-end with an MCP repro harness: 5/5 cold-start runs with handshake ~1 s,
embed_graph5–33 s, and genuine vector search results immediately afterwards — previously 10/10 runs hung indefinitely. See: tirth8205#46, tirth8205#136 - New
-
.mcp.json: pin the MCP server to the installedcode-review-graphexecutable (11com7): the previousuvx code-review-graph serveresolves "latest on PyPI" on every launch. The moment upstream published 2.3.4,uvxsilently stopped using the locally installed fork (2.3.3+11com7.x) and ran the upstream package without any of the Windows fixes — which is why the hang appeared to "come back" despite verified fixes. The command is nowcode-review-graph serve, which always runs whateveruv tool installput on PATH.
2.3.3+11com7.3
Fixed
- Windows:
semantic_search_nodes_tooldeadlock on first call after server start (11com7):
even with the process-level_local_model_cachein place, the very first call to
semantic_search_nodes_tool(orembed_graph_tool) after a fresh server start still
triggered a Windows DLL loader deadlock. Loading PyTorch / sentence-transformers native
extensions from aThreadPoolExecutorthread while the main thread is blocked inside the
asyncio event loop (WindowsSelectorEventLoopPolicy) causes a circular wait on the OS
loader lock. Fixed by pre-warming the embedding model on the main thread inmain(),
immediately afterasyncio.set_event_loop_policy()and beforemcp.run(). When
CRG_EMBEDDING_MODELis set and the platform is Windows,LocalEmbeddingProvider._get_model()
is called once to populate_local_model_cache; subsequentasyncio.to_threadcalls then
only do a dict lookup with no native DLL loading. No-op on Linux/macOS. See: tirth8205#46, tirth8205#136
2.3.3+11com7.5
Fixed
-
Windows:
semantic_search_nodes_toolstill freezes MCP server on default config (11com7): the pre-warm daemon thread introduced in 2.3.3+11com7.4 was gated behindif _prewarm_model and sys.platform == "win32", meaning it only fired whenCRG_EMBEDDING_MODELwas explicitly set. Users relying on the default model (all-MiniLM-L6-v2) received no pre-warm and still hit the DLL Loader Lock deadlock on the firstsemantic_search_nodes_toolcall. Fixed in two places:-
main.py: the pre-warm now runs on Windows wheneversentence-transformersis importable, regardless of whetherCRG_EMBEDDING_MODELis set. When the env var is absent,LOCAL_DEFAULT_MODEL(all-MiniLM-L6-v2) is used as the fallback. -
search.py:_embedding_search()now checks_local_model_cachebefore touching the embedding provider on Windows. If the model is not yet cached (pre-warm still in progress), the function returns[]immediately and the caller falls back to FTS5. This closes the remaining race window: even if the first MCP tool call arrives before the daemon thread finishes, the event loop does not deadlock — it simply returns FTS5-only results for that one call, and full embedding search resumes once the cache is populated. See: tirth8205#46, tirth8205#136
-