You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
C++ as a supported language. New Language::Cpp variant, tree-sitter-cpp dependency, cpp.scm symbol query, and cpp_refs.scm reference query. Symbol coverage: free functions, in-class methods (declared and defined), out-of-line method definitions (void Foo::bar() {} resolved to Foo::bar), constructors / destructors, operator overloads (including reference-return forms like Foo& operator=), classes / structs / unions, plain enums + enum class, typedefs, using aliases, C++20 concepts, and #define macros. Template-wrapped definitions are captured automatically (the inner definition node still matches its pattern regardless of the template_declaration wrapper). Reference coverage: #include, bare / qualified / member / template-function calls, new T / new ns::T / new T<U> instantiations, base-class clauses (bare, qualified, and template-typed), and using declarations. Qualified names walk enclosing namespaces (including the C++17 namespace A::B form). Honest caveat: tree-sitter does not expand macros, so libraries that hide class/namespace behind project-specific macros (e.g. nlohmann/json's NLOHMANN_BASIC_JSON_TPL_DECLARATION and NLOHMANN_JSON_NAMESPACE_BEGIN) lose those symbols. The macro turns the surrounding declaration into an ERROR node and the structural query finds nothing. File source recognition: .cpp, .cc, .cxx, .c++, .cppm, .ixx, .hpp, .hh, .hxx, .h++, .tpp, .ipp are always C++. .h defaults to C; the discovery layer auto-flips it to C++ for any project that also contains an unambiguous C++ extension (no config knob). .c always stays C, even in a C++ project.
assess_risk_diff now reports import cycles that involve patch files. New cycles_touching_patch[] field on RiskDiffAssessment lists each strongly-connected component of size ≥2 in the file-level import graph where at least one member is in the patch's file list. Each entry carries members (sorted repo-relative paths), size, and max_churn_file (highest-churn member, used as a "most likely refactor site" heuristic; often the right place to break the cycle). When non-empty, a summary note like "2 import cycle(s) involve patch files (largest: 3 files)" lands in summary_notes for PR-description inclusion. Edges are derived from the existing refs table (kind = import / include / inheritance / trait_use resolving through symbols.name or symbols.qualified_name), so no new data collection. Uses an iterative Tarjan's SCC so deep include chains (php-src-scale) don't blow the stack. Honest caveat documented in the MCP tool description: we detect cycles the patch touches, not cycles the patch introduced. We don't have a pre-patch index to diff against, so pre-existing cycles involving patched files get reported too. Recommendations doc §1.1 (source: SocratiCode codebase_graph_circular, GitNexus cycle detection). 4 new tests cover two-file cycles, three-file diamonds, patches outside the cycle (not reported), and linear acyclic graphs.
Advisory file-lock coordination for concurrent codesage writers. index, git-index, and cleanup now take an exclusive flock on .codesage/indexing.lock at startup (stdlib File::try_lock, no new dep). If another writer already holds it, the second process exits 0 with another codesage indexer is running on <path> — skipping instead of colliding at the SQLite layer and emitting a scary Error: database is locked. Matters most for install-hooks-registered background hooks (post-commit, post-merge, post-checkout, post-rewrite), where rapid commits or a git pull during a manual index used to spam hook logs with SQLite contention errors. The concurrency-audit finding from recommendations doc §2.4 confirmed no data was at risk, but the UX looked like a bug; this eliminates the class of error. Re-run bench/concurrency-audit.py on any onboarded project to verify.
Gated hybrid BM25+semantic retrieval (recommendations doc §2.1 accepted). New FTS5 sidecar tables {chunk_table}_fts are created alongside each vec0 chunk table and populated in lockstep during insert_chunks. On search, a gate function (query_has_rare_literal) decides whether the query contains a distinctive literal: backticked identifiers, :: scope resolution, *.ext globs, dotted-identifier pairs like moduleref.create, or long (≥8 char) code-shaped tokens with <1% corpus DF. When the gate fires, BM25 results are fetched from the FTS5 sidecar and fused with the semantic ranking via weighted Reciprocal Rank Fusion (BM25 weighted 2x). The cross-encoder reranker is skipped in the gated path because it consistently demotes literal-match wins (the exact failure mode the original project_hybrid_bm25_rrf.md memo warned about). Rare-literal-shaped queries that miss from plain semantic retrieval (the memo's predicted failure mode on mechanism-heavy external corpora) are the specific target; everything else keeps the semantic-only path unchanged. Measured on canary corpora: ripgrep miss@10 20% → 13% (−7pp), nest miss@10 10% → 3% (−7pp), zero regressions.
find_coupling (MCP and CLI) now returns CouplingReport { coupled, file_indexed, file_commits, note? } instead of a bare Vec<CoChangeEntry>. Agents should read response.coupled for the ranked list. Motivation: retrospective session analysis showed 59% of find_coupling calls returned [] with no context, leaving the agent unable to tell apart the three causes: file never indexed, file has history but no co-change pair crosses the min-count=3 threshold, or path shape doesn't match the index. The new note field disambiguates each case with a concrete hint (run codesage git-index, verify path shape, or accept that this file changes in isolation). file_indexed and file_commits are always present, so even non-empty results carry enough context for the agent to judge a thin response.
assess_risk_diff now clusters per-file detail when a patch touches ≥5 files from one directory. The crowded directory's entries move from files[] into a new top-level clustered_directories[] field (top-3 files by score preserved in full detail, the rest listed by name as omitted_files). Rollup arrays (test_gap_files, wide_blast_files, fix_heavy_files, hotspot_files) still list every clustered file, so cross-referencing a cluster back to a specific concern still works. Small patches (≤4 files per directory) keep the original flat shape, so agent prompts written against the prior schema keep working without changes. Addresses recommendations doc §1.5 after retrospective session-log analysis measured this as the real cost center (p95 24 KB responses on this tool alone).
Stronger MCP tool descriptions for find_symbol, find_references, and search that explicitly say "prefer over Grep for code-symbol lookups" and call out the specific disambiguation failure modes (grep mixing definitions, comments, and string literals). Session-log analysis showed agents reach for Grep on patterns codesage answers in one call; sharper descriptions are the first intervention.
Drift instrumentation for the structural/semantic index. New structural_index_state table (migration 0002) records the HEAD SHA each successful codesage index run built against. codesage status and codesage doctor now show an index-drift indicator (fresh, N commits behind HEAD, not an ancestor of HEAD for rebases, or never indexed). The MCP server appends one JSON line to <project>/.codesage/drift.log the first time it resolves a project in a session, bounded to the last 10,000 lines. No auto-reindex behavior: this is pure measurement so we can characterize how often git-hook drift happens in real usage before deciding whether to build the content-hash backstop (recommendations doc §1.3).
Changed
bench/codesage-bench-runner scorecard output now includes a full metadata header: project HEAD SHA, repo size (file + LoC counts), corpus + case count + top-N, codesage version, embedding model + device, reranker, explicit baseline-for-comparison slot, and ISO run timestamp. Adds a "measured quantities" / "NOT measured" block so the honesty gap (no agent tool-call counts, no wall-clock-vs-grep) is visible in the output itself. New trailing "Quotable one-liner" copies all metadata into a single sentence suitable for external communication; the machine-parseable METRICS HTML comment gains the new fields for downstream tooling. Optional --baseline "ripgrep 14.1.0 (grep -rn)" flag (or CODESAGE_BENCH_BASELINE env) fills the baseline slot. Informational only, does not run a comparison tool. Template shape follows landscape-sweep item 1.6 in notes/20260424-reference-tool-recommendations.md.
Fixed
MCP tool params now accept integer fields encoded as JSON strings ("limit": "5" alongside "limit": 5). Strict Option<usize> deserialization was failing with invalid type: string "5", expected usize on ~10% of find_coupling calls; retrospective session-log analysis (bench/analyze-codesage-quality.py) found agents occasionally emit stringy numbers, which is standard LLM JSON behavior and not something the protocol should reject. Applies uniformly to limit, offset, and depth across CouplingParams, ImpactParams, ExportContextParams, and SearchParams. Genuinely non-numeric strings still error, and the error now quotes the offending value for diagnosability instead of a generic type-mismatch message.
codesage status no longer errors with no such table: on projects opened without a selected embedding model. Root cause: cmd_status opened the DB via Database::open() (which leaves chunk_table empty) and then called chunk_count(), which interpolated the empty table name into its SQL. Status now calls a new total_chunk_count() that sums across every vec0 chunk table in the DB, a more useful number anyway, since DBs that have been benchmarked across multiple models carry orphan chunk tables cleanup hasn't dropped. Zero vec tables returns 0 instead of failing.
Security
Bumped transitive openssl crate to 0.10.78 (from 0.10.77) to pick up fixes for CVE-2026-41676, CVE-2026-41677, CVE-2026-41678, CVE-2026-41681, and GHSA-hppc-g8h3-xhp3. Four of the five are high-severity memory-safety bugs in rust-openssl callbacks and AES key wrap. CodeSage pulls openssl transitively via native-tls (used by hf-hub for model downloads and ort-sys at build time), so the exposure is limited to TLS paths exercised during model fetches, but the bump is cheap and closes the Dependabot alerts.
Bumped transitive rustls-webpki to 0.103.13 (from 0.103.12) for RUSTSEC-2026-0104 (reachable panic in CRL parsing). Pulled via rustls → ureq / hyper-rustls / tokio-rustls on the same model-download and reqwest paths. Clears the scheduled cargo audit workflow failure.