Commit Graph
4377 Commits
Author SHA1 Message Date
Martin Vogel 8af291deb4 Merge branch 'main' into fix/2433-server-discover-probe-log 2026-10-20 06:00:20 +02:00
Martin Vogel 4c80d89edf Merge pull request #2713 from DeusData/fix/py-namespace-effect-allowlist
fix(python): a top-level call of a harmless same-module function keeps the package namespace
2026-10-20 04:54:32 +02:00
Martin Vogel 5a4af9c46f Merge pull request #1913 from ilyabrykau-orca/fix/go-method-receiver-qn
fix(extract): receiver-qualify Go method QNs
2026-10-20 04:16:17 +02:00
Martin Vogel 171f99b698 Merge pull request #2714 from DeusData/feat/hooks-graph-follow-up-hint
feat(hooks): name the graph follow-up tools in every search augmentation
2026-10-20 03:28:57 +02:00
Martin Vogel 8781bbd29f Merge origin/main into fix/py-namespace-effect-allowlist
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-20 00:26:27 +02:00
Martin Vogel 28a6905c07 Merge pull request #2712 from DeusData/fix/py-namespace-string-future-facts
fix(python): docstrings and __future__ imports are no namespace barriers
2026-10-20 00:25:58 +02:00
Martin Vogel d21152603c Merge pull request #2707 from DeusData/fix/data-flows-test-file-scope
fix(routes): scope DATA_FLOWS pairing at the test-file boundary (R1 + R2)
2026-10-19 23:55:09 +02:00
Martin Vogel c52ca5d34c feat(hooks): name the graph follow-up tools in every search augmentation
When an agent greps for a symbol that the graph knows, the PreToolUse
augmentation (Grep, Glob and Bash searches) lists the matching symbols. It now
closes with one line that is cbm's own statement, outside the untrusted symbol
block, naming the graph tools for the usual next question:

  [codebase-memory] For these symbols, trace_path lists callers and callees,
  get_code_snippet returns the exact source, and search_graph finds related
  symbols by name.

The line carries no repository-derived value and is stated as facts, as Claude
Code asks of hook context. It is appended at the string's real end into
reserved room: the list loop's offset can run past its cap when a row is
truncated, so appending at that offset could have written past the buffer.
Searches without a hit, errors and non-search events are unchanged.

Refs #509

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 23:52:05 +02:00
Martin Vogel cd16ef8046 Merge fix/go-weak-selector-calls (#1907, with main 32445b20) into fix/go-method-receiver-qn
Brings main 32445b20 in through the refreshed #1907. No conflicts.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 22:29:34 +02:00
Martin Vogel 6856f09705 Merge origin/main (32445b20) into fix/go-weak-selector-calls
Conflict in tests/test_pipeline.c: this branch and #2703 (on main) each
registered two Go tests at the same place in the suite list; both pairs
are kept.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 22:29:14 +02:00
Martin Vogel 170fad6b65 Merge fix/py-namespace-string-future-facts (#2712) into fix/py-namespace-effect-allowlist
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 22:26:49 +02:00
Martin Vogel 32445b2001 Merge pull request #2706 from DeusData/fix/php-trait-flatten-index
fix(php): index trait methods by receiver while flattening (#1265)
2026-10-19 22:26:45 +02:00
Martin Vogel efe007ea1b Merge pull request #2708 from DeusData/feat/mcp-profile-instructions
feat(mcp): state the structural questions in the scout and analysis profiles
2026-10-19 21:58:51 +02:00
Martin Vogel 2248ae1f3c Merge pull request #2698 from DeusData/feat/mcp-tool-search-discovery
feat(mcp): load the structural tools upfront and say what the graph answers
2026-10-19 21:57:17 +02:00
Martin Vogel 2cda42e894 Merge remote-tracking branch 'origin/main' into fix/data-flows-test-file-scope
Conflict in tests/test_edge_types_probe.c: both sides added tests after
et_handles_exact (the R1 + R2 DATA_FLOWS tests and the #1692 ASP.NET
overload test); both are kept.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 07:50:13 +02:00
Martin Vogel 93724ba36b Merge main d4980ad7 into fix/py-namespace-string-future-facts
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 07:49:58 +02:00
Martin Vogel 5e345e32c0 feat(mcp): state the structural questions in the scout and analysis profiles
The read-only analysis profile and the fast scout profile serve the graph
subagents (codebase-memory-auditor, codebase-memory-scout). Their server
instructions listed tools only. Like the default profile, they now state as
facts which structural questions the graph answers (where a symbol is defined,
who calls a function, what it calls, and for analysis also call chains, data
flow and change impact), keywords first: analysis 190 -> 648 characters, scout
199 -> 427, well within Claude Code's 4,096-character cut. The profile names,
check_index_coverage and the absence of index_repository are unchanged.

Stacked on #2698.

Refs #509

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 07:45:37 +02:00
Martin Vogel d4980ad757 Merge pull request #2705 from DeusData/fix/php-global-use-known-absent
fix(php): prove an unaliased global class use absent from the project
2026-10-19 07:44:57 +02:00
Martin Vogel 8d1815cb76 Merge remote-tracking branch 'origin/main' into fix/data-flows-test-file-scope
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 07:43:28 +02:00
Martin Vogel 6f00af3b7e fix(routes): scope DATA_FLOWS pairing at the test-file boundary (R1 + R2)
Design decision 2026-10-18 (R1 + R2): a handler defined in a test file
pairs only with callers in that same file, and a caller in a test file
pairs only with handlers in that same file. A test's request to the
application, a production caller matched to a test's mock server, and
the private mock servers of two tests pairing across files are not
data flows of the application. #2689 links every caller of a route to
every handler of it, so these pairs were made on main.

try_create_data_flow now refuses a pair whose caller or handler is in a
test file (cbm_is_test_file, the per-language rule extraction uses)
when the two files differ. HTTP_CALLS and HANDLES are unchanged; only
the pairing is scoped. The pass log counts the refused pairs
(skipped_test_scope).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 07:43:28 +02:00
Martin Vogel 30bd5aaa91 test(routes): test-file callers and handlers pair only within their file
Design decision 2026-10-18 (R1 + R2): a DATA_FLOWS caller in a test file
pairs only with handlers in that same file, and a handler in a test
file pairs only with callers in that same file.

infrascan: test callers by each language rule (a tests/ directory, a
Java *Test.java, a TS .spec.ts) get no flow to a production handler; a
production caller gets no flow to a test mock server; a test reaches
the mock server of its own file but not one in another test file.
Controls: a production caller keeps its flow, and so does a same-file
test pair.

edge_types_probe: a Spring controller, a mock controller under
src/test/, a production TS client and a TS .test.ts client meet on
the same __route__ANY__ nodes. Exactly one flow (production client to
production controller) on the sequential and on the parallel path.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 07:43:28 +02:00
Martin Vogel f26da1aaf2 Merge pull request #2688 from DeusData/fix/issue-1692-aspnet-routes-v2
fix(routes): extract ASP.NET Core attribute routes (#1692)
2026-10-19 07:43:12 +02:00
Martin Vogel 3c2853e338 Merge main 0f6cc06b into fix/py-namespace-string-future-facts (#2704 landed)
tests/test_pipeline.c conflict: main moved the surface codec to v3 (#2703), this branch moved the Python facts payload to v2; the codec rows carry both (surface v3, py v2), plus the stale py v1 row this branch added.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 07:21:57 +02:00
Martin Vogel 0f6cc06ba2 Merge pull request #2704 from DeusData/fix/py-namespace-reexports-v2
fix(python): resolve names re-exported through package namespaces
2026-10-19 07:20:14 +02:00
Martin Vogel ac78896253 test(python): an escaped string in __all__ is no effect
WHY: the multiple-bindings test pinned an UNKNOWN EFFECT fact for `__all__ = ['escaped\n']`, which only existed because the namespace facts proved plain one-line strings harmless and nothing else. A string literal, escapes included, evaluates nothing, which is this branch's rule, so the fact is gone. ALL_UNKNOWN stays: an escaped name is no literal export name. The assertion still pins the exact stream (13 facts, verified against an index of the same source with the base and this branch).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 06:28:24 +02:00
Martin Vogel 684dcf5447 Merge pull request #2703 from DeusData/fix/go-lsp-multi-return
fix(go-lsp): type variables bound by cross-file multi-value and named results
2026-10-19 06:11:30 +02:00
Martin Vogel 41232687dd Merge pull request #2691 from DeusData/fix/py-nested-class-qn-v3
fix(python): walk a class nested in a class under its outer class
2026-10-19 06:11:17 +02:00
Martin Vogel e9b334e7a1 Merge remote-tracking branch 'origin/main' into fix/php-trait-flatten-index
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 06:05:01 +02:00
Martin Vogel 1e644b4414 fix(python): bound the module-path scan of the __go import check
WHY: the check read an import's module path with strcspn, which runs to a NUL, but a tree-sitter source slice need not be NUL-terminated: the first segment is now found with memchr inside the slice. The leading dots move into py_ns_leading_dots; written inline, cppcheck 2.20 (the CI version) took the loop to always end at the slice length and reported three knownConditionTrueFalse findings on the branches that follow it.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:56:15 +02:00
Martin Vogel 90bb6845d6 fix(python): a top-level call of a harmless same-module function keeps the namespace
A top-level call withdrew the whole namespace (UNKNOWN, reason EFFECT): sqlalchemy's sql/__init__.py ends with __go(locals()) and lost all 108 re-exports. The call is now allowed when the callee is the latest binding of its name, a proven same-module function with no star import or unknown effect since; every argument is a safe expression or a bare locals()/globals() the module does not rebind; and the body (nested scopes included) has no global/nonlocal and never names a parameter, a function or class the module defined earlier, the module's own name, or a name that reaches a namespace (globals, vars, locals, exec, eval, setattr, delattr, __import__, sys, importlib, __name__, __dict__, __globals__, __builtins__, builtins, __self__, inspect, f_globals, f_locals, f_back, gc). Each submodule of the package the body imports becomes a package attribute and is shadowed at the call; in a plain module the imports install nothing. Every other call stays a whole-namespace effect. The function table and verdicts live in the extraction scratch; each callee is checked once.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:56:15 +02:00
Martin Vogel 3281626c60 style(test): clang-format the __go allowlist test
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:55:21 +02:00
Martin Vogel 74b115e929 test(python): a top-level __go(locals()) keeps the package namespace
sqlalchemy's sql/__init__.py ends with __go(locals()). The callee is a same-module function that never touches its parameter or the module's globals; only the submodules its body imports become package attributes. The extraction test pins the exact shape (the namespace survives, the imported submodules are shadowed at the call) and its controls: the body uses the parameter, global (also nested), globals(), sys.modules[__name__], the own package name, a same-module helper; an imported, rebound or not yet defined callee; an argument with an effect; a module-level locals. The pipeline fixture adds the same shape end to end, with a re-export the body's submodule import replaces and a __go that writes its parameter.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:55:21 +02:00
Martin Vogel 7dc07d97fd refactor(php): keep the trait index in the LSP arena
The class-field pass stored the address of a stack-local index in ctx for
the duration of the pass. It was restored before returning, but the
pattern is what cppcheck's autoVariables check rejects. The index now
lives in the call's arena; on an allocation failure it stays NULL and
flattening runs the full scan, as before.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:53:24 +02:00
Martin Vogel 4ae1909223 Merge remote-tracking branch 'origin/main' into fix/php-global-use-known-absent
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:47:30 +02:00
Martin Vogel bf56fcfd14 Merge #2704 head 67e53d0e into fix/py-namespace-string-future-facts (pick up #2697)
cbm.h conflict: #2700's cbm_import_binds_runtime_name helper and the facts version 2 comment are adjacent; both kept.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:40:35 +02:00
Martin Vogel 728bb3b822 fix(python): a star import from __future__ is a parse barrier
The grammar rejects from __future__ import * as Python does, so the file carries an error node and its whole namespace is a PARSE unknown: a stronger barrier than the UNSUPPORTED statement the previous commit guarded for. That guard could never run and is dropped; the control asserts the parse barrier the parser actually yields.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:13:48 +02:00
Martin Vogel 67e53d0ead Merge origin/main into fix/py-namespace-reexports-v2 (pick up #2697)
py_lsp.c: #2697 moved py_lookup_attribute below the C3 code (own members, C3 order, then the depth-first walk) and declares it up front; the trivial definition this branch still carried next to its module-member lookup is dropped, the module-member block is kept, and the C3 section takes main's header.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:07:32 +02:00
Martin Vogel 27c330324d Merge pull request #2697 from DeusData/fix/py-attr-c3-mro
fix(python-lsp): look attributes up along the C3 method resolution order
2026-10-19 05:06:00 +02:00
Martin Vogel 240b24ae82 fix(python): docstrings and __future__ imports are no namespace barriers
The namespace facts proved only plain one-line strings safe, so every triple-quoted string statement (a module, class or attribute docstring) was an unknown EFFECT, and from __future__ import x was UNSUPPORTED. A barrier withdraws every name bound before it: sqlalchemy's sql/expression.py lost its 149 re-exports to the docstring after nullsfirst = nulls_first, and a docstring made an otherwise plain class unproven. A string literal now evaluates nothing in any quoting or prefix (r, b, u, rb), implicit concatenations included; an f-string with a replacement field stays an effect (__format__/__str__ may run code). from __future__ import x [as y] binds like a from-import of module __future__; a star import from __future__ is a syntax error and stays unsupported.

CBM_PY_NAMESPACE_FACTS_VERSION moves to 2. The version travels in the lsp_surface py payload and a payload of another version does not decode, so the first incremental run on an index of an older binary falls back to a full re-index (delta.rehydrate_failed -> CBM_PIPELINE_FORCE_FULL_REINDEX) instead of mixing old and new facts. The codec test now spells the current version and rejects a version-1 payload.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 05:00:19 +02:00
Martin Vogel 35598e66fd test(python): docstrings and __future__ imports keep the package namespace
A string-literal statement has no effect and from __future__ import x binds x, but the namespace facts recorded both as unknown statements: a triple-quoted string as an EFFECT, a future import as UNSUPPORTED. sqlalchemy's sql/expression.py lost its 149 re-exports to a docstring after nullsfirst = nulls_first. The extraction test pins every string form (module, class and attribute docstrings, single and triple quotes, r/b/rb/u prefixes, implicit concatenation, an f-string without a replacement field) and the future import forms (alias, parentheses); controls: an f-string with a field (also inside a concatenation), a call, and a star import from __future__ stay barriers. The pipeline fixture adds the expression.py shape end to end and an f-string control package.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 04:57:30 +02:00
Martin Vogel 782ab752d9 Merge origin/main into fix/py-namespace-reexports-v2 (pick up #2700)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 04:47:30 +02:00
Martin Vogel e6b47a393b Merge pull request #2700 from DeusData/fix/py-type-checking-imports-v2
fix(python): resolve annotations through TYPE_CHECKING imports (#1277)
2026-10-19 04:46:55 +02:00
Martin Vogel 2cee7a12e2 Merge remote-tracking branch 'origin/main' into fix/issue-1692-aspnet-routes-v2
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 04:40:49 +02:00
Martin Vogel 9adada0185 fix(php): index trait methods by receiver while flattening (#1265)
flatten_trait_into_class looked up each trait's methods by scanning every
function of the registry, and every flattened method appends more of them.
A file with many trait-using classes paid O(classes x functions), and the
scan grew with its own output.

The class-field pass now keeps a receiver -> function-id index, synced
incrementally as functions are registered, and flattening reads only the
trait's own entries registered before the current class. If the index
cannot be built, the previous full scan runs unchanged. Only entries
registered before flattening started are visited, so the output is
identical.

Tests: the work-counter tests for #1265 (100 -> 200 trait-using classes,
ratio gate 2.5) and #765 (trait collision shapes stay bounded) use the
existing test-only work ceiling.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 04:39:09 +02:00
Martin Vogel 4527fc731a Merge pull request #2702 from DeusData/fix/php-single-file-registry-finalize
fix(php): index the single-file LSP type registry
2026-10-19 04:37:17 +02:00
Martin Vogel 7b5e680f33 Merge origin/main into fix/py-namespace-reexports-v2 (pick up #2680, #2690, #2695, #2696)
py_lsp.c: the module-member lookup of this branch and the C3 super() lookup of #2696 were added at the same place; both are kept.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 03:59:33 +02:00
Martin Vogel 0ae936f053 fix(python): a per-file def selection follows package re-exports
A small incremental closure resolves each changed file through a per-file
selection of defs (its own and its imported modules') instead of the shared
registry. A package that re-exports a name keeps only the record there; the
definition lives in the module the record points at, possibly through more
re-exports, so a call through the package found nothing after an
incremental re-index of the caller alone. cbm_pxc_filter_defs_for_file now
follows every selected re-export or star record of a Python module to the
module it names (relative exactly, absolute under the longest matching
prefix, a member as a submodule) and selects that module's defs too, once
per record, so the per-file LSP folds the same chain the shared registry
does.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 03:49:49 +02:00
Martin Vogel 9b2ad49dbc test(python): package re-exports survive an incremental re-index
The re-export records of a package come from its namespace facts. An
incremental re-index that touches only the caller must still find them for
the unchanged packages (restored from their stored facts). Both drivers now
also run as a full index followed by an incremental re-index of app/main.py
alone, with the same expectations.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 03:38:19 +02:00
Martin Vogel bec015461c fix(go-lsp): split the raw Go result list, keep a single result raw
The first D1 change fed the Go cross-file registrar the extractor's
return_types. Those are cleaned names for type-reference edges:
clean_type_name strips `[]` and `*`. A cross-file single result `[]T`
therefore reached the registrar as `T`. `xs := pkg.Generate(); xs[0].M()`
lost its typing and fell back to a name guess. On prometheus the base
binds `sample[0].T()` to chunks.Sample.T, but the first D1 change bound
it to tsdb.T.

pass_lsp_cross.c now leaves a single result's raw text alone, as before
D1; the registrar already parses `*T` and `[]T`. Only a result LIST
`(...)` is split, at its top-level commas, into raw element types:
- `(*T, error)` -> `*T|error`;
- the named `(t *T, err error)` -> `*T|error`;
- the grouped `(a, b int)` -> `int|int`.
`chan`, `func`, `map`, `struct` and `interface` never count as result
names. A list that does not split cleanly keeps its raw text.

Tests: the D1 fixture gains three call sites, each with two same-named
decoys:
- a single slice result indexed (`xs[0].TS()`);
- a tuple with a slice element;
- grouped named results.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 03:29:05 +02:00
Martin Vogel c06465bae1 Merge pull request #2680 from DeusData/fix/py-relative-package-import
fix(python): from . import x names the importing package, not its parent
2026-10-19 03:24:43 +02:00