Commit Graph

88 Commits

Author SHA1 Message Date
pcristin 1519f8619a feat(git): add worktree branch roots
Signed-off-by: pcristin <xxxokzxxx@protonmail.com>
2026-06-19 13:56:17 +02:00
Martin Vogel 630bd40a90 Replace the graph-UI HTTP server with a first-party implementation
The web server behind the graph UI is now a purpose-built in-house
module (src/ui/httpd.c): localhost-only listener, strict HTTP/1.1
parsing with fixed request caps, a per-connection receive deadline,
and Connection: close semantics. http_server.c keeps the routing and
handlers, rewritten against the new transport API; the public server
API and main.c are unchanged. The previously vendored third-party
server is removed entirely.

Comes with a new 28-test transport + routing suite (tests/test_httpd.c)
covering parsing edge cases (strict CRLF, Content-Length limits, raw
path matching, percent-decode rules) and live-socket behavior (CORS
policy, RPC dispatch, receive deadline, clean shutdown). The security
audit scripts now check the new file layout and treat any network call
in vendored code as a failure.
2026-06-12 02:17:25 +02:00
Martin Vogel eab4148de8 Upgrade vendored mimalloc 3.2.8->3.3.2 + platform-correct override; regen vendored checksums (#424)
mimalloc bumped to v3.3.2 (MI_MALLOC_VERSION 30302), which adds static-CRT malloc override on Windows. Makefile: define the real MI_MALLOC_OVERRIDE=1 ONLY on MinGW (enables the static-CRT override entry points — the generic Windows fix for #424); on Unix keep static-link-order override (defining MI_MALLOC_OVERRIDE there compiles alloc-override.c's forwarding defs and crashes macOS with 'mi_free: invalid pointer' since system libs keep the system allocator). The old -DMI_OVERRIDE=1 was never read by mimalloc source — kept as a prod marker. smoke-test.sh: hard-fail on any 'mimalloc: error' in stderr. Regenerated scripts/vendored-checksums.txt for the new tree (security-vendored.sh passes; no dangerous calls in vendored mimalloc).
2026-06-09 11:55:59 +02:00
Martin Vogel 0d2da15d4e Bind tree-sitter runtime to mimalloc in prod build to fix C++ indexing crash (#424)
Root cause (corrected from the report): not a tree-sitter null-subtree sentinel — 0xffffffffffffffff is a freed/heap-corrupted pointer. The vendored ts runtime allocates via its overridable ts_current_malloc/free; under the prod build's MI_OVERRIDE=1, the Windows static-MinGW link (--allow-multiple-definition) resolves ts_malloc to mimalloc but ts_free can resolve to the CRT free (or vice-versa), corrupting the heap freelist and crashing mid-parse on large templated C++ headers (scales with parse churn, not syntax). cbm_init now calls ts_set_allocator(mi_malloc, mi_calloc, mi_realloc, mi_free) so ts allocate+free go through one allocator on every platform. Guarded to CFLAGS_PROD (MI_OVERRIDE=1); the test build stays CRT+ASan to avoid an alloc/free mismatch there. Tests in test_stack_overflow.c: allocator-binding mechanism + large-templated-C++ extraction guard (the C++ gap noted in #424).
2026-06-09 09:20:15 +02:00
Martin Vogel 0518274783 Complete 159-grammar coverage: IDL/template/IaC/markup/remaining-code probes; +1 new class
Final 4 probe suites (grammar_probe_d/e/f/g, 250 cases) bring node/edge-creation coverage to all 159 grammars. Triage fixed 4 fixture mislabels (objc Method label, smithy $version order, json non-ignored filename, sql DDL-only Variables) and confirmed ~46 real reds. Findings map to known classes 2/3 (import/include-edge resolution missing for ~34 grammars: agda/bicep/elm/func/move/nasm/pony/purescript/qml/smali/tlaplus/vhdl/protobuf/thrift/capnp/wit/smithy/astro/nix/nickel/pkl/jsonnet/hyprlang/devicetree/cmake/makefile/meson/gn/just/bitbake/kconfig/html/css/scss), 16 (node-extraction incompleteness: wolfram multi-def/set_top, wit world funcs, bicep resource, just recipe), plus infra gaps (INFRA_MAPS hcl/k8s only from YAML topic bindings; DEPENDS_ON only from Helm Chart.yaml — go.mod/requirements produce none). NEW class 17 = file-index routing gap: .env/.gitignore/.gitattributes/.re have no EXT_TABLE/FILENAME_TABLE entry in discover/language.c so index_repository never indexes them (direct extraction works). Vue/Svelte/Astro component imports: extraction works but JS/TS path resolver doesn't handle those extensions. All 159 grammars now have reproduce-first guards.
2026-06-07 20:05:24 +02:00
Martin Vogel 21bc98ff92 Add grammar-only probe suites (~35 code grammars); +1 new root-cause class
Extends the node/edge-creation hunt to ~35 grammar-only code languages (129 graph/extraction cases, 73 green guards / 56 real RED reproductions; triage confirmed 0 fixture errors — all reds are genuine product gaps). Findings map to: class 2 import-EXTRACTION missing (cbm_extract_imports dispatch in extract_imports.c covers only ~38 langs + default:break, so ~25 grammar-only langs' configured *_import_types are never consumed → 0 imports); class 1 INHERITS missing (crystal/julia/pascal/powershell/squirrel/solidity); and NEW class 16 = per-grammar NODE-extraction incompleteness (class_types configured + grammar node present, but extract_defs.c emits 0 type nodes for D struct/class, F# record/union, Gleam type, Julia/Odin struct, Pascal record, PowerShell class, Hare struct, ReScript type, squirrel class, sway struct/abi, tcl namespace, wgsl struct). Affected-grammar matrix in the suite comments. Brings code-grammar coverage to ~61 of 66.
2026-06-07 19:13:01 +02:00
Martin Vogel 019a33683b Add convergence + matrix probe suites; triage confirms no new root-cause classes
Final hunt batches (convergence_probe 47, matrix_known_classes 44, matrix_new_constructs 63 = 154 graph-level cases) plus triage. Result: ZERO genuinely-new root-cause classes — every confirmed red maps to the already-known taxonomy (classes 1,4,5,6,7,12,13,14). Triage greened 5 fixture mis-builds: recursion self-calls (Go/Python/Rust/Java) produce 0 CALLS because self-loop edges are SUPPRESSED BY DESIGN (pass_calls.c/pass_parallel.c src->id==dst->id) — not a bug, fixtures now add a non-self caller; Ruby bare 'describe' parses as identifier not call (fixed to describe()). 26 reals kept as reproductions with // REAL BUG: root-cause comments. Confirms the bug hunt has converged: aggressive probing across new constructs + more languages surfaces only instances of the ~15 mapped classes.
2026-06-07 18:29:05 +02:00
Martin Vogel 0798748abf Add probe reproduction suites: LSP-pass resolution, node creation, edge-type creation
Reproduce-first probes (215 graph-level cases) hunting node/edge-creation + LSP-pass bugs across 26 languages + web frameworks. node_creation_probe: 80/81 green (node creation is solid; only Go method-on-struct under threshold). lsp_resolution_probe: 72/83 green — RED reproductions for the cross-LSP dispatch gap (Rust/Kotlin S1 cross-file call) and constructor/static/virtual/generic resolution edge cases (C/C++/TS/Java/C#/PHP S3-S7). edge_types_probe: 29/51 green — candidate RED reproductions for HANDLES (7 web frameworks), HTTP_CALLS (5 clients), WRITES (all 5 langs), Kotlin throws/raises, Go DEFINES_METHOD/OVERRIDE (these need real-bug-vs-fixture verification). Green cases retained as regression guards throughout. No unrelated suite regressed.
2026-06-07 13:36:15 +02:00
Martin Vogel e31dd24433 Add graph-level edge-creation reproduction suite (pipeline edge resolution)
Reproduce-first coverage of the pipeline step that turns extracted symbols/imports into resolved graph edges, across the 9 hybrid-LSP languages (91 graph-level cases). Live RED reproductions: IMPORTS edges are never created for Rust/Kotlin/Java/C#/PHP (all forms) despite working extraction (+ Python aliased/wildcard, TS re-export edge cases); cross-file CALLS do not resolve for Rust and Kotlin (lsp_cross pass processes 0 files for them) — missed by real-repo checks because same-file calls dominate; same-file IMPLEMENTS edges absent for Java/C# and Go implicit-interface satisfaction; same-file DECORATES absent for TS and C# attributes; cross-file USAGE absent for TS; cross-file INHERITS absent for TS/PHP/Kotlin/Python (extraction-propagated). Green guards retained for every working path (Go/Python/TS imports, cross-file CALLS for 7 langs, INHERITS for Java/C#/C++, etc.). 51 RED reproductions; no other suite affected.
2026-06-07 13:00:55 +02:00
Martin Vogel 857432b33d Add language-agnostic extraction reproduction suite (inheritance + imports)
Reproduce-first coverage of class-inheritance (base_classes) and import extraction across the 9 hybrid-LSP languages (~300 table-driven cases). Live RED reproductions: TS/TSX store the extends/implements keyword instead of the base type; PHP never populates base_classes and its use-imports parse the wrong node type; Kotlin does not parse the : supertype list; Python loses Generic[T] (paren/bracket leak); C++ drops qualified bases (std::vector) and leaks ':'; Rust misses several impl-Trait-for-T forms; C# import aliases (using F = X) and Python wildcard/__future__ imports are missed. C# inheritance and Go/TS/JS/Java/Kotlin/Rust import extraction pass as regression guards. Suite is RED until the extractors are fixed; 12 failing reproductions, no other suite affected.
2026-06-07 12:35:53 +02:00
Martin Vogel 7a43b38ef7 Add per-grammar regression suite across all 159 grammars
Index a fixture for every grammar through the full pipeline and assert graph-level invariants: golden node-label histograms, def/node breadth, CALLS and IMPORTS breadth, presence of all 26 pipeline edge types (structural, type-relationship, service/dataflow, and similarity), no-crash, and call resolution. Reproduced quality gaps (under-extraction, import-extraction, and CALLS-resolution) are kept as hard failures for the fix phase rather than skipped. Adds a real-repo scale tier (scale_contract.sh) and ignores local-only private/ scratch.
2026-06-05 21:53:25 +02:00
Martin Vogel 31f7438ce0 Add Rust hybrid LSP resolver
Hybrid tree-sitter + type-resolution call resolver for Rust, alongside the
Go/C/PHP/C#/Python/TypeScript/Java/Kotlin resolvers. Resolves method dispatch
on typed receivers, UFCS and associated-function calls, trait/impl methods,
generics, closures, the `?` operator, and iterator adapters; seeds the standard
library and well-known crates, parses Cargo.toml for dependency context, and
expands a curated set of derive/proc-macro generated methods.

Includes rust_cargo (manifest parsing), rust_proc_macros (derive expansion),
rust_rustdoc (doc extraction), a generated stdlib + crates seed, and a large
test suite (registered as suite rust_lsp). Wired into the per-file dispatch.
Field-access chains through Self-returning calls remain a documented coverage
gap. Full suite green (4666 tests).
2026-06-03 02:13:30 +02:00
Martin Vogel 5daed0bb01 Add per-language grammar extraction regression suite
Data-driven net (tests/test_grammar_regression.c) that extracts a minimal
sample per language and asserts a catastrophic-break floor (defs >= min) plus
expected definition names across 35 languages. Guards against silent
extraction breakage on future vendored-grammar refreshes — the failure mode
where a renamed/removed node type drops extraction to zero defs (as the
tree-sitter-kotlin name-field change did). Extend by appending a row to CASES.
2026-06-03 01:54:49 +02:00
Martin Vogel fd6c003dfc Add Kotlin hybrid LSP resolver
Hybrid tree-sitter + type-resolution call resolver for Kotlin, matching the
existing Go/C/PHP/C#/Python/TypeScript/Java resolvers. Resolves intra-file
and imported calls, method dispatch on typed receivers, extension functions,
operator conventions (plus/compareTo/contains/get/unary), scope functions and
trailing lambdas (it/this), smart-casts (is / when), super dispatch,
constructor-val properties, and stdlib defaults.

Targets the refreshed tree-sitter-kotlin grammar: discovers declarations via
simple_identifier/type_identifier, walks the statements wrapper, extracts
method names from navigation_suffix, descends call_suffix for trailing
lambdas, reads delegation_specifier for inheritance, class_parameter for
constructor fields, and import_list for imports.

Wires the resolver into the per-file dispatch and registers its test suite
(78 tests). Full suite green (4160 tests).
2026-06-03 01:33:25 +02:00
Martin Vogel d849aeaee7 Merge Java hybrid LSP
Integrates the type-aware Java semantic resolver from
worktree-adding-java-lsp-support (based on 673ac4e): java_lsp.c + generated
java.lang/util stdlib registry, wired into the per-file LSP pass, with 94
java_lsp tests + ~170 java_lsp_coverage tests.

Wiring conflicts (the shared files diverged 146 commits on main) resolved by
keeping main's existing LSP registrations (go/c/php/py/ts/cs) and adding the
Java entries in lsp_all.c, cbm.c (cbm_run_java_lsp dispatch), test_main.c and
Makefile.cbm.

Adapted to the current unity-build: renamed java_lsp.c's static helpers
node_text/return_type_of to java_node_text/java_return_type_of so they no
longer collide with ts_lsp.c inside the shared lsp_all.c translation unit.

4082 tests pass (incl. java_lsp + java_lsp_coverage); ASan/UBSan clean.
2026-06-02 19:06:02 +02:00
Martin Vogel a600e80cb1 feat(lsp): Java hybrid LSP resolver (type-aware calls, annotations, signatures)
Pure-C Java semantic resolver (java_lsp.c) + generated java.lang/util stdlib
registry, wired into the per-file LSP pass. Resolves direct/method/trait/
generic/lambda calls with confidence; conservative no-edge on unresolved.
Includes test_java_lsp.c + coverage suite.
2026-06-02 18:52:37 +02:00
Martin Vogel 12683b84a9 feat(graph): queryable computation-bottleneck metrics + indexing perf & fixes
Per-function complexity metadata is now stored on graph nodes and queryable,
alongside several indexing performance and correctness fixes developed and
validated together (3704 tests, ASan/UBSan clean).

Bottleneck metrics (query via query_graph):
- Tier A (in the extraction AST walk): cyclomatic (complexity), cognitive
  (nesting-weighted), loop_count, loop_depth (max nested-loop depth),
  param_count, max_access_depth.
- Tier B (new pre-dump pass, pass_complexity.c): transitive_loop_depth
  propagated along CALLS edges + a recursive flag (direct self-recursion and
  mutual-recursion cycles), plus the call-context signals linear_scan_in_loop,
  alloc_in_loop, recursion_in_loop and unguarded_recursion.
- query_graph and get_architecture tool descriptions document the metrics and
  the Leiden community clusters.

Cypher engine:
- node_prop exposes arbitrary persisted node properties to WHERE/RETURN.
- Fix projection aliasing: multi-property rows shared a single static buffer so
  every column returned the last value read; now per-column/rotating buffers.
- Fix a stack-use-after-scope in aggregate RETURN (caller-owned value buffers).

Indexing performance:
- Gate C/C++ #define Macro-node extraction to full mode (it is ~49% of nodes on
  the Linux kernel); moderate/fast skip it.
- Emit the complexity property block only for Function/Method nodes so the
  millions of Macro/Field/Variable/Class/Enum nodes no longer carry zeroed
  fields — large RAM reduction at scale.
- Classify node types via tree-sitter TSSymbol bitsets in cbm_kind_in_set
  instead of per-node strcmp scans (thread-local cache, strcmp fallback;
  behaviour-identical).
- Subsample frequent (Zipfian) tokens in the semantic co-occurrence finalize;
  ~14x faster finalize on the kernel, output unchanged.
- pass_lsp_cross: replace O(n^2) linear dedup with hash-set dedup.

Windows:
- Canonicalize drive-letter case during path normalization so "c:/repo" and
  "C:/repo" derive the same project key and cache file (#394/#227/#367).

Tests: extraction, pipeline and cypher regressions covering all of the above.
2026-06-02 18:50:55 +02:00
Martin Vogel c29e6d51f4 fix(hooks): replace blocking Claude PreToolUse gate with non-blocking augmenter
The previous PreToolUse hook gated Grep/Glob/Read/Search with 'exit 2'
on the first call per session, which broke Claude Code's
read-before-edit invariant (issue #362) and could deny tool calls under
upgrade/missing-binary failure modes.

Replace it with a structurally non-blocking augmenter:

- New 'codebase-memory-mcp hook-augment' subcommand reads the hook JSON
  from stdin and, for Grep/Glob, queries search_graph (in-process, no
  shell) and emits hookSpecificOutput.additionalContext. Every failure
  path (no project, short token, missing binary, slow query, timeout)
  exits 0 with no stdout — the hook physically cannot block a tool call.
- 300 ms SIGALRM/_exit(0) in-process deadline; 5 s settings.json timeout
  backstop. Output is written exactly once at the very end, so a
  mid-work timeout yields a clean no-op (never partial JSON).
- Matcher narrowed to 'Grep|Glob' (Read explicitly excluded) for Claude;
  Gemini matcher narrowed to 'google_search|grep_search' (excludes
  read_file) for the same reason.
- The installed shim is a thin wrapper that delegates to the binary;
  legacy filename 'cbm-code-discovery-gate' is kept so existing
  settings.json entries upgrade with zero migration. Installer refuses
  to embed binary paths containing a double quote (shim injection
  defense).
- Per-agent 'old matchers' lists let upsert/remove clean up historical
  matcher strings during upgrade.
- Smoke tests (8d/8e/8l) updated to assert the new behavior and
  regress-test against re-introducing Read in the matcher or 'exit 2'
  in the shim.
- Session reminder text updated: 'always Read a file before editing it'
  replaces the prior 'fall back to Read only for text content'.

(cherry picked from commit f72c8e68c4d91e52911a569a967ad782ce5472b2)
2026-05-19 23:46:46 +02:00
Martin Vogel 03e01dc616 feat(lsp): add C# / .NET hybrid LSP resolver
Add a C# language LSP resolver mirroring the existing Go/C/PHP/Python
modules: type evaluation, using-directive resolution, method lookup,
and a corpus-seeded .NET BCL stdlib data table.

- internal/cbm/lsp/cs_lsp.{c,h}: resolver implementation
- internal/cbm/lsp/generated/cs_stdlib_data.c: BCL type/method tables
- wire CBM_LANG_CSHARP dispatch in cbm.c and lsp_all.c
- register cs_lsp + cs_lsp_bench suites in the test harness
- tests/test_cs_lsp.c, tests/test_cs_lsp_bench.c

(cherry picked from commit 8f677c67a0c9d7829cd6ac27393de2d0aaaac534)
2026-05-19 22:47:53 +02:00
edwardmhughes 22153563cd fix(store): use PASSIVE checkpoint to avoid file-shrink under concurrent readers
cbm_store_checkpoint() invoked SQLITE_CHECKPOINT_TRUNCATE, the most
aggressive mode. When two cbm-mcp processes share a cache dir, one
process's TRUNCATE can shrink files while another has them mmap'd,
raising SIGBUS on macOS. PASSIVE never blocks readers and never
ftruncate()s either file; SQLite still autocheckpoints in PASSIVE
mode at 1000-page boundaries, so reclamation is unaffected for
single-process users.

Recommended by SQLite docs for shared databases:
https://www.sqlite.org/pragma.html#pragma_wal_checkpoint
2026-05-10 21:10:27 +02:00
edwardmhughes 093707cda2 feat(store): expose mmap_size via CBM_SQLITE_MMAP_SIZE env
Hard-coded `PRAGMA mmap_size = 67108864` in configure_pragmas() left
no path for users running multiple cbm-mcp instances against the
same cache to opt out of memory-mapped I/O. On macOS, when one
instance's checkpoint or reindex truncates the DB file under
another instance's live mmap, accessing the now-missing pages
raises SIGBUS, taking the process down.

Setting CBM_SQLITE_MMAP_SIZE=0 reverts to read()/pread() I/O,
which returns recoverable SQLITE_IOERR instead of crashing the
process.

- Default unchanged (67108864 / 64 MB). No behavior change for
  single-instance users.
- Malformed values (non-numeric, partial-numeric) fall back to
  the default rather than failing the store open.
- Negative values clamp to 0.
- New tests: tests/test_store_pragmas.c covers all five resolver
  paths plus an integration smoke that opens a file-backed store
  with mmap disabled.

Empirical evidence: 9 SIGBUS crash reports collected on macOS
arm64 v0.6.0 in a 14-hour window, all signature 'cluster_pagein
past EOF' with stacks bottoming in SQLite btree code under the
watcher thread's incremental-index pipeline.
2026-05-10 21:03:42 +02:00
Martin Vogel b2a71b80b9 feat(pipeline): wire cross-file LSP across all five hybrid resolvers
Per-file LSP (run from cbm_extract_file) only sees one file's defs, so
callees whose receiver type comes from an imported module stayed
unresolved — even though Go/C/Py/TS already had cross-file entry points
(cbm_run_X_lsp_cross). Those entry points existed but no pipeline pass
ever invoked them. PHP didn't even have a cross-file entry point.

This change lands the missing pieces:

1. cbm_run_php_lsp_cross + cbm_batch_php_lsp_cross — PHP cross-file
   resolver mirroring py_lsp_cross / ts_lsp_cross. Reuses PHPLSPContext,
   php_lsp_init, php_lsp_add_use, php_lsp_process_file. Handles PHP-
   specific labels (Class / Interface / Trait / Enum / Type) and uses
   def_module_qn so cross-file return types qualify against the def's
   own module rather than the importing module's.

2. pass_lsp_cross.c — new pipeline pass. For every cached file result,
   builds a project-wide CBMLSPDef[] (CBMDefinition → CBMLSPDef
   converter), per-file import map (from gbuf IMPORTS edges), then
   dispatches to the matching cbm_run_X_lsp_cross. Output strings get
   copied into the per-file arena (with O(N²) memory bug avoided by
   running each file inside a fresh scratch arena that gets destroyed
   after the LSP call — the LSP allocates its registry + stdlib +
   project defs in scratch, not in the caller's persistent arena).

3. Wired into both pipelines:
   - sequential: definitions → k8s → lsp_cross → calls → ...
   - parallel:   parallel_extract → registry_build → lsp_cross →
                 parallel_resolve → ...

4. Updated tests/test_parallel.c run_parallel helper to mirror the new
   parallel ordering so existing cross-file Python/PHP tests now exercise
   pass_lsp_cross.

Languages covered: Go, C/C++/CUDA, Python, TS/JS/JSX/TSX, PHP. PHP cross
file is new. The other four had functions but no pipeline integration
until this change.

Verified on a 1670-file FastAPI index: pass_lsp_cross processes 1050
Python files (525 non-LSP files skipped), collects 4070 project defs in
9.2s, and parallel_resolve subsequently reports lsp_overrides=568 — the
LSP-strategy attribution gain over per-file alone.

Per-LSP emit functions dedup by (caller_qn, callee_qn) within their own
run; pxc_append_results dedups again at append time so cross-file
entries duplicating per-file output get dropped instead of inflating
result->resolved_calls.
2026-05-10 17:35:01 +02:00
Martin Vogel d1143fbdce feat(lsp): TypeScript / JavaScript / JSX / TSX hybrid LSP resolver
Adds clean-room ts_lsp resolver covering single- and cross-file type
resolution for TS, JS, JSX, TSX. Mirrors the structure and entry-point
pattern of the existing Go and C/C++ LSP hybrids (single-file
cbm_run_ts_lsp + batch cbm_run_ts_lsp_cross), with TS-specific surface:

- Hash-table indexed type registry (FNV-1a, lazy via cbm_registry_finalize)
- Partial structural relater for NAMED / BUILTIN / LITERAL / TUPLE /
  UNION / INTERSECTION / FUNC / TEMPLATE / TYPE_PARAM / ALIAS
- Conditional types T extends U ? X : Y with union distribution
- 'infer X' constraint solver, keyof / typeof / T[K] type-position parsing
- Flow-sensitive narrowing (instanceof / typeof / discriminated unions),
  optional chaining, non-null assertion
- TS utility-type passthrough (Partial, Pick, ReturnType, Awaited, etc.)
- Function overload resolution by argument types
- Async iterables + 'for await ... of'
- Curated DOM stdlib subset + comprehensive base stdlib
- Mode flags: js_mode (.js/.jsx with JSDoc inference), jsx_mode (JSX
  expressions), dts_mode (.d.ts ambient declarations, no resolved calls)

Touches existing files only with additive changes:
- type_rep.h/.c: new TS-specific CBMTypeKind values + constructors
- type_registry.h/.c: TS-specific fields, hash indexes, cbm_registry_finalize
- cbm.c: dispatch for JS/TS/TSX languages
- lsp_all.c, Makefile.cbm, tests/test_main.c: include + suite registration
- README.md: feature list mention

291 ts_lsp test cases. CBM_LSP_DISABLED=1 disables the resolver
(matches CBM_LSP_DEBUG / CBM_LSP_DISABLED knobs in go_lsp / c_lsp).
2026-05-10 14:44:50 +02:00
Martin Vogel e6f4aa74bd Merge origin/main into worktree-php-lsp-integration
Resolved 5 conflicts where the php-lsp branch and main both added LSP
infrastructure but with different APIs:

- Makefile.cbm: kept both TEST_PHP_LSP_SRCS and main's py_lsp/scope/
  type_rep/stack_overflow test source variables in ALL_TEST_SRCS.
- internal/cbm/cbm.c: kept both #include lsp/php_lsp.h and lsp/py_lsp.h;
  the per-language dispatch already supports stacking PHP after PYTHON.
- internal/cbm/lsp_all.c: kept both lsp/php_lsp.c + php_stdlib_data.c
  AND lsp/py_lsp.c + python_stdlib_data.c in the compilation unit.
- src/pipeline/pass_calls.c: took main's version entirely.
  find_lsp_resolution + resolve_single_call(ctx, call, lsp_calls, ...)
  is language-agnostic — it matches any CBMResolvedCall whose caller_qn
  hits the textual call's enclosing function and whose callee_qn
  short-name hits the textual callee. PHP LSP populates the same
  result->resolved_calls array via cbm_run_php_lsp, so the unified
  resolver handles PHP for free. Dropped the php-branch's
  qn_short_name + lsp_override_resolution helpers as superseded.
  Locked confidence floor at main's CBM_LSP_CONFIDENCE_FLOOR = 0.6f
  (was 0.5f on the php branch).
- tests/test_main.c: kept all suite externs and RUN_SUITE invocations
  for php_lsp + py_lsp/py_lsp_bench/py_lsp_stress/py_lsp_scale +
  scope + type_rep.

Auto-merged: src/pipeline/pass_parallel.c.

Brings in 52 commits from main since the branch was forked at c768b61
(#207 watcher follow-up): #208 through #281, dependabot bumps, Pine
Script, AUR docs, INHERITS edges, get_architecture wiring, codeql
revert, the entire python-lsp subsystem.

Suite: 3249 passed, 0 failed (was 2971 on main; +278 new tests from
php-lsp matches the branch's claimed test count).
2026-05-09 17:14:14 +02:00
Martin Vogel 4d9f62f7e6 Merge origin/main into worktree-python-lsp-integration
Resolved conflict in Makefile.cbm: keep both TEST_STACK_OVERFLOW_SRCS
(from main, #217) and the new py_lsp test variables (TEST_SCOPE_SRCS,
TEST_TYPE_REP_SRCS, TEST_PY_LSP_SRCS, TEST_PY_LSP_BENCH_SRCS,
TEST_PY_LSP_STRESS_SRCS, TEST_PY_LSP_SCALE_SRCS) in ALL_TEST_SRCS.

Other auto-merged files: internal/cbm/extract_defs.c (PR #279),
tests/test_main.c (multiple suite registrations on each side).

Brings in 28 commits from main since the branch was forked at 8fbdb0f
(#207 thread safety): #208 decorator USAGE, #209 memory helpers, #210
refactor, #217 traversal stacks, #224 Svelte/Vue imports, #231
search_graph default limit, #243 path aliases, #249 GH Actions shell
injection, #251 incremental destructive overwrite, #257 temporal
properties, #265 Nix flake, #267-270/#289 dependabot, #273 Pine Script,
#278 AUR docs, #279 INHERITS edges, #281 get_architecture wiring +
follow-up, codeql revert.
2026-05-09 16:56:06 +02:00
Martin Vogel ecfbbf48d6 perf(py_lsp): cap dedup window at 256 entries — 91.8x -> 31.4x scaling
Audit for O(n^2) hot paths after Round 10. The dedup loop in
py_emit_resolved_call scanned ALL prior entries linearly, making
emission O(N) and total file work O(N^2). On a synthetic 2000-class
fixture with 8000 calls, that dominated time at 5083 ms.

Cap the scan window at the last 256 entries. Most duplicate
emissions come from the same expression being walked twice (resolver
+ emitter passes), so they're temporally adjacent — a 256-entry
window catches every common case while keeping the dedup O(1) per
emission.

Scale measurements (test_py_lsp_scale.c added):
| Classes | Calls | Resolved | Before | After |
|--------:|------:|---------:|-------:|------:|
|     100 |   400 |      300 |   55ms |  57ms |
|     500 |  2000 |     1500 |  333ms | 323ms |
|    2000 |  8000 |     6000 | 5083ms | 1804ms|

Ratio for 20x input: 91.8x -> 31.4x (linear would be 20x; quadratic
would be 400x). No remaining quadratic hot path in code added by
this branch.

Other hot paths surveyed:
- py_register_def -> cbm_registry_lookup_type: O(types) per def at
  registration, O(D x types) for D defs. Pre-existing linear scan in
  type_registry.c; hash-indexing it would be a cross-language
  optimization (Go and C++ LSPs benefit too).
- py_lookup_attribute / py_lookup_field: O(types) per call site.
  Same pre-existing linear scan. With ~900 stdlib types + per-file
  defs, accounts for ~75µs/call at 2000-class scale.
- MODULE submodule prefix scan: O(funcs) per module-attribute access,
  early-exits on first match. Bounded by registry size.
- lambda / dict-literal registries: linear, but bounded by per-file
  N which is small.

Bench unchanged at 100% / 12ms (52/52 calls, 178 LOC).
2026-05-09 15:44:09 +02:00
Martin Vogel f35745c79f feat(py_lsp): with-as, except-as, tuple unpack, slice, container TypeVar special-cases — bench 100%
Round 8. Stress test suite (test_py_lsp_stress.c) added with 22 advanced
patterns; bench fixture grew to 178 lines covering the full surface.

with-as binding via __enter__:
- Look up __enter__ on the context manager's NAMED type. Use its
  return type when known; fall back to the manager type itself
  (matches the practical case where __enter__ returns self). Handles
  tree-sitter Python's wrapping of `X as y` in an as_pattern node
  inside with_item, plus the flat field-only shape.

except-as binding:
- Bind exception alias to the caught type. Resolves through
  except_clause's as_pattern wrapper and the as_pattern_target
  identifier wrapper.

Tuple unpacking:
- Both `a, b = f()` and `for k, v in d.items()` destructure the RHS
  element-by-element when the rhs is TUPLE or TEMPLATE("tuple", ...).
  *rest binds the trailing slice as list[elem]. Shared helper
  py_bind_for_target used by for-statement, comprehension for_in_clause,
  and assignment LHS.

Slice subscript:
- `lst[1:3]` returns the same TEMPLATE container — list[T][1:3] is
  list[T], dict[K, V][1:3] is dict[K, V] (rare but consistent).

Container TypeVar substitution:
- Without a constraint solver we can't generally substitute K, V into
  registered method return types. Hand-rolled the most-impactful
  cases: dict.items/keys/values/get/pop/copy, list.copy/__iter__/pop,
  set.copy. These return properly parameterized ItemsView[K, V] /
  KeysView[K] / ValuesView[V] / Optional[V] / etc. so subsequent
  iteration and unpacking yield typed elements.

ItemsView / KeysView / ValuesView / dict_items / dict_keys / dict_values
added to py_iterable_element_type. ItemsView[K, V] iterates as
tuple[K, V], unpacked correctly by py_bind_for_target.

Bench: 178 LOC, 52 calls, **52 resolved (100%)**, 11.59 ms (ASan + UBSan).

22 new stress tests, 8 of 10 documented gaps closed in this round.
Remaining gaps documented inline (typeddict_subscript, post-early-return
narrowing, function-as-dict-value, match sequence pattern, lambda
inference). All 2898 prior tests stay green; total 2920 / 0.
2026-05-09 12:11:50 +02:00
Martin Vogel a8c9ace4a5 feat(py_lsp): Phase 11 benchmark methodology + integration bench
Final phase of Python LSP integration. Adds:

- docs/BENCHMARK_PYTHON.md — methodology document covering target
  metrics (per-file LSP overhead < 10 ms, resolution ratio ≥ 40% on
  application code, ≥ 70% with stdlib, < 5% spot-check false positive
  rate), the declared accepted-failure-class ceiling (metaclass
  __call__, __getattr__/__getattribute__, dynamic setattr, runtime
  importlib, monkey-patching, eval/exec, descriptors beyond the
  standard property/classmethod/staticmethod, dynamic type() classes),
  and instructions for running scripts/benchmark-index.sh against the
  largest Python repos in datadice (falkemedia: 232 files, api-hub:
  188 files).

- tests/test_py_lsp_bench.c — in-process benchmark wired into
  scripts/test.sh. Runs cbm_extract_file on a 65-line Python fixture
  covering imports, dataclasses, classmethods, single-inheritance with
  super(), typed parameters, attribute chains, and stdlib calls.
  Asserts soft floor (resolved >= calls/4) and hard time budget
  (< 50 ms under sanitizers). Initial run reports:

    bench: 65 lines, 16 calls, 13 resolved (81%), 2.45 ms

  81% beats the 40% application-code target and the 70%
  application-plus-stdlib target. 2.45 ms under ASan + UBSan is well
  inside the 10 ms (no-sanitizer) production budget.

This wraps the 11-phase plan documented in PYTHON_LSP_PLAN.md. Total
test count: 2874 / 0 (100% pass). Repository-level benchmarks via
scripts/benchmark-index.sh remain a manual operation against a fresh
production binary; methodology is now in place for when that runs.
2026-05-09 00:47:00 +02:00
Peter Cox 10a9b83e27 fix(pipeline): resolve build-tool path aliases for cross-file imports (#243)
CALLS edges for cross-file function calls were mis-wired or missing in
TypeScript projects that use path aliases (e.g. @/lib/auth → src/lib/auth):

  1. resolve_relative_import only handled "./relative" paths, so
     "@/lib/auth"-style imports never reached IMPORTS edges. The import
     map stayed empty, Strategy 1 in cbm_registry_resolve failed, and
     resolution fell through to name-based lookups that pick the wrong
     target when functions share names across files.

  2. resolve_import_map built the candidate QN as the bare module QN
     when the callee had no dot ("requireAdmin" vs "pkg.Func"), instead
     of module_qn.requireAdmin. Even with a populated import map, bare
     calls still mis-resolved.

Fix:

  - New src/pipeline/path_alias.{h,c}: language-agnostic build-tool
    path-alias subsystem. Currently parses tsconfig.json and
    jsconfig.json compilerOptions.paths + baseUrl via the existing
    yyjson dependency (with ALLOW_COMMENTS|ALLOW_TRAILING_COMMAS for
    JSONC). The data model (cbm_path_alias_t / map / scope /
    collection) is generic; adding Vite/Webpack/Python loaders means
    writing a new file-parsing helper and registering it in
    find_alias_files — the resolver, collection, and pipeline
    integration do not change.

  - O(n log n) ordering: scope-by-dir-specificity and entry-by-
    prefix-specificity are sorted with qsort, replacing the O(n²)
    bubble sorts that the original draft used. With caps of 256
    entries per config and 256 configs per repo, the sort is
    effectively flat.

  - Resource ceilings with logging: directory recursion capped at
    32 levels; total config files capped at 256; per-config size
    capped at 64 KB. Cap hits emit cbm_log_warn so silent truncation
    on pathological monorepos shows up in the index log.

  - Walk hygiene: skips node_modules, .git, dist, build, .next,
    coverage, target.

  - Single integration point: cbm_pipeline_resolve_module gets a
    Strategy 1b between relative-import resolution and pkgmap so both
    the sequential and parallel pipelines pick up alias resolution
    automatically. Non-TS projects pay nothing — the loader returns
    NULL when no usable configs are found.

  - Layer 2 fix in registry.c: resolve_import_map now builds the
    candidate as resolved.prefix when suffix is NULL (one-line fix,
    matching comment).

  - Tests: 10 path_alias tests (basic wildcard, specificity, exact,
    extension stripping, baseUrl, NULL safety, nearest-ancestor scope
    selection, end-to-end loader against a tmp monorepo, no-configs
    case) plus a registry test for the bare-call fix. Full suite:
    2835 passed, 0 failed.

Cherry-picked and generalized from #243 (originally TypeScript-only
under tsconfig-named types). Renamed types (cbm_path_alias_scope_t /
collection_t in place of cbm_tsconfig_*) and centralised the load
into cbm_load_path_aliases so future loaders for other build tools
slot in without touching extraction or pipeline code.

Closes #243.

Co-authored-by: Peter Cox <info@petercox.ie>
2026-05-08 23:45:32 +02:00
Martin Vogel f0905589cf test(php-lsp): add 16 single-file regression tests
Covers parameter binding (typed param, arrow function, $this, catch),
method dispatch (typed receiver, chain, self/parent), static calls,
namespace + use resolution, PHPDoc @var, magic methods, and the
ConfiguresPrompts $prompt->value() regression from PHP_LSP_PRE_FLIGHT.md.

Wired into ALL_TEST_SRCS and suite_php_lsp invocation in test_main.c.
All 16 tests pass; total suite is now 2829/0 passing.
2026-05-08 23:33:16 +02:00
Martin Vogel b1a6285601 feat(lsp): py_lsp scaffold + smoke tests
Phase 2 of Python LSP integration. Adds the py_lsp module mirroring
the go_lsp / c_lsp pattern: PyLSPContext, single-file entry
(cbm_run_py_lsp), cross-file + batch shells, stdlib registration stub.

Bodies are no-ops at this phase. Subsequent phases fill in:
  Phase 3 — imports
  Phase 4 — scope binding
  Phase 5 — expression typing
  Phase 6 — attribute resolution + method dispatch
  Phase 7 — decorators
  Phase 8 — class hierarchy + generics
  Phase 9 — cross-file resolution + batch
  Phase 10 — cbm_python_stdlib_register from typeshed

Wired into cbm_extract_file alongside the existing Go and C/C++ LSP
calls. lsp_all.c #includes py_lsp.c so the CGo umbrella picks it up
without a separate translation unit.

tests/test_py_lsp.c adds 5 smoke cases: empty source, single function,
single class, syntax error recovery, imports pass-through. The helper
shape (extract_py / find_resolved / require_resolved) matches
test_go_lsp.c so future phases can grow the suite by category.

All 2839 existing tests stay green; 5 new py_lsp tests pass.
2026-05-08 23:06:02 +02:00
Martin Vogel def7b94db7 feat(lsp): type-rep extensions for Python (UNION, LITERAL, PROTOCOL, MODULE, CALLABLE)
Phase 1 of Python LSP integration. Five new CBMTypeKind variants needed
for Python type modeling, plus structural-equality helper used by union
deduplication and protocol matching:

- UNION: A | B / Union[A, B] / Optional[T]. cbm_type_union normalizes
  by flattening nested UNIONs, deduplicating members via cbm_type_equal,
  and collapsing single-member unions to their member. Empty unions
  reduce to UNKNOWN.
- LITERAL: Literal["foo", 3] — wraps a base type with literal text;
  text-distinct literals compare unequal.
- PROTOCOL: typing.Protocol with method-name set. Structural matching
  via cbm_type_protocol_satisfied_by — succeeds when the candidate's
  method set is a superset of the protocol's.
- MODULE: bound name from `import X`, distinguishing module-attribute
  lookup from class-attribute lookup. Equality is by module_qn.
- CALLABLE: Callable[[A, B], R] including the elliptic Callable[..., R]
  encoded as param_count = -1.

cbm_type_equal added as a recursive structural-equality helper covering
all kinds; FUNC/INTERFACE/STRUCT fall back to pointer identity since
their structural members are rarely compared and the cost would be
substantial.

Adds tests/test_type_rep.c with 16 cases covering normalization,
equality, and protocol satisfaction.

All 2823 existing tests stay green; 16 new type-rep tests pass.
2026-05-08 22:46:48 +02:00
Ahmed Mohammed aa6df6b906 fix(extraction): replace fixed traversal stacks with growable arena-allocated stacks (#217)
AST traversal functions used fixed-size TSNode stack[] arrays. When the
DFS stack filled up, the child-push loop exited silently, dropping
entire subtrees without warning (e.g. 600 TS imports → 511, 130 Express
routes capped at 512). Reported in #199; also addresses #213 (large
TypeScript files producing zero nodes from stack exhaustion) and #215
(SEGV in template calls from stack overflow).

Adds ts_node_stack.h — a growable stack backed by the existing arena
allocator. Initial capacity matches previous fixed caps so small files
allocate no extra memory; doubles on overflow instead of truncating.
Old blocks are abandoned in the arena and freed on arena_destroy at
the end of file extraction (no realloc, no mixed lifetimes).

Applied to all 14 TSNode fixed stacks across 9 extraction files
(extract_calls, extract_channels, extract_defs, extract_env_accesses,
extract_imports, extract_semantic, extract_type_assigns, extract_type_refs,
extract_usages). walk_defs (uses walk_defs_frame_t, different struct
type) deliberately left as-is.

7 regression tests in tests/test_stack_overflow.c cover TS imports >512,
JS/Python calls >512, Go calls >1024, Express routes >150, deeply
nested calls, and YAML vars >256.

Cherry-picked from #217 with merge-conflict resolution against post-#206/#207
main:
- Makefile.cbm: keep TEST_STACK_OVERFLOW_SRCS plus main's new
  TEST_ZSTD_SRCS / TEST_ARTIFACT_SRCS.
- extract_channels.c: keep main's scan_string_consts_js name (main also
  added scan_string_consts_python after the PR was opened); declare
  CHAN_STACK_CAP in the enum block.
- test_stack_overflow.c: replace sprintf with snprintf using the actual
  remaining buffer size, since macOS clang rejects sprintf and naive
  snprintf with sizeof(p) on a char* fails -Wformat-truncation.

Closes #199, #213, #215. Closes #217.
2026-05-08 22:36:40 +02:00
Martin Vogel 6757dc8c0c refactor(lsp): chunked linked-frame CBMScope + MRO depth cap
Phase 0 of Python LSP integration. Two shared-infrastructure fixes that
silently bug Python without affecting Go/C++ regression tests:

- CBMScope replaces the fixed 64-binding array with a growable linked
  list of 16-binding chunks. The arena pointer is captured at scope_push
  and reused for chunk allocation, so existing callers benefit from
  dynamic growth without an API change. Python class bodies and large
  dataclass scopes routinely exceed 64 locals; this prevents silent
  drops past the legacy cap.

- CBM_LSP_MAX_LOOKUP_DEPTH=16 wraps go_lookup_field_or_method and
  c_lookup_member via depth-aware internal helpers. Pathological
  multi-inheritance hierarchies (SQLAlchemy / Django / Zope) can exceed
  20 levels and previously stack-overflowed; now they degrade to NULL
  past depth 16 instead.

Adds tests/test_scope.c with 10 cases covering basic API, parent-chain
walks, child-shadowing, and dynamic growth at 300 bindings + chunk
boundaries. test_main.c and Makefile.cbm wire the new suite.

PYTHON_LSP_PLAN.md added at the worktree root: full phased plan plus
the verbatim challenger review.

All 2823 existing tests stay green; 10 new scope tests pass.
2026-05-08 22:26:56 +02:00
test 2343f8efba feat: generic package/module resolution for IMPORTS edges across 10 languages
Bare import specifiers (@myorg/pkg, github.com/foo/bar, use my_crate::foo)
previously produced zero IMPORTS edges. This adds manifest-aware resolution
during parallel extraction for JS/TS (package.json), Go (go.mod),
Rust (Cargo.toml), Python (pyproject.toml), PHP (composer.json),
Java (pom.xml), Gradle (build.gradle), Dart (pubspec.yaml),
Elixir (mix.exs), and Ruby (*.gemspec).

Workers parse manifest files during extraction with zero extra I/O,
entries are merged into a hash table before registry build, and
cbm_pipeline_resolve_module() provides unified resolution with
prefix matching for Go/Java/PHP path-based specifiers.

Closes #180.
Based on #184 by dLo999 (JS/TS package map approach).

Co-Authored-By: Dustin Obrecht <dustin@kurtnoble.com>
2026-04-15 23:57:09 +02:00
test 8babe67bea feat: add persistent artifact storage for team sharing
Add .codebase-memory/graph.db.zst — a zstd-compressed knowledge graph
artifact that can be committed to the repo. Teammates bootstrap from
the artifact instead of running a full reindex from scratch.

- Vendor zstd 1.5.7 (amalgamated build) for 8-13:1 compression
- Two-tier export: zstd -9 + index stripping for explicit index,
  zstd -3 for watcher/incremental auto-updates
- Import: decompress → integrity check → auto-recreate indexes
- Bootstrap in handle_index_repository: when no local DB exists but
  artifact is present, import first then run incremental
- Auto-create .gitattributes with merge=ours to prevent conflicts
- Fix: add missing idx_edges_url_path to create_user_indexes and
  url_path_gen generated column to init_schema
- 13 new tests (5 zstd wrapper + 8 artifact round-trip/edge cases)
2026-04-15 23:56:03 +02:00
Martin Vogel c5ad6e8c59 Add cross-repo intelligence mode with CROSS_* edge types
New index_repository mode="cross-repo-intelligence" that matches Routes,
Channels, and async topics across indexed projects:

- CROSS_HTTP_CALLS: matches HTTP_CALLS edges against Route nodes in
  other projects via deterministic QN (__route__METHOD__/path)
- CROSS_ASYNC_CALLS: matches ASYNC_CALLS edges by broker + topic
- CROSS_CHANNEL: matches Channel nodes (EMITS/LISTENS_ON) by name +
  transport across projects

Edges are written bidirectionally: both source and target project DBs
get a CROSS_* edge so the link is visible from either side.

Usage: index_repository(repo_path="/path", mode="cross-repo-intelligence",
       target_projects=["*"])

New files: src/pipeline/pass_cross_repo.c/.h
Modified: src/mcp/mcp.c (handler + tool schema), Makefile.cbm
2026-04-15 23:53:42 +02:00
Martin Vogel 894c04fc0e Fix cross-platform vector blob assembly and vendored security allowlist
- code_vectors_blob.S: preprocessor conditionals for macOS (Mach-O
  __DATA,__const + underscore prefix) vs Linux (ELF .rodata, no prefix)
- Makefile: use $(CC) -c instead of $(AS) to enable preprocessor on .S
- Add vendored/nomic to KNOWN_VENDORED security allowlist (pure int8
  vector data, zero executable code)
- Update vendored checksums
2026-04-06 12:54:13 +02:00
Martin Vogel a0715ff1eb Add JS/TS IMPORTS resolution, Channel schema, lint cleanup
Features:
- Relative import resolution for JS/TS/Python/Ruby (./foo, ../bar,
  leading dots) via cbm_pipeline_resolve_relative_import in fqn.c
- Generic Channel detection: Socket.IO emit/on, EventEmitter patterns
  produce Channel nodes with EMITS/LISTENS_ON edges and transport
  property (extract_channels.c)
- Constant resolution trick: `const EVENT = "foo"; emit(EVENT)` resolves
  the channel name through a per-file constant table

Lint fixes (337 errors across 16 files):
- Named constants replacing all magic numbers (enums/defines)
- Cognitive complexity reduced via helper extraction (table-driven
  sqlite_writer, pipeline predump passes, graph_buffer dump phases)
- SQLITE_TRANSIENT int-to-ptr workaround in store.c + mcp.c
- clang-format + cppcheck clean

Co-Authored-By: Koolerx <tommy@koolerx.com>
2026-04-06 12:41:31 +02:00
Martin Vogel 647af0b57c Add BM25 full-text search + C# Interface extraction fixes
Ports four targeted improvements from #162 (Koolerx):

- FTS5 BM25 ranked search via a new `query` parameter on search_graph.
  Uses a contentless nodes_fts virtual table populated at dump time with
  a cbm_camel_split() SQL function that splits camelCase identifiers
  (updateCloudClient -> update Cloud Client) so tokenized word queries
  match identifier internals. BM25 rank is boosted by node label
  (Function/Method +10, Route +8, Class/Interface/Type/Enum +5) and
  noise labels (File/Folder/Module/Variable) are filtered out. The new
  path is opt-in via the query parameter; regex name_pattern and vector
  semantic_query paths are untouched.

- Enable SQLITE_ENABLE_FTS5 in the vendored sqlite3 build flags.

- Rebuild nodes_fts after incremental reindex. The btree dump path
  bypasses any FTS5 triggers, so the index is wiped and repopulated via
  delete-all + INSERT .. SELECT after each incremental merge.

- Add Interface to the label filter on cbm_registry_add in both the
  parallel and sequential definition passes, so C#/Java class
  declarations like Foo : IBar can resolve IBar to an INHERITS edge
  target during enrichment.

- Add an explicit C# base_list handler to extract_base_classes() that
  iterates named children directly instead of returning the raw node
  text. Without this the fallback path returned ": IExamService" (with
  the leading separator) and registry lookup always failed, so C#
  INHERITS edges to interfaces never resolved.

Co-Authored-By: Koolerx <Koolerx@users.noreply.github.com>
2026-04-06 12:41:30 +02:00
Martin Vogel 8a06d78ac7 Parallelize post-passes, fix mode filtering + semantic edge quality
- Parallelize pass_similarity and pass_semantic_edges via worker pool with
  thread-local edge buffers; sequential final merge since gbuf is not
  thread-safe. Adds cbm_lsh_query_into() as a thread-safe variant with
  caller-provided candidate buffer.

- Add activatable profiling subsystem (CBM_PROFILE=1 env or --profile flag)
  for step-level timing of extract, resolve, corpus build, vector phases,
  and sqlite dump. Zero overhead when disabled.

- Fix cbm_index_mode_t enum mismatch between pipeline.h (FULL=0, MODERATE=1,
  FAST=2) and discover.h (FULL=0, FAST=1). mode=fast silently no-op'd
  fast-discovery filtering because discover.c compared against the wrong
  value. Linux kernel fast mode went 1:40 -> 3:11 as a result; now back to
  1:40. Broaden the filter guard to mode != CBM_MODE_FULL so MODERATE and
  FAST both get aggressive discovery.

- Clamp cbm_sem_combined_score output to [0, 1]. The proximity multiplier
  returns up to 1.10 as a same-file boost which could push the final
  cosine score above 1.0.

- Short-circuit semantic scoring when MinHash jaccard >= 0.95. Exact
  near-clones are already emitted as SIMILAR_TO edges; returning 0 here
  avoids flooding SEMANTICALLY_RELATED with cross-service copy-paste
  boilerplate and frees the edge budget for genuine vocabulary-bridged
  relations.

- Validate search_graph semantic_query as an array of strings and return
  a clear error for a single-string input. Update the tool description
  to spell out the requirement explicitly with an example.

- JSON-escape user-controlled strings (callee names, call arguments,
  URL paths, import local_name) in call/argument properties. Introduces
  cbm_json_escape() in foundation/str_util.

- Skip SQLite pending_byte_page (file offset 0x40000000) during raw page
  writes in sqlite_writer to avoid corrupting databases that cross the
  1 GiB boundary.

- Migrate pretrained vector blob from UniXcoder (51K tokens) to
  nomic-embed-code (40856 tokens x 768d int8). Includes the extraction
  script under scripts/extract_nomic_vectors.py.
2026-04-06 12:41:30 +02:00
Martin Vogel e81a796fe6 Embed Nomic nomic-embed-code pretrained vectors (40K tokens × 768d) for semantic search quality
Major quality improvement: replace random index vectors with pre-trained
Nomic nomic-embed-code token embeddings. Tokens like 'error' and 'exception' now start
with similar vectors (learned from millions of code repos) instead of
arbitrary random projections. Co-occurrence enrichment adds project-specific
context on top.

Architecture:
- vendored/nomic/code_vectors.bin: 37.7MB raw int8 vectors
- vendored/nomic/code_vectors_blob.S: assembler .incbin (instant build)
- vendored/nomic/code_vectors.h: extern declarations + pretrained_vec_at()
- vendored/nomic/code_tokens.h: 40856 token strings (575KB)
- semantic.c: cbm_sem_random_index() now looks up pretrained vectors first,
  falls back to sparse random for unknown tokens
- CBM_SEM_DIM raised from 256 to 768 to match Nomic nomic-embed-code

Also: RRI (Reflective Random Indexing), code pattern vocabulary injection,
120+ abbreviation expansions, callee/caller/body token enrichment,
label filter (Function/Method/Class only) in vector search SQL.

Binary size: 136MB → 169MB (+33MB from embedded vectors).
Search quality: keyword queries return relevant error-handling functions.
Domain-specific keyword queries return the expected functions.
2026-04-06 12:41:29 +02:00
Martin Vogel 8a0ad0489b WIP: Algorithmic semantic embeddings + indexing modes (checkpoint before vector storage) 2026-04-04 16:17:58 +02:00
Martin Vogel 452f5a7b1f Add MinHash fingerprinting and SIMILAR_TO edges for near-clone detection
Compute K=64 MinHash signatures from normalized AST node-type trigrams
during function extraction, then generate SIMILAR_TO edges via LSH
(b=32, r=2) for function pairs with Jaccard >= 0.95.

- src/simhash/minhash.{h,c}: MinHash compute, Jaccard, hex encode/decode,
  LSH index with band hashing for O(n) candidate generation
- src/pipeline/pass_similarity.c: post-pass reads fingerprints from node
  properties, builds LSH index, emits SIMILAR_TO edges with jaccard and
  same_file metadata. Same-language only, max 10 edges per node.
- internal/cbm/cbm.h: fingerprint fields on CBMDefinition
- internal/cbm/extract_defs.c: compute_fingerprint() hook at 3 extraction
  sites after complexity, skip functions with < 10 AST body nodes
- pass_definitions.c + pass_parallel.c: serialize fingerprint to "fp" hex
  in properties_json for both sequential and parallel pipeline paths
- pipeline.c + pipeline_incremental.c: register pass_similarity in both
  full and incremental post-pass lists
- tests/test_simhash.c: 28 tests across 4 suites (core, LSH, edge gen,
  pipeline integration with generated Go project + incremental)
2026-04-03 20:06:33 +02:00
Martin Vogel dbab4d372f Wire up risk_labels on trace_path, add --progress CLI flag
risk_labels: adds CRITICAL/HIGH/MEDIUM/LOW classification to BFS
results based on hop distance. Opt-in via risk_labels=true.

--progress: human-readable indexing progress on stderr. Log sink
replaces default output, maps pipeline events to [1/9]..[9/9] phases.

Co-Authored-By: halindrome <halindrome@users.noreply.github.com>
Co-Authored-By: gdilla <gdilla@users.noreply.github.com>
2026-04-02 23:47:40 +02:00
Martin Vogel 9662063090 Add Alpine portable as 4th local test platform, fix musl compat
Local test infra now covers 4 platforms:
- Linux arm64 (Ubuntu, ASan+LeakSan)
- Linux portable (Alpine musl, static binary)
- Windows (mingw cross-compile)
- macOS (native)

Fixes:
- Remove sys/unistd.h and sys/poll.h (glibc-only, musl lacks them)
- Add STATIC=1 support to Makefile (appends -static to LDFLAGS)
- CI portable build uses docker run alpine instead of container directive
  (GitHub Actions JS actions break in Alpine containers on ARM64)
2026-04-02 20:28:20 +02:00
Martin Vogel 25d94d821a Suppress UBSan alignment check for vendored tre library
tre-compile.c has misaligned struct access on Windows (UBSan kills
the test runner). The misalignment is in vendored code we don't
control. Add -fno-sanitize=alignment to tre test build only.
2026-04-02 18:31:33 +02:00
Martin Vogel 7fa3acd0c6 WIP: strict linting + RAM-first pipeline (lint fixes pending) 2026-04-01 23:22:44 +02:00
Martin Vogel b523116ef2 Achieve zero clang-tidy errors at CC<=25 threshold
Split all remaining pipeline functions. Extract 40+ helpers.
Add lint-no-suppress quality gate. Add clang-tidy to lint.sh.
Zero NOLINTNEXTLINE, zero clang-tidy errors, 2741 tests pass.
2026-03-31 20:04:12 +02:00
Martin Vogel c02d3c96a1 Incremental indexing + MCP tool integration tests (155 tests)
Comprehensive test suite using real repo (FastAPI 0.99.1, ~1100 files):
- Full index baseline with perf metrics (nodes, edges, RSS, timing)
- Incremental: modify/add/delete/rename files, noop, formatter run
- Adversarial: empty file, syntax errors, 5000-line function, binary
- Stress: rapid reindex, batch 20 files, delete-all + re-add
- Accuracy: incremental vs full within ±2 nodes/edges
- MCP tools: all 14 tools, every parameter, content validation
- Error handling: bad project, missing params for each tool
- Performance: 5s timeout on every tool call

Fix use-after-free in detect_changes and manage_adr:
yyjson_mut_arr_add_str stored pointer to stack buffer without copy.
Changed to yyjson_mut_arr_add_strcpy / yyjson_mut_obj_add_strcpy.
2026-03-28 13:59:42 +01:00