Per-case-verified callable-sourcing fixes (AST-confirmed via a standalone dumper).
PRODUCTION BUGS:
- Clojure/Scheme/Racket: [CBM_LANG_*] had empty function_node_types, so no
SCOPE_FUNC was pushed and every in-body call sourced to Module. Wire
clojure_func_types={"list_lit"}, scheme/racket={"list"}. Since these kinds also
match plain call forms, gate def-vs-call in compute_func_qn (extract_unified.c,
which has ctx->source; the shared resolver does not): compute_lisp_func_qn
returns the def name only when the list head is a def-form (defn/define/...),
NULL otherwise, so non-def lists like (add x 1) push no scope.
- Elixir: def/defp/defmacro are `call` nodes (so are in-body calls); the shared
resolver returned NULL for them. compute_elixir_func_qn names a def call (gated
strictly on the def-macro target) and returns NULL for non-def calls.
BROKEN FIXTURE (production correct) -- Starlark: extraction works; the in-body
go_binary(...) callee is an EXTERNAL undefined rule, so it correctly yields no
CALLS edge (unresolved callee -> no edge, universal behavior). Added a same-file
_base_deps() def called inside make_binary's body for a resolvable in-body call
-> Function-sourced edge. go_binary(...) kept (dim-6 callee assertion preserved).
Reproduced by repro_grammar_{functional,scripting,config} (Clojure/Scheme/Racket/
Elixir/Starlark callable-sourcing were RED). AWK (rule-naming) + Dart (structural)
deferred to focused tasks.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Per-case verification (AST-confirmed) of three RED expression-config-language
reproductions, distinguishing genuine production bugs from broken fixtures.
PRODUCTION BUG — Nickel (def + call were not extracted):
- nickel_func_types "fun" -> "fun_expr": the bare `fun` matched only the keyword
token, never a node; the lambda node is `fun_expr` (anonymous), whose name is on
the enclosing let_binding's `pat` field, so cbm_resolve_func_name climbs to the
let_binding to name it (anonymous lambdas self-filter).
- nickel_call_types "infix_expr" -> "applicative": infix_expr is binary-operator
application (`a + b`); the real call `f x y` is a curried `applicative`. New
extract_nickel_callee emits one edge per curried call (requires a `t2` arg,
outermost applicative only), guarded against the pass-through applicative
wrapper that surrounds every Nickel value.
BROKEN FIXTURES (production correct) — the callable-sourcing invariant requires
module_sourced==0, but these fixtures called functions at the TOP LEVEL (the
output expression), which is legitimately Module-sourced. Each fixture corrected
so its only call sites are INSIDE a function body and the output merely REFERENCES
the function; all def/call assertions preserved (makeServer/addPort/go_binary defs
+ their calls remain):
- jsonnet: output `server: build`, `local build(host)=makeServer(host,8080)`.
- nickel: output `make = mkServer`; `addPort port 0` stays in mkServer's body.
- starlark: `default_rule = make_binary`; go_binary stays in make_binary's body.
Reproduced by repro_grammar_config (jsonnet/nickel/starlark callable-sourcing +
nickel def/call were RED).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- Jsonnet: a function binding is a `bind` node with the name on the `function`
field and a `params` field (value binds have no params -> skipped); add "bind"
to jsonnet_func_types and resolve its name. Call: `functioncall` callee = first
`id` child.
- Typst: `#let f(x) = ...` is a `let` whose `pattern` is a `call`; the name is
that call's `item` field (value `#let x = 1` has a non-call pattern -> skipped).
Add "let" to typst_func_types. Call: `call` callee = `item` field.
- Meson: meson_call_types listed phantom node kinds ("function_expression",
"command") that do not exist in the grammar; the real builtin invocation is a
`normal_command` whose `command` field is the callee. Replace with the real
node + resolve the callee.
Reproduced by the repro_grammar_{config,markup,build} suites (jsonnet/typst defs
+ jsonnet/typst/meson calls were RED).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Pony `fun`/`be`/`new` (method/constructor/ffi_method) live in pony_func_types,
so the main def-walk extracts them via extract_func_def and labelled every one
"Function" — even those declared inside a class/actor/struct/trait/interface/
primitive, which are methods. Detect the enclosing class-like ancestor (via the
spec's class_node_types) and promote such defs to "Method" with a parent_class
link, mirroring the Go-receiver and C++ out-of-line Method paths. Free functions
(no class-like ancestor) stay "Function".
Reproduced by repro_grammar_systems_pony ("no def labelled Method", was RED).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- GraphQL/Prisma/Smali: extract schema fields as "Field" defs (field_definition /
column_declaration), gated per-language; recognise their class-body nodes
(fields_definition / statement_block) via per-language cases so the common
"statement_block" kind never hijacks another language's class body.
- Pony: class members (method/constructor/ffi_method) have no name field; the
name is the first identifier child -> "Method" defs.
- PKL: clazz name is an identifier child -> "Class" def.
- Dockerfile: ENV/ARG names from env_pair name field / arg_instruction -> Variable.
- CSS: call_expression callee is a function_name child (e.g. url()).
- TLA+: add bound_op to tlaplus_call_types so operator applications resolve.
Reproduced by the repro_grammar_* suites (were RED for missing defs/calls).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The return-clause `limit` was zero-initialized (calloc) and every guard tested
`limit > 0`, so an explicit `LIMIT 0` was indistinguishable from "no LIMIT
clause" and returned ALL rows. Standard Cypher treats `LIMIT N` as an upper
bound, so `LIMIT 0` must return zero rows.
Default `limit` to the sentinel -1 ("no limit"); the parser still sets 0 for an
explicit `LIMIT 0`. Change the truncation guards (rb_apply_skip_limit,
bindings_skip_limit) and the apply call to `limit >= 0`, so limit 0 truncates to
0 while -1 returns all. The proj_cap optimization stays `> 0` (LIMIT 0 falls
through to full projection, then the final truncation yields 0).
Reproduced by repro_new_cypher_limit_zero (was RED): `RETURN ... LIMIT 0` must
yield row_count == 0.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Several grammars produced no Function def and/or no CALLS edge because the
name/callee is not in the standard `name`/`function` field. Add precise
per-grammar extraction (verified against the actual tree-sitter parse trees):
- SCSS: function_statement/mixin_statement name is a `name` CHILD (not a field);
the call node is `include_statement` (callee = its identifier child). Also fix
scss_call_types, which listed "call_expression" instead of include_statement.
- SQL: create_function / invocation carry the name on a nested
object_reference > `name` field.
- COBOL: call_statement's callee is its `x` string-literal (program name),
dequoted.
- Elm: value_declaration name is functionDeclarationLeft > lower_case_identifier;
function_call_expr callee is target > value_expr > name (value_qid).
- Teal: the `local function foo()` form carries the name on a `function_name`
child rather than the `name` field.
Reproduced by the repro_grammar_* and repro_invariant_enclosing_parity suites
(SCSS/SQL/COBOL/Elm/Teal), which were RED for missing defs/calls.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The LSP-resolution join compared the LSP's class-qualified callee short-name
(e.g. "inc" from "Counter.inc") against the call's RAW callee_name, which is
receiver-qualified for method/qualified calls ("c.inc", "A.Helper", "c.Add").
That comparison always missed for method dispatch, so every type-aware LSP
resolution (lsp_type_dispatch, cs_static_typed, lsp_method_dispatch, ...) was
silently dropped and the call fell through to the weaker textual registry
resolver — the LSP strategy never reached the CALLS edge's properties_json.
Reduce the call's callee_name to its bare last dot-separated segment before the
comparison, mirroring what is already done to callee_qn. The LSP has already
performed receiver->type resolution, so matching bare method names is correct;
free-function calls (already bare) are unaffected. This lets the more accurate
type-aware LSP resolution win over textual registry matching for method calls.
Reproduced by the repro_lsp_* per-pass strategy suites (java/cs/go/rust/kotlin/
php/ts), which asserted the LSP strategy is present on a CALLS edge and were RED.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The unified/calls extractor used a private 4-case name resolver
(resolve_func_name_node) that returned NULL for the ~130 grammars whose
function node has no `name` field — Fortran subroutine, SCSS mixin, SQL
create_function, Julia short-form, the Lisp/FP family, and more. On NULL,
push_boundary_scopes never pushed a SCOPE_FUNC, so every call inside such
a function was attributed to the enclosing Module instead of the callable
(QUALITY_ANALYSIS gap #3).
Export the rich resolver from extract_defs (cbm_resolve_func_name) — a
strict superset that already drives definition extraction (generic name
field, arrow/declarator, C/C++ declarator chain, plus the per-language
quirks) — and route the calls scope path (compute_func_qn) through it,
preserving the enclosing-class QN join. Definition extraction is
unchanged (it already called this resolver); the two paths now share one
source of truth, the same de-drift pattern used for
cbm_resolve_c_declarator_name_node (#438).
Reproduced by repro_invariant_enclosing_parity (Fortran, SCSS, SQL,
Verilog, Julia, Nix, CommonLisp, EmacsLisp, Dart, COBOL): each asserts no
CALLS edge is Module-sourced and was RED; the fix makes them
callable-sourced.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Data-loss fix: resolve_store() unlink()'d a project DB (+WAL/SHM) when
cbm_store_check_integrity() failed, destroying the user's graph with no recovery.
Rename the DB to <path>.corrupt instead (clearing any prior backup first so rename
succeeds on Windows; fall back to unlink only if rename fails). Re-index rebuilds a
fresh DB. Flips repro_issue557 green (DB preserved / backup exists).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- repro_main: RUN_SUITE honors CBM_REPRO_ONLY (comma list of suite-name
substrings) for fast targeted validation of a single fix.
- fast-repro.yml: ASan single-platform (the no-sanitizer build crashed some
suites); single-platform is the speedup vs the 5-platform board.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- repro_main.c: redefine RUN_SUITE to print '[SUITE] <name> P passed, F failed'
so board/fast-lane output is greppable for which suites still have reds.
- bug-repro.yml: exclude qa/fast-** / qa/soak-** / qa/smoke-** from the board
push trigger (those branches run only their dedicated lane workflow).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Avoids waiting ~15min for the full 5-platform ASan board just to see whether a
fix dropped the red count. Pushing a qa/fast-** branch builds+runs test-repro on
ubuntu-latest without sanitizers (~5min). The full bug-repro.yml board stays the
comprehensive all-platform check.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The doc comment literally contained a block-comment opener while explaining the
no-nested-comment rule, breaking the test-repro build on all platforms. Reword.
(make test was unaffected — repro files are not in ALL_TEST_SRCS — but the board's
test-repro runner failed to build, blocking all board validation.)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
QUALITY_ANALYSIS gap #1: .claude-worktrees was indexed (nearly tripling this
repo's indexed surface + polluting queries with stale duplicate symbols).
Discovery skipped .claude and .worktrees but not .claude-worktrees. Add it to
ALWAYS_SKIP_DIRS. Flips the discovery-hygiene invariant green.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Root cause of the systemic callable-sourcing gap (QUALITY_ANALYSIS gap #3; only
3.69% of real-repo CALLS were Function/Method-sourced): func_kinds_for_lang fell
back to func_kinds_generic for the ~130 languages without a curated entry, missing
their real function node types, so cbm_find_enclosing_func never found the parent
function and attributed in-body calls to the Module node.
Fix: in the default case, use the language spec's function_node_types (the single
source of truth extraction already uses) when present. Curated languages (Go/
Python/C/C++/Rust/Java/...) are unchanged — no regression to the green gate.
Flips the callable-sourcing + enclosing-parity reproductions for the drifted
languages (dart/perl/scss/nix/fortran/cobol/verilog/vhdl/...) toward green.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
dry-run.yml and release.yml now pass broad_platforms:true to _test.yml + _smoke.yml,
which add (via a dynamic setup-matrix job) ubuntu-22.04 (older glibc / AlmaLinux
class), ubuntu-22.04-arm, macos-15, windows-2025, windows-11-arm on top of the core
set — a broader 'does it run everywhere' picture. The PR gate (pr.yml) and the
shipped release-binary targets (_build.yml) are unchanged. Broad-only legs are
tagged optional + continue-on-error, so a flaky/less-common runner is visible but
never blocks a release.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
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>
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>
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>
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>
The multi-platform smoke run flagged index-cli + index-status as empty/not-ready
even though the binary indexed fine (nodes>0, status ready) — the MCP tool result
wraps its payload as a JSON STRING with escaped quotes (\"nodes\":N), but the
checks grepped for unescaped "nodes":N. Strip backslashes/quotes before matching
and tolerate the nodes= log form. (Binary was healthy; this was a script-parsing
gap the wide-matrix smoke surfaced.)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- scripts/smoke-invariants.sh: 30-check battery against the PROD binary —
--version/--help, MCP initialize handshake with stdin OPEN (#513), tools/list
(all 14), EVERY tool invocable with valid JSON-RPC + no crash, index→non-empty
graph, malformed-input resilience (bad JSON / empty / huge line / binary /
non-UTF8 / missing path), clean EOF exit, shared-lib resolution, install
dry-run. Bounded waits (read -t / timeout), no sleep loops; msys2-safe.
- .github/workflows/smoke.yml: runs it on the WIDEST runner matrix — ubuntu
22.04+24.04 (x64+arm64; 22.04 = older glibc / AlmaLinux class), macos
14/15/15-intel, windows 2022/2025 + windows-11-arm (experimental). A FAIL on
any platform is a binary a user would receive. workflow_dispatch + qa/smoke-**.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
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>
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>
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>
#581 explicitly crashes Windows (50+ GB virtual memory → crash), so Windows is
the most important soak target — the earlier 2-platform cap (ubuntu+macos) missed
exactly where the bug manifests. Expand to the full matrix: linux amd64+arm64,
darwin arm64+amd64, and a windows-latest msys2 job (mirrors _soak.yml's windows
build + .exe binary-path detection). All legs run the query-leak mode, 320-min
budget.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
timeout-minutes is evaluated at workflow setup, where the inputs context is null
on push events; fromJSON(inputs.duration_minutes || '240') + 60 was a startup
failure (0 jobs), so the soak never ran on qa/soak-** either. Use a fixed 320-min
budget (covers the 240-min default soak + build + analysis).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- _soak.yml: timeout-minutes was 30 (soak-quick) / 45 (asan) while nightly passes
duration_minutes=240 — every 'nightly 4h soak' was silently KILLED at 30 min and
never ran multi-hour. Raise to 300/60 so the soak can actually complete.
- soak-test.sh: add CBM_SOAK_MODE=query-leak (default unchanged). It indexes once
then hammers read-only tools (search_graph/query_graph/trace_path/
get_code_snippet/search_code) with NO reindex/mutation — so index_repository's
cbm_mem_collect never runs to sweep the query-only leak #581 implicates. The
existing RSS ceiling/slope/ratio checks become the #581 detector.
- soak.yml: workflow_dispatch (duration_minutes, mode) + push to qa/soak-** ;
builds the prod binary and runs the soak with timeout = duration + 60, on
ubuntu + macos. Pushing a qa/soak-* branch starts a real multi-hour run.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
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>
- #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>
build_perl_nested_calls is only used in the POSIX fork/alarm branch; on Windows
the test body is SKIP_PLATFORM, so the helper was unused and tripped
-Werror=unused-function (windows-only board failure). Mark it unused.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- #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>
- #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>
- #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>
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>
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>
- #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>
- #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>
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>
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>
A reproduction fails its assertion and returns before cleanup, so LeakSanitizer
flagged benign harness leaks on every red store-level test and _exit()'d,
swallowing the unflushed summary — repro.sh then misread it as a build failure
(Linux only; macOS/Windows have no LSan). Fixes:
- repro.sh: export ASAN_OPTIONS=detect_leaks=0 for the board run. Leak-cleanliness
is not the board's signal (the RED rows are); the #581 leak bug gets a dedicated
RSS-growth test. ASan's real checks (use-after-free, overflow) stay enabled.
- repro_main.c: setvbuf(stdout, _IONBF) so the summary + RED rows survive any
abnormal _exit (sanitizer or crash).
- add tests/repro/repro_harness.h: shared multi-file index + store-query + fork
crash-detector helpers (ported from the proven test_lang_contract.c harness)
for the cross-file / store-level / crash reproduction waves.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- 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>
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>
After the C declarator-name walker was de-duplicated into one shared helper
(cbm_resolve_c_declarator_name_node), the C/C++ enclosing-function resolver now
resolves qualified names (Foo::bar) via resolve_qualified_name() and no longer
treats type_identifier as a terminal name. Add reproduce-first coverage so the
#438 fix cannot silently regress on the qualified-declarator path:
- cpp_out_of_line_method_caller_attribution: a call inside `void Foo::bar()` must
attribute to the method, not the module.
- cpp_out_of_line_ctor_dtor_caller_attribution: calls inside `Foo::Foo()` and
`Foo::~Foo()` must attribute to the special member, not the module.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>