Commit Graph
132 Commits
Author SHA1 Message Date
Martin Vogel a4e1f21c3a Merge pull request #681 from RollerCoaster1899/test/add-unit-tests-semantic-slab-astprofile
test: add 56 unit tests for semantic, slab_alloc, and ast_profile modules
2026-07-04 20:26:19 +02:00
Martin Vogel c7374135ac Merge pull request #847 from DeusData/distill/ui-label-and-ts-repro
fix(graph-ui): honest Project-ID label; RED repro for TS inherited-method gap
2026-07-04 15:31:27 +02:00
Martin Vogel 896aa914f6 Merge pull request #844 from DeusData/distill/828-usage-ownership
fix(graph): stable USAGE-edge ownership for directory-module nodes
2026-07-04 15:00:16 +02:00
Martin Vogel b58d1d84e6 test(repro): RED — TS cross-file inherited method call unresolved
PR #840 flips the lrp_ts_s6_inherited_method probe from a fragile
false-green (a unique_name short-name registry guess that happened to
bind the right node in a 2-file fixture) to asserting the weak edge is
suppressed. That flip is correct, but it leaves the underlying gap —
ts_lsp cross-file INHERITED method call resolution — with no red
reproduction anywhere.

Add tests/repro/repro_ts_inherited_method.c to the non-gating repro
suite: base.ts defines Base.greet, derived.ts has Derived extends Base
and a typed call site d.greet(). A GREEN precondition test proves both
files extract cleanly, the Base.greet Method def exists and the call
site is extracted; the RED pipeline test additionally verifies the
callee node and the INHERITS edge are in the graph, then asserts the
correct outcome: a CALLS edge callSite -> *.Base.greet carrying an
lsp_ts_* strategy on that same edge. The pre-#840 lucky unique_name
fallback edge does not satisfy it (verified: the run dumps that exact
edge, strategy unique_name, confidence 0.75), and post-#840 suppression
yields no edge at all — so the test stays RED until ts_lsp genuinely
resolves cross-file inheritance. Guards the gap left visible after

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
#840's probe flip; see the test header for the root-cause pointer.
2026-07-04 13:37:45 +02:00
Martin VogelandTommy Corbett f6b9ac9d65 fix(graph): stable USAGE-edge ownership for directory-module nodes
Since #667 every file in a Java/Go package shares a directory-based module
QN that collides with the pipeline's Folder/Project node. Class-level
references resolved their enclosing scope to that shared node, whose
file_path was clobbered by each file's always-emitted Module def — last
writer wins (sequential upsert) or last-merged worker wins (parallel merge)
— so USAGE-edge sources conflated across same-package files and varied run
to run.

Three coordinated changes:
- cbm_gbuf_upsert_node: a Module def colliding with a Project/Folder node
  no longer updates ANY field (the #667 guard only protected the label).
- merge_update_existing: same guard on the parallel worker-gbuf merge path;
  the ID remap stays outside the guard so worker edges still redirect onto
  the canonical node.
- Source-node finders (pass_usages, pass_parallel, pass_calls) treat a
  lookup landing on a Folder/Project container as a miss and fall back to
  the per-file File node (new cbm_pipeline_node_is_dir_container helper).

Adds tests/repro/repro_issue787.c: exact-source-set, 5-run stability, and
>50-file parallel-path guards (red on the unfixed code in both sequential
and parallel flavors).

Distilled from #828: the author's commits lacked DCO sign-off trailers and
could not be cherry-picked; content applied verbatim plus one clang-format
fix in graph_buffer.c.

Closes #787.

Co-authored-by: Tommy Corbett <spde89@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-04 13:34:54 +02:00
Martin Vogel 9770803527 Merge pull request #829 from SyntaxSawdust/codex/issue-801-libgit2-floor
fix(build): gate libgit2 allocator support
2026-07-04 11:20:26 +02:00
Martin Vogel 9871e7e086 Merge pull request #827 from DeusData/feat/index-supervisor-subprocess
feat(index): crash/hang supervisor — quarantine the culprit file, index the rest
2026-07-04 05:12:58 +02:00
Dustin Persek 87ec7bc464 fix(build): gate libgit2 allocator support
Signed-off-by: Dustin Persek <dustin.persek@protonmail.com>
2026-07-03 18:51:47 -04:00
Martin Vogel d9f8d5bfd8 Merge pull request #723 from kriswill/fix/macos-nix-build
fix(build): make scripts/build.sh work under Nix on macOS
2026-07-03 18:35:56 +02:00
Martin Vogel 9b9638e1dd feat(index): supervise index_repository in a crash/hang-isolating worker
A single pathological file that hard-crashes the native indexer (SIGSEGV / stack
overflow / abort / tree-sitter assertion) previously took down the whole MCP
server or CLI (issue #668). index_repository now runs in a worker subprocess: the
parent reaps it, and a crash is contained — the parent survives and reports it
instead of dying.

- src/mcp/index_supervisor.{h,c}: worker-role state + cbm_index_spawn_worker(),
  which spawns `<self> cli --index-worker index_repository <json> --response-out
  <tmp>` via the subprocess primitive, reaps, and classifies. fork+exec only;
  recursion is prevented by the --index-worker argv flag, never an ambient env
  var (which could misfire and silently run unprotected in-process).
- src/mcp/mcp.c: a supervisor gate at the top of handle_index_repository wraps the
  run unless this process is the worker or CBM_INDEX_SUPERVISOR=0 (kill switch).
  Clean exit passes the worker's response through; a crash/hang returns a
  contained-failure response; a spawn failure degrades to the in-process path.
- src/main.c: parse --index-worker / --response-out; the worker writes its result
  to the response file for the parent to read back.
- src/ui/http_server.c: its existing index spawn now passes --index-worker so it
  does not re-supervise (one isolation layer, no redundant process nesting).
- src/foundation/subprocess.c: the forked child redirects output with open()+dup2()
  instead of freopen() — async-signal-safe, no malloc between fork and exec, since
  the parent may be multithreaded (server threads + mimalloc/sqlite/libgit2 state).
- internal/cbm/cbm.c: CBM_TEST_CRASH_ON / CBM_TEST_HANG_ON deterministic fault
  injectors (test-only, env-gated) so the guard is honest — green iff a real fault
  is genuinely contained, not a fixture that may stop faulting.
- scripts/smoke-invariants.sh: inv_crasher_contained_cli asserts the crash escapes
  as a signal without the supervisor (rc>=128) and is contained + reported with it
  (rc<128, outcome=crash).

Precise skip-and-continue (quarantine the culprit file, index the rest) is layered
on in a follow-up; this change contains the crash and keeps the process alive.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-03 11:55:15 +02:00
Martin Vogel c7c94003cc feat(foundation): add cross-platform subprocess spawn + exit classification
New src/foundation/subprocess.{h,c}: spawn a child, tail its output, and
classify how it ended — clean / exit-nonzero / crash / hang / killed — from
POSIX WIFSIGNALED/WTERMSIG and the Windows NTSTATUS exception exit codes
(0xC0000005 access violation, 0xC00000FD stack overflow). Adds an EINTR-safe
reap loop, a quiet-timeout that kills and reports a hung child (no new output
within the window), and partial-line-safe log tailing.

Generalized from the ad-hoc index spawn in src/ui/http_server.c so the
crash/hang supervisor can reuse one primitive across platforms. The exit-code
to outcome mapping lives in a pure cbm_proc_classify() so the Windows
crash-code path is unit-tested on every platform, not just an untested
Windows branch.

tests/test_subprocess.c: 13 tests — the classifier on all platforms plus real
POSIX spawn/reap of clean, non-zero, SIGSEGV-crash and hang children. Green
under ASan/UBSan.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-03 11:20:32 +02:00
Martin Vogel e89aa776a7 feat(index): report per-file indexing failures via skipped[] + logfile (Stage 2/B2-B4)
index_repository silently dropped files that failed to index: CBMFileResult.has_error
was set but never read, oversized files (>100 MB) were dropped with no signal, and
read/extract failures only bumped a logged-only counter — a file that couldn't be
indexed just vanished from the graph.

Collect and surface them:
- New retained {path, reason, phase} error list on struct cbm_pipeline (mirrors the
  excluded_dirs pattern) + accessor + a back-pointer on cbm_pipeline_ctx_t so both
  extraction paths can append. Wired in BOTH the sequential (pass_definitions) and
  parallel (per-worker, merged lock-free) paths — small repos take the sequential
  path, so wiring only one would leave the guard vacuous.
- Feeds: read-fail, extract-fail, the newly-CONSUMED has_error (parse timeout /
  parse failed, with error_msg), and oversized. The cross_lsp phase is reserved for
  the crash supervisor (Track C) and not fed here (the cross-LSP passes are
  best-effort with no failure return; feeding the no-source case would be false
  positives).
- MCP/CLI response gains top-level "skipped_count" (0 on clean) and, when >0, a
  capped "skipped":{files[<=50],count,truncated} + "logfile". Status stays "indexed"
  — a reported skip is the expected, handled outcome, not a degradation.
- Per-run logfile (full uncapped list) written ONLY when >=1 file is skipped:
  $CBM_INDEX_LOG override else <cache_dir>/logs/<project>-<epoch>.log.
- Generous env-configurable caps (src/foundation/limits.c): CBM_MAX_FILE_BYTES,
  default raised 100 MB -> 512 MiB; over-cap files are REPORTED (phase oversized) +
  WARNed, never silently dropped.

Reproduce-first: tests/test_index_resilience.c (gating) — an oversized file (cap
lowered via env) must appear in skipped[] with the 2 good files still indexed and a
logfile written; a clean run has skipped_count 0 and no logfile. Genuine guard:
no-op'ing the recorder makes the oversized file silently vanish (RED). Full suite
5750/0, no ASan/UBSan.

Part of the resilient-indexing effort (Track B surfacing layer). Refs #668.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-02 22:11:00 +02:00
Martin Vogelandanivaryam 354befe557 fix(git): correct canonical_root for worktrees and subdirectory projects
detect_changes returned an empty impacted_symbols set for projects indexed
inside a git worktree or from a repo subdirectory (#659): canonical_root was
computed wrong. `git rev-parse --git-common-dir` emits a path relative to the
directory passed via -C (input_path), NOT to worktree_root, and the "/.git"
suffix was stripped textually without resolving ".." components. So e.g.
"../.git" joined against worktree_root, or "/ws/scripts/../.git" strip, left an
unresolved ".." and never matched the project's stored root path.

Fix (src/git/git_context.c): join the relative --git-common-dir against
input_path (the -C dir) instead of worktree_root, and realpath()/_fullpath()-
normalize the result BEFORE stripping "/.git" (the .git dir always exists for a
valid repo, so resolution succeeds). This is consistent with the existing
realpath'd root_path used for prefix matching (mcp.c).

Reproduce-first: tests/test_git_context.c. The subdirectory case is the genuine
guard — a repo indexed from a subdir yields a *relative* --git-common-dir, so the
unfixed code leaves an un-normalized "<root>/subdir/.." (verified RED; GREEN after
the realpath fix). The linked-worktree case is a supporting invariant, not the
#659 reproducer: git that emits an *absolute* worktree --git-common-dir (e.g.
2.48.x) doesn't manifest the bug, so it passes either way; it still enforces the
worktree->main-root invariant. All three git-based tests SKIP_PLATFORM on Windows
(the CI shell there cannot init a repo via system()).

Distilled from #672 (thanks @anivaryam); that PR's production fix is taken
verbatim. Its repo_root/subdir tests ran git unconditionally and failed the
Windows CI leg ("failed to init git repo"); here all three git-based tests are
Windows-skipped so ci-ok is green cross-platform. Supersedes #696, which only
realpath'd after the strip without changing the join base and so still yielded
the workspace parent for the subdirectory case.

Closes #659

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Co-Authored-By: anivaryam <anivaryam.dev@gmail.com>
2026-07-02 00:19:16 +02:00
Martin Vogel 39e3c0798f Merge pull request #662 from KerseyFabrications/fix/rust-lsp-call-push-overread
fix(build): rebuild lsp_all.o when its included headers change
2026-07-01 23:47:55 +02:00
Kris Williams 1aaa8297f3 fix(build): make scripts/build.sh work under Nix on macOS
verify_compiler() in scripts/env.sh ran `file` on the clang path and
grepped for arm64/universal. Nix's clang is a bash wrapper script, so
`file` reports "ASCII text executable" and the check falsely concluded
"x86_64 only", aborting before any compile. Replace it with a capability
probe: compile + link a trivial program for the target -arch and trust
the result — correct for wrapper scripts (Nix, ccache) and cross-compilers.

Make arch targeting explicit and toolchain-agnostic: export
ARCHFLAGS="-arch <arch>" from env.sh and fold it into CC/CXX in
Makefile.cbm via `override`, so it reaches every compile and link
(including the vendored objects) and survives a command-line CC=
override. This replaces the `arch -<arch> make` prefix, which cannot
work with Nix's fixed-target clang. Drop the now-redundant ARCH_PREFIX
from build/test/lint/repro.

Refs #705.

Signed-off-by: Kris Williams <115474+kriswill@users.noreply.github.com>
2026-06-30 09:05:45 -07:00
Raul Acosta 6919ae669f test: add 56 unit tests for semantic, slab_alloc, and ast_profile modules
Add focused unit tests for three previously untested modules:

- tests/test_semantic.c (32 tests): cbm_sem_tokenize (camel/snake/dot/abbrev),
  cbm_sem_cosine (identical/orthogonal/zero/negative/null),
  cbm_sem_normalize, cbm_sem_vec_add_scaled,
  cbm_sem_random_index (determinism), cbm_sem_proximity,
  cbm_sem_diffuse, corpus lifecycle, IDF, config

- tests/test_slab_alloc.c (14 tests): slab malloc/free (≤64B and >64B),
  calloc zeroing/overflow, realloc (shrink/promote/same-size),
  free-list cycling, pointer uniqueness, thread reset

- tests/test_ast_profile.c (10 tests): to_str/from_str round-trip
  (25 fields), null/empty/invalid inputs, to_vector range/saturation

All 56 tests pass with ASan+UBSan. Registered in Makefile.cbm
and test_main.c.

Signed-off-by: Raul Acosta <raulacosta@users.noreply.github.com>
Signed-off-by: Raul Acosta <raul.acosta.mmm@gmail.com>
2026-06-28 21:57:35 -06:00
Kris Kersey a864eca69e fix(build): rebuild lsp_all.o when its included headers change
lsp_all.o is a unity object that #includes every lsp/*.c resolver; those include
cbm.h, and CBMCall is copied BY VALUE across the lsp->pipeline boundary
(cbm_calls_push). Its Make rule depended only on lsp_all.c, not on the headers it
pulls in. So after CBMCall gained a field (is_method, #477) an incremental build
kept a stale lsp_all.o with the old struct layout while the rest of the tree
recompiled against the new one -- an ODR mismatch ASan flags as a
stack-buffer-underflow in cbm_calls_push (the by-value copy under-reads the
shorter stale struct). Clean builds (CI) always match, so it only bites
incremental developer builds.

Add an LSP_UNITY_DEPS prerequisite (lsp/*.c + lsp/*.h + lsp/generated/*.c +
internal/cbm/*.h) to lsp_all.o and prod_lsp_all.o so the object rebuilds when any
included header or unity source changes. Wildcard-based, so new LSP resolvers are
covered automatically; explicit because this Makefile has no -MMD auto-deps.

Verified: touching internal/cbm/cbm.h now rebuilds lsp_all.o (was stale before);
clean build + full ASan suite green, rustlsp_dbg_macro_inner_call no longer aborts.

Signed-off-by: Kris Kersey <kris@kerseyfabrications.com>
2026-06-27 18:20:20 +00:00
Martin Vogel 2f2b3771b1 test(repro): grammar battery — final 15 langs (100% grammar coverage)
ASSEMBLY/BEANCOUNT/BICEP/CFML/CFSCRIPT/LINKERSCRIPT/PINE/RESCRIPT/SQL/SQUIRREL/
SYSTEMVERILOG/TABLEGEN/TEMPL/VERILOG/VHDL. Completes per-grammar invariant
coverage for ALL 159 vendored grammars. Callable langs (verilog/vhdl/systemverilog/
cfml/cfscript/rescript/squirrel/pine/templ/sql) full battery (callable-sourcing RED
via enclosing-func drift); asm/linker/tablegen/bicep/beancount structural + a
robustness dim (malformed input must not crash) on every language.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 21:52:54 +02:00
Martin Vogel 66cf07a9e8 test(repro): grammar batteries — scientific/shaders + markup/docs
- scientific (15): GLSL/HLSL/WGSL/ISPC/Slang/Cairo/Sway/FunC/Wolfram/MATLAB/Magma/
  FORM/TLA+/Agda/Apex — full battery; callable-sourcing RED across the board
  (grammar-only, no cross-LSP rescue); FunC/Wolfram/FORM/TLA+/Agda calls-extraction
  at-risk RED (unusual call-node types) + robustness dim.
- markup (18): Markdown/RST/Typst/BibTeX/Mermaid/PO/Diff/Regex/CapnP/Smithy/WIT/QML/
  Liquid/Jinja2/Blade/PureScript/SOQL/SOSL — adaptive (docs/schema structural,
  Typst/QML/PureScript callable) + robustness dim. Markdown headings→Class (BM25 #518).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 21:46:34 +02:00
Martin Vogel 720ebc5038 test(repro): grammar batteries — build/infra + shells/misc
- build/infra (15): Dockerfile/Makefile/CMake/Meson/GN/Just/K8s/Kustomize/GoMod/
  Requirements/Gitignore/Gitattributes/SSHConfig/BitBake/Puppet — adaptive
  (structural defs + CMake/Make/Just/Puppet/BitBake callables) + robustness dim.
- shells/misc (19): Bash/Zsh/Fish/PowerShell/Tcl/Awk/Vimscript/Fennel/Janet/Nix/
  GDScript/Luau/Teal/Smali/LLVM-IR/NASM/DeviceTree/Kconfig/Hyprlang — full battery
  for callable shells (PowerShell/Tcl/Awk/Fennel/Nix/Teal callable-sourcing RED via
  enclosing-func drift), structural for asm/data; robustness dim each.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 21:39:35 +02:00
Martin Vogel 9d8420285f test(repro): grammar battery — config/data languages
JSON/JSON5/YAML/TOML/INI/HCL/XML/CSV/PROPERTIES/DOTENV/KDL/RON/PKL/NICKEL/JSONNET/
STARLARK. Adaptive battery (most are structural-only: extract-clean + valid
labels/FQNs/ranges + defs-present), plus HCL/Nickel/Jsonnet/Starlark callable +
callable-sourcing, plus a robustness dim: malformed/truncated input must RETURN
(no crash) on every config language.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 21:32:03 +02:00
Martin Vogel 2128f2b118 test(repro): LSP-pass invariants — TS, Java/C#, Kotlin/PHP/Rust
Completes per-pass coverage for all hybrid LSPs:
- repro_lsp_ts: 8 ts_lsp strategies (ts_local/method/namespace/import/jsx/jsx_import).
- repro_lsp_java_cs: 16 Java lsp_* (type/inherited/outer/super/this/static dispatch,
  interface_resolve/dispatch, method_ref*, constructor*) + 13 C# cs_* (cs_method_*/
  static/extension/ctor — C# uses its own cs_ vocabulary, not lsp_).
- repro_lsp_kt_php_rust: 14 Kotlin lsp_kt_*, 7 PHP php_*, 8 Rust lsp_* — Rust all RED
  (pass_lsp_cross.c has no CBM_LANG_RUST → cross-LSP never runs for Rust).
Each asserts callable-sourcing + strategy-presence in the CALLS edge.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 21:25:31 +02:00
Martin Vogel 648f0d399c test(repro): grammar battery — web/markup/schema languages
HTML/CSS/SCSS/Vue/Svelte/Astro/GraphQL/Protobuf/Thrift/Prisma/GoTemplate/JSDoc.
Adaptive battery: structural-only langs assert extract-clean + valid labels/FQNs/
ranges + defs-present; SCSS/GoTemplate also assert calls + callable-sourcing
(both RED — enclosing-func gap / no Function node to source the call).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 21:19:04 +02:00
Martin Vogel b9bd471cb1 test(repro): grammar battery — systems languages
Zig/Nim/Crystal/Hare/Odin/Pony/Ada/Fortran/COBOL/Pascal/Solidity/Move full
invariant battery. Zig/Hare/Solidity callable-sourcing GREEN guards; the rest RED
via enclosing-func drift (func_kinds_for_lang only covers Zig); Nim RED (no spec/
grammar — zero defs/calls, asserted as the documented gap).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 21:12:28 +02:00
Martin Vogel 0b232ba14a test(repro): grammar batteries (scripting, functional) + Go/Python LSP passes
- repro_grammar_scripting: Python/Ruby/PHP/JS/TS/TSX/Lua/Perl/R/Julia/Groovy/Dart
  full invariant battery (Perl/R/Julia/Groovy/Dart callable-sourcing RED).
- repro_grammar_functional: Haskell/OCaml/F#/Elixir/Erlang/Elm/Clojure/Scheme/
  Racket/CommonLisp/EmacsLisp/Lean/Gleam (Elm calls-extraction RED; all dim-7 RED
  via enclosing-func drift).
- repro_lsp_go_py: 7 Go strategies (direct/type/embed/interface_resolve+dispatch/
  cross_file/unresolved) + 14 Python strategies (method/super/super_init/
  module_attr/dict_dispatch/operator_dunder/builtin*/generic_method/method_union),
  each asserting callable-sourcing + strategy-presence.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 13:46:46 +02:00
Martin Vogel daa554b08c test(repro): C/C++ LSP-pass invariants (18 resolution strategies)
Per-pass resolution contract for the C/C++ hybrid LSP: one fixture per lsp_*
strategy c_lsp.c emits (lsp_direct, implicit_this, scoped, type/virtual/base/
smart_ptr dispatch, template[_instantiation], func_ptr, dll_resolve, operator,
constructor/destructor/copy_constructor, conversion, adl, unresolved). Each
asserts the inner call is callable-sourced AND properties_json carries that
strategy. Ties to repro_invariant_lsp_rescue (the exact-QN join can suppress a
correctly-emitted strategy).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 13:39:43 +02:00
Martin Vogel 64b7a1af38 test(repro): invariant lib + per-grammar battery (core compiled/OOP langs)
Foundation for the exhaustive all-grammar/all-LSP invariant suite:
- repro_invariant_lib.h: shared invariant helpers (extract-clean, label-valid,
  fqn-wellformed, range-valid, callable-sourcing split, dangling-edge count,
  lsp-strategy presence, target-QN-suffix).
- repro_grammar_core.c: full invariant battery for C/C++/CUDA/Rust/Go/Java/C#/
  Kotlin/Scala/Swift/ObjC/D — one TEST per language asserting extract-clean +
  valid labels/FQNs/ranges + defs-present + calls-extracted + callable-sourcing
  (Module-sourced==0) + no-dangling. Callable-sourcing reds are the known gap.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 13:15:21 +02:00
Martin Vogel b897d5db1e test(repro): deep invariant dives — enclosing-func drift, LSP rescue, discovery/FQN
Comprehensively reproduce the remaining QUALITY_ANALYSIS gaps:

- repro_invariant_enclosing_parity (gap #3): cbm_find_enclosing_func's hardcoded
  func_kinds_for_lang switch has drifted from lang_specs.function_node_types.
  Languages absent from the switch whose function nodes aren't in func_kinds_generic
  silently attribute every in-function call to Module. Full drift table +
  per-language reproductions (fortran/scss/sql/verilog/julia/nix RED via Module
  source; commonlisp/emacslisp/dart/cobol RED, some compounded by a callee-
  extraction gap).
- repro_invariant_lsp_rescue (gap #5/#5a): cbm_pipeline_find_lsp_resolution
  (lsp_resolve.h:65) joins LSP results to tree-sitter calls by EXACT
  caller_qn==enclosing_func_qn; when tree-sitter says Module, the LSP rescue is
  discarded. C++ out-of-line fixture asserts the edge is LSP-callable-sourced AND
  that properties_json preserves the lsp_* strategy/confidence (both RED today).
- repro_invariant_discovery_fqn (gaps #1,#4): comprehensive 50+ skip-dir table
  (.claude-worktrees RED, the rest GREEN guards) + 6 FQN same-stem collision cases
  (api.h/api.c + svc.h/svc.cpp RED; cross-dir/.d.ts/cross-package GREEN guards).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 12:59:54 +02:00
Martin Vogel c66719a7ce test(repro): large graph-quality INVARIANT test group
A systemic-invariant suite derived from the prior QUALITY_ANALYSIS (only 3.69% of
real-repo CALLS edges are Function/Method-sourced; the rest fall back to Module):

- repro_invariant_calls: source-position-aware CALLS attribution per language —
  a call inside a function body must be sourced at Function/Method, never Module.
  C/C++/Rust/Java/C# RED (Module fallback + LSP-rescue join too strict); Go/Python
  GREEN guards.
- repro_invariant_graph: discovery hygiene (.claude-worktrees must be skipped —
  RED), FQN same-stem distinctness (api.h vs api.c collide via strip_ext — RED),
  no-dangling-edges (GREEN integrity guard), Perl enclosing-func parity
  (func_kinds_for_lang lacks subroutine_declaration_statement — RED).
- repro_invariant_breadth: 26-language table asserting in-body calls are
  callable-sourced (~12 RED incl. r/julia + the known-gap langs, ~14 GREEN guards).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 12:15:33 +02:00
Martin Vogel d2ec388e89 test(repro): memory-budget #363 + bounded RSS-soak #581
- #363: the cgroup CPU axis was fixed in v0.8.0 (detect_system_linux reads
  cpu.max/cfs_quota), but there is no user-controllable memory ceiling —
  cbm_mem_init derives the budget from host/cgroup RAM with no override. Test
  sets CBM_MEM_BUDGET_MB and asserts cbm_mem_budget honors it; RED because that
  env knob is not read (mirrors the open ask from the issue thread; note this is
  the memory-override remainder, adjacent to enhancement #580).
- #581: bounded RSS-growth reproduction for the long-running leak — repeats a
  query op ~150x and asserts current RSS (cbm_mem_rss / /proc/self/statm) stays
  within 3x of the warmup baseline. Documents flakiness + that a slow per-op leak
  may not trip the bounded threshold (then a true multi-hour soak is needed).

#513 (Windows stdio handshake hang) is NOT reproducible in the unit-test runner:
it is Windows-only WaitForSingleObject code in cbm_mcp_server_run. It needs the
prod cbm binary built on windows-latest + a stdio-driver test that sends an
initialize request, keeps stdin open, and asserts a response before EOF. Deferred
as a board extension (documented in private/BUG_REPRO_PLAN.md).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 10:17:41 +02:00
Martin Vogel 51cc7d8b12 test(repro): #548 lighter harness, strengthen #56, + 3 NEW bugs
- #548: reworked off the live-HTTP-server draft — calls cbm_is_dir (the exact
  function handle_browse gates on) with a backslash path + the drive-root parent
  strrchr logic; no sockets/threads. RED on current code.
- #56: was a false-pass (bare-name resolver routed crate_a::helper to the only
  'helper' candidate). Added a second local helper to force ambiguity and now
  assert a CALLS edge whose target QN is in crate_a's namespace. Genuinely RED.

NEW bugs found by the discovery sweep (each a RED reproduction, root-cause traced):
- repro_new_ts_class_field_arrow: TS class-field arrow methods (handler = () => {})
  are never emitted as Method defs and inner calls mis-attribute to the class QN
  (extract_class_methods + resolve_toplevel_arrow_name ignore public_field_definition).
- repro_new_py_tuple_unpack: 'x, y = f()' yields no Variable defs — extract_vars
  only handles an identifier 'left', not pattern_list.
- repro_new_cypher_limit_zero: 'LIMIT 0' returns ALL rows — limit==0 is used as
  both the unset sentinel and a valid value; three guards check limit>0.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 09:55:14 +02:00
Martin Vogel 8bf892cd8f test(repro): GLR blowup #471 + opencode PATHEXT guard #221
- #471 (RED): deeply-nested ambiguous Perl call chain f(f(f(...))) makes the GLR
  stack-merge O(n^2) (stack_node_add_link recurses over all shared heads; the
  #461 recursion-depth cap bounds stack depth, not total iterations). Reproduced
  in a forked child with alarm(15) at depth 5000; asserts the child is not
  signal-killed (RED when the quadratic blowup blows the time budget).
- #221 (GREEN guard): cbm_find_cli now probes Windows PATHEXT (.exe/.cmd/.bat/.ps1,
  commit 0485d3f), so opencode is resolvable. Guards that fix on a fake
  opencode shim placed on PATH. Candidate to close after reporter retest.

(#548 held: its agent draft spins up a live HTTP server in a thread to exercise
handle_browse; reworking to a lighter harness before adding.)

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 07:01:59 +02:00
Martin Vogel 806da45896 test(repro): more install/UX + persistence reproductions for #607, #403, #434
- #607 (RED, DATA LOSS): cbm_cmd_install prints 'must be rebuilt' then calls
  cbm_remove_indexes() which unlink()s every .db in the cache and never rebuilds
  — re-running install destroys the user's index. Asserts the DB survives.
- #403 (RED): cbm_should_skip_dir excludes neither Antigravity / Programs /
  AppData, and the .gitignore path is git-gated, so an IDE install tree is fully
  indexed. Asserts a sentinel under the install dir is not discovered.
- #434 (RED): the incremental dump_and_persist only re-exports the artifact when
  one already exists and never receives the persistence flag, so persistence=true
  is silently dropped on first index. Asserts the artifact exists after one
  index_repository with persistence=true.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 01:49:57 +02:00
Martin Vogel ed15d0611a test(repro): install/UX reproductions for #570, #409, #431
In-process via cli.h + a temp HOME + the cbm_build_install_plan_json dry-run
oracle (no real filesystem mutation of the user's config):

- #570 (RED): install_cli_agent_configs hardcodes the Codex hook target to
  ~/.codex/config.toml and never checks for ~/.codex/hooks.json, so the hook is
  planned into config.toml even when hooks.json is in use (dual registration).
- #409 (GREEN guard): the legacy blocking PreToolUse gate regression is already
  fixed (cbm_install_hook_gate_script writes the non-blocking hook-augment shim);
  this guards the upgrade/overwrite scenario no existing test covered. Issue is a
  candidate to close after reporter retest.
- #431 (RED): the VSCode install path computes only Code/User/mcp.json and has no
  profile-aware API, so Code/User/profiles/<id>/mcp.json is never written; the
  plan omits the profile path entirely.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 01:43:54 +02:00
Martin Vogel 78d1aff20d test(repro): Rust trait-method extraction depth for #333
push_nested_class_nodes (extract_defs.c) re-queues only class-like/field/decl
children of a trait body — never function_item / function_signature_item — so
abstract + default trait methods are dropped from the graph (a major source of
the shallow-Rust-index degradation). impl-block methods (separate code path) are
extracted, serving as the positive control. Asserts all three trait-body methods
appear as defs; RED on current code.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 01:27:20 +02:00
Martin Vogel 8c35d5e2f4 test(repro): data-loss + watcher reproductions for #557, #520
- #557 (DATA LOSS): resolve_store() in mcp.c unlink()s the user's DB (+wal/+shm)
  with no backup when cbm_store_check_integrity() returns false — e.g. a project
  row whose root_path fails the path-shape SQL check. The test plants root_path
  "826" (from the issue evidence) under a CBM_CACHE_DIR temp dir, calls a tool
  that routes through resolve_store, and asserts the DB still exists OR a backup
  was made (RED: it is silently deleted).
- #520: detect_changes runs 'git diff', which never lists UNTRACKED new files, so
  a newly-created file is invisible until a manual re-index. Indexes a git repo,
  creates an untracked file, calls detect_changes, asserts the new file appears.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 01:19:33 +02:00
Martin Vogel 2534f3bb89 test(repro): crash + trace + gitignore reproductions for #627, #514, #510
- #627: query_graph crash — integer overflow in cypher.c cross_join_with_rels
  (node_count^2 * growth overflows int -> tiny malloc -> OOB write) on an
  OPTIONAL MATCH with an already-bound terminal node. Reproduced in a FORKED
  child (crash must not kill the runner); asserts the child is not signalled.
- #514: trace_path data_flow mode drops argument expressions — cbm_edge_info_t
  carries no properties_json, so the CALLS-edge arg text never reaches the MCP
  JSON. Asserts the arg expression appears in the data_flow response.
- #510: non-root .gitignore ignored — try_load_nested_gitignore() bails when the
  walk frame prefix is empty, so a directory's own .gitignore is never loaded
  when indexing a non-git-root subtree. Drives cbm_discover() directly and
  asserts an explicitly-ignored file is excluded.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 01:09:10 +02:00
Martin Vogel a8e274ce04 test(repro): fix harness include + #408 comment; add #571, #523, #546
Build fixes (wave 2 broke on all platforms):
- repro_harness.h: include <pipeline/pipeline.h> for cbm_project_name_from_path
  (-Werror=implicit-function-declaration).
- repro_issue408.c: the doc comment contained '["packages/*"]' whose '/*' opened
  a nested block comment (-Werror=comment); reworded.

New reproductions:
- #571: cbm_project_name_from_path strips CJK (per-byte [A-Za-z0-9._-] filter
  rewrites every UTF-8 continuation byte to '-'); a purely-CJK trailing path
  segment vanishes. Asserts the name is not the ASCII-only truncation.
- #523: cross-repo HTTP_CALLS — unindexed-lib guard in pass_calls.c drops the
  client call before the HTTP_CALLS edge is emitted, so cbm_cross_repo_match
  returns http_edges==0 for a byte-identical call/route. Asserts >=1.
- #546: trace_path splits a symbol duplicated by an ambient .d.ts into two nodes;
  inbound traversal walks only one, dropping callers by import style. Asserts
  BOTH the relative-import and alias-import callers appear.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 00:58:16 +02:00
Martin Vogel a84c59c27a test(repro): cross-file + trace reproductions for #408, #56, #480
First users of the shared repro_harness.h (validates the multi-file + MCP-tool
harness end to end):

- #408: STRONGER than the existing weak test_lang_contract.c guard (which any
  IMPORTS edge satisfies) — fixture has zero relative imports + no dependencies
  field, so the only possible IMPORTS edge is the workspace cross-package one
  packages/b -> @org/a; asserts >=1.
- #56: Rust multi-crate workspace; crate_b::run calls crate_a::helper; asserts
  CALLS >= 2 so an intra-crate edge can't mask the missing cross-crate edge.
- #480: precondition asserts CALLS>0 (edges exist), then drives the trace_path
  MCP tool and asserts the caller appears + the result is not the empty
  "callers":[] shape — isolating it as a traversal bug, not extraction.

All assert correct behaviour; RED on current code (board verifies).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 00:47:54 +02:00
Martin Vogel 8423b0b274 test(repro): -Itests fix + reproductions for #495, #521, #382
- Makefile: add -Itests to the test-repro-runner compile so repro files in the
  tests/repro/ subdir resolve "test_framework.h" (they sit one dir deeper than
  the existing tests/*.c, which find it relative to their own directory).
- #495: cfg-gated twin functions collapse — two mutually-exclusive #[cfg] Rust
  fns get identical qualified_name and the UNIQUE(project,qualified_name) upsert
  overwrites one; asserts the twins get distinct QNs.
- #521: Route nodes minted from URL string literals in infra/config files
  (try_upsert_infra_route has no source-vs-config guard); indexes a YAML-only
  fixture and asserts zero Route nodes.
- #382: Java class-level + marker_annotation decorators dropped; asserts @Entity/
  @RestController on the class and @Override on the method (strengthens the weak
  existing #382 method-only test).

All three assert correct behaviour and are RED on current code.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 00:25:40 +02:00
Martin Vogel 6e70a8b019 test(repro): scaffold cumulative bug-reproduction suite + cross-platform board
Stand up a separate, non-gating reproduce-first suite that holds one RED case
per open bug issue (the redness is the deliverable + the regression guard):

- tests/repro/ + repro_main.c -> `make test-repro` (its own runner with its own
  main + counters; deliberately EXCLUDED from ALL_TEST_SRCS so the gating
  `make test` / ci-ok required check stays green and PRs are not wedged)
- repro_extraction.c: first reproduction, #554 -- a C++ out-of-line method's
  inner CALLS edge must attribute to the class-qualified Method QN, not the
  Module; ties the call's enclosing_func_qn to the method definition's own
  qualified_name so a class-qualifier drop (the live root cause) fails it
- scripts/repro.sh: build+run the board; a build/link failure fails the job,
  while expected test redness is reported as the board state (job stays green)
- .github/workflows/bug-repro.yml: workflow_dispatch (platform filter) + qa/**
  push; runs the board on linux x2 / macos x2 / windows so many bug vectors can
  be reproduced on many platforms at once

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-06-26 00:09:23 +02:00
Martin Vogel 008f6e3873 Merge pull request #506 from imateusdev/fix/route-param-canonicalization
fix(routes): canonicalize path parameters so client calls match handlers (#505)
2026-06-23 23:08:05 +02:00
imateusdev 8b813675be fix(routes): canonicalize path parameters so client calls match handlers
Route QNs (__route__METHOD__/path) are the rendezvous key for joining a client
HTTP_CALLS to the server handler that serves it, but they were built from the raw
path. Frameworks write path parameters with different placeholder syntaxes
(":id" Express/clients, "{id}" Axum/Spring/OpenAPI, "<id>" Flask/Rocket), so a
client call and its handler produced different QNs and never joined. Static
routes joined; parameterized routes (most of a REST surface) did not.

Add cbm_route_canon_path() which collapses each parameter token (":name",
"{name}", "<name>", "${...}") to a single "{}" token, and apply it to the path
at every HTTP-route QN construction site (pass_route_nodes, pass_parallel,
pass_calls, pass_cross_repo). Broker/async/infra QNs are left untouched. Only the
QN (identity/dedup key) is canonicalized; the Route node display name keeps the
original path. Parameter names are intentionally discarded so the same logical
endpoint matches across services that name the variable differently.

Adds tests/test_route_canon.c (11 cases). Fixes #505.

Signed-off-by: imateusdev <imateusdev@gmail.com>
2026-06-23 13:52:08 -03:00
Sam Li 559993a853 Add post-dump plausibility gate returning status:degraded (#334)
Compare persisted SQLite node counts to in-memory dump counts after
index_repository completes so partial WAL/durability loss surfaces as
status:"degraded" instead of silent indexed.

Signed-off-by: Sam Li <yangsec888@gmail.com>
2026-06-23 06:38:45 +09:00
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