Django REST Framework ViewSets expose extra endpoints through @action-decorated
methods, e.g. @action(detail=True, methods=["post"], url_path="approve"). On
main these methods carried no route_path, so Phase 2a created no Route node and
no HANDLES edge for them -- the endpoint was invisible in the graph (#603).
Add try_drf_action_decorator (internal/cbm/extract_defs.c): when a method's
decorator identifier is "action", read the methods / url_path / detail kwargs
and set *out_path/*out_method. The existing decorator-route pipeline
(ensure_one_decorator_route, Phase 2a) then creates the Route + handler->Route
HANDLES edge in the established direction. Fallbacks: method name for url_path,
"GET" for methods; detail=True -> /{pk}/<seg>, else /<seg>.
Reproduce-first guard: handles_drf_action_python in the edge_types_probe suite
-- a DRF @action method must produce >=1 HANDLES edge (RED with 0 edges before
the extractor, GREEN after).
Known limitation: multi-method actions (methods=["get","post"]) capture only the
first method -> a single Route.
Distilled from #647 (thanks @muskiteer). Drops that PR's unrelated project-wide
HANDLES edge-direction reversal, which was unnecessary for the fix and broke the
handler->Route convention relied on by pass_cross_repo.c's find_route_handler.
Closes#603
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Co-Authored-By: muskiteer <agarwal.vansh3110@gmail.com>
hook-augment's PreToolUse Grep/Glob augmenter required POSIX-style
'/'-prefixed absolute paths in two guards, so a Windows cwd (C:\repo or
C:/repo) always failed: the _WIN32 cwd check returned early, and the
walk-up loop guard (dir[0] == '/') never iterated. The result was a
structural no-op on Windows — zero additionalContext for every Grep/Glob,
even for an exactly-indexed symbol.
Add cbm_hook_path_is_abs(), accepting POSIX "/..." and Windows "X:/..." roots;
normalize backslashes to forward slashes on the Windows cwd; and stop the
parent-climb at the drive root as well as the POSIX root. POSIX behavior
is unchanged.
Adds a cli_hook_augment_path_is_abs regression test (the predicate is
exposed as cbm_hook_path_is_abs via cli.h) covering POSIX and Windows
drive roots — it fails on the pre-fix POSIX-only guard and passes after.
Verified with that test plus a standalone walk self-test (full path
resolves first, then climbs to one level below the drive root, symmetric
to POSIX); clang-format clean on all three files; clang -fsyntax-only on
the translation unit. Not built through the full toolchain locally (no
make/zlib) — relying on Windows CI to confirm the build.
Fixes#618
Signed-off-by: catsmonster <shaked.brand@gmail.com>
Companion to the raw-JSON deprecation warning committed on this branch (adds the
smoke coverage that was meant to land with it).
Migrate scripts/smoke-test.sh from raw-JSON `cli <tool> '{...}'` invocations to
FLAG form for every tool (index_repository / search_graph / trace_path /
get_graph_schema / query_graph incl. all Cypher checks / get_architecture /
search_code / delete_project), with identical results verified against a prior
run; flag form passes the Cypher query as one argv token, so the old
JSON-escaping helper is removed. Add dedicated guards for an integer flag
(--limit / --depth), a bare boolean (--exclude-entry-points), a repeated array
flag (--semantic-query -> JSON array), piped stdin, --args-file, and per-tool
--help (RC 0 + shows a flag; unknown tool -> RC!=0), plus the deprecation guard
(raw JSON warns on stderr; flag form does not). Non-CLI phases (incl. Phase 10
binary security) unchanged. Full smoke: ALL PASSED.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Raw JSON (cli <tool> '{...}') still works for back-compat, but now emits a
deprecation warning to STDERR (stdout stays clean JSON for piping) nudging
users to flags / --args-file / stdin. Only the raw-JSON path warns.
Also make scripts/smoke-test.sh exercise the whole new CLI input surface
end-to-end against the built binary: every tool call migrated from raw JSON to
FLAG form (index_repository / search_graph / trace_path / get_graph_schema /
query_graph incl. all Cypher checks / get_architecture / search_code /
delete_project -- flag form passes the query as one argv token, so the old
JSON-escaping dance is gone), plus dedicated guards for an integer flag
(--limit / --depth), a bare boolean (--exclude-entry-points), a repeated array
flag (--semantic-query -> JSON array), piped stdin, --args-file, and per-tool
--help (RC 0 + shows a flag; unknown tool -> RC!=0). One raw-JSON call is kept
as the deprecation guard (asserts the warning fires; the flag form asserts it
does not).
Reproduce-first: reverting the warning makes the raw-JSON smoke assertion fail
("did not emit a deprecation warning"); restored -> green. Full smoke ALL
PASSED; C unit suite 5735/0 (unchanged -- only main.c + the smoke script).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
collect_db_project_names built the "project not found or not indexed" error's
available_projects list into a fixed 4 KB buffer and, on overflow, let snprintf
truncate the last name mid-token then clamped offset to the buffer end -- so the
error response was malformed, unterminated JSON (e.g. ...,"tmp-cb],"count":50})
whenever enough projects were indexed to overflow the buffer. A client parsing
the error would fail. Found while exercising the CLI against a cache with 280
indexed projects.
Write element-boundary instead: before each name, require the whole element
(optional leading comma + the quoted name + NUL) to fit in the remaining buffer;
otherwise stop at that boundary. The array then always holds only complete names
and count == its length, so the JSON is always valid (just fewer names when the
buffer is full -- callers can run list_projects for the complete set).
Reproduce-first: tool_bad_project_error_valid_json_issue235 builds 50 dbs with
~120-char internal names (well over 4 KB), triggers the error, and asserts the
response parses as JSON and available_projects is an array with length == count.
RED before (yyjson_read returns NULL on the truncated buffer), GREEN after. The
existing tool_bad_project_name_no_overflow_issue235 (buffer bounds) still passes.
Full suite 5736/0.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Query tools opened the project db with SQLITE_OPEN_READWRITE and ran write
pragmas (PRAGMA journal_mode=WAL + wal_checkpoint), so every query MUTATED the
db (rewrote the header, created -wal/-shm) and FAILED outright when the db or
its directory was read-only (the WAL pragma cannot create -shm on a read-only
filesystem -> resolve_store returned NULL -> "project not found"). Latent
data-integrity bug found while diagnosing #704 (a query grew a 0-byte shadow
db to 100 KB).
- cbm_store_open_path_query now opens SQLITE_OPEN_READONLY. configure_pragmas
gains a read_only mode that runs only the non-writing pragmas (foreign_keys,
temp_store, busy_timeout, mmap_size) and skips journal_mode / wal_checkpoint
/ synchronous. The read-write open (cbm_store_open_path) is unchanged.
- Read-only-filesystem fallback: SQLite opens lazily, so a RO-FS failure
surfaces on first access; we probe with SELECT 1 and, only if the file
exists, retry once with a `file:<path>?immutable=1` URI (URI-special
characters percent-encoded) which bypasses WAL and reads the main db
directly. The fallback is gated to that failure path so writable-fs readers
still see uncheckpointed WAL frames via -shm.
- manage_adr is the only resolve_store caller that writes; it now opens its own
read-write handle (cbm_store_open_path) for ADR storage (in-memory/embedded
stores still write through the resolved store). All 12 resolve_store callers
audited; query_graph is read-only (the Cypher engine rejects writes).
Reproduce-first (non-vacuous, no real read-only mount needed):
- readonly_query_does_not_mutate_db: snapshot a rollback-journal db's bytes,
run search_graph, assert the file is byte-identical + no -wal created. RED
before (the WAL pragma rewrote the header).
- readonly_query_succeeds_on_readonly_fs: chmod the containing DIRECTORY 0555
(the real read-only-fs symptom -- a 0444 file alone is vacuous since SQLite
downgrades the open), run search_graph, assert it succeeds. RED before (WAL
pragma could not create -shm -> "project not found"); exercises the immutable
fallback.
Full suite 5724/0.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Project resolution was filename-addressed but internal-name-validated:
resolve_store opened <cache>/<passed>.db and required the internal
projects.name row to equal the passed name, while list_projects advertised
the .db FILENAME. When a db's filename differed from its internal name (a
copied/renamed db, or a legacy '.'-vs-'-' username twin from a past path
sanitization), it showed in list_projects but every query returned "project
not found or not indexed". Node rows are keyed on the internal name, so just
opening the file is not enough -- queries must use the internal name too. The
intermittency ("works now, fails 10 min later") was a warm in-process store
cache masking a broken cold resolve; a fresh cli call is always cold.
Key list + resolve on the INTERNAL project name, with no file renames or
mutation:
- list_projects / collect_db_project_names / build_project_json_entry now
advertise each db's internal projects.name (via db_internal_project_name)
and skip ghost/empty/corrupt dbs (0-byte, no project row) so the listed
names are exactly the ones that resolve.
- resolve_store keeps the fast path (open <passed>.db, get_project -- the
common case where filename == internal name). On a miss it runs a bounded
cache-dir scan, adopting the db whose sole internal project name equals the
passed name (gated on exactly one project row so it never serves another
project's data). No match -> NULL (a typo stays not-found). The scan runs
only on the fallback.
Reproduce-first: tool_resolve_store_by_internal_name_issue704 builds an
isolated CBM_CACHE_DIR with a control db, a drifted db (filename != internal
name), and a 0-byte ghost; asserts list advertises the internal name (not the
filename), the drifted project resolves via the scan, and the ghost is neither
listed nor resolvable. RED (113/1, list lacked the internal name) -> GREEN
(114/0). Full suite 5723/0.
Also hardened tool_bad_project_name_no_overflow_issue235: its fixture used
1-byte non-SQLite files that the new ghost filter would skip, making the
buffer-overflow guard vacuous; rebuilt it with valid dbs carrying long
internal names so the guard still fires.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The CLI only accepted a single inline JSON argument (cbm cli <tool> '<json>'),
which PowerShell mangles on Windows and which made `cli <tool> --help` return
"repo_path is required" -- the "--help" token was parsed as the JSON args, and
there was no per-tool help at all.
run_cli now resolves tool arguments by precedence: --args-file <path>, a raw
JSON object (back-compat), --flag / --flag=value / bare-boolean flags, piped
stdin, then empty {}. Flags are converted to a JSON object via the tool's
input_schema: kebab-case keys map to snake_case, values are typed
(string/integer/boolean), and repeated flags accumulate into array-typed
properties (--semantic-query a --semantic-query b -> ["a","b"]). stdin and
--args-file are UTF-8-clean and bypass shell quoting entirely (the robust path
on Windows). `cli <tool> --help` prints usage + the tool's flags, generated
from the same input_schema the MCP tools/list advertises -- no hand-maintained
table.
cbm_mcp_tool_input_schema() exposes a tool's schema; cbm_cli_build_args_json()
and cbm_cli_print_tool_help() do the conversion and help.
Reproduce-first: 8 cli tests (string / integer-not-string / bare-boolean /
repeated-array / kebab->snake / --key=value with spaces / bad-positional-errors
/ help) -- RED on a stub, GREEN after. Full suite 5730/0.
Note: true CJK-path support on Windows still needs a UTF-8/wmain entry point
(argv is ANSI-codepage-mangled upstream of parsing); stdin/--args-file mitigate
it and are tracked separately.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Query tools resolved the project only via the exact "project" key, but
list_projects surfaces the field as "name" and the not-found hint said
"pass the project name", so callers naturally pass "project_name". With no
alias the arg was NULL and resolve_store bailed before opening any .db, so
every query returned "project not found or not indexed" even though the
project was indexed -- the reporter's strace shows the cache dir scanned but
no .db opened. Reproduced on macOS; not Linux-specific.
Add get_project_arg() accepting project | project_name | project_id |
projectName across the 11 query handlers (not bare "name" -- index_repository
uses it for an explicit project-name override). Split the no-store error so a
missing argument reports "missing required argument: project" instead of
masquerading as "not indexed", and name the literal "project" key in the
not-found hint.
Reproduce-first: tool_get_architecture_accepts_project_name_alias_issue640
and tool_search_graph_accepts_project_name_alias_issue640 (RED before ->
GREEN after). Full suite 5724/0, no regressions.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Standard fopen on Windows uses the ANSI/system codepage and silently
fails to open files whose absolute path contains non-ASCII characters
(accented, CJK, etc.). This causes the pipeline to skip parsing those
files entirely — File nodes are created by directory traversal (which
already uses wide Win32 APIs) but tree-sitter never receives the source,
so no Function/Class/Method nodes are extracted.
Add cbm_fopen(path, mode) to compat_fs:
- Windows: converts the UTF-8 path to UTF-16LE via cbm_utf8_to_wide
(existing helper in win_utf8.h) and calls _wfopen.
- POSIX: delegates to fopen (no-op change, paths are already UTF-8).
Replace every fopen call that operates on user project file paths:
- src/pipeline/pass_parallel.c (primary indexing path)
- src/pipeline/pass_definitions.c
- src/pipeline/pass_calls.c
- src/pipeline/pass_semantic.c
- src/pipeline/pass_usages.c
- src/pipeline/pass_lsp_cross.c
- src/pipeline/pass_pkgmap.c
- src/pipeline/pass_k8s.c
- src/pipeline/pass_envscan.c
- src/pipeline/path_alias.c (tsconfig detection)
- src/pipeline/artifact.c
- src/discover/language.c (content-sniff for language detection)
- src/discover/gitignore.c
- src/discover/discover.c
- src/discover/userconfig.c
- src/mcp/mcp.c (read_file_lines, adr.md reader)
Fixes#700
Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
QA round 1 (PR #632) flagged that the SubagentStart hook uses matcher "*",
the same matcher a user is most likely to pick for their own catch-all
SubagentStart hook. Because is_cmm_hook_entry keyed ownership on the matcher
string alone, install would remove the user's "*" entry and replace it with
ours, and uninstall could remove the wrong one.
Add an optional `match_command_substr` to the upsert/remove args, threaded into
is_cmm_hook_entry: when set, an entry must ALSO carry a hooks[].command
containing that substring to be claimed as ours. The Claude SubagentStart hook
passes "cbm-subagent-reminder"; all existing callers leave it NULL, preserving
their matcher-only behavior unchanged. Install/uninstall now only ever touch
CMM's own entry and never clobber a foreign "*" hook.
Add cli_claude_subagent_hook_preserves_user_entry covering the case: a
pre-existing user "*" SubagentStart hook survives both install and uninstall.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Verify the Claude SubagentStart reminder: install writes a SubagentStart
entry with a match-all matcher pointing at cbm-subagent-reminder, a second
upsert is idempotent (no duplicate entry), and removal leaves no SubagentStart
key behind.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Subagents spawned via the Agent tool do not fire SessionStart, so the
existing SessionStart reminder never reaches them — they start without the
codebase-memory-mcp code-discovery guidance and fall back to grep/file-read.
Register a Claude Code SubagentStart hook (matcher "*") that injects a leaner
variant of the protocol via JSON additionalContext, omitting the
index_repository step since the parent session has already indexed the
project. SubagentStart injects context only via a JSON object on stdout, not
plain text, so the generated script emits a static JSON literal — no runtime
escaping and no python3/jq dependency. Advisory only; installed and removed
alongside the SessionStart hook in install_claude_code_config.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
The "tampered arm64 binary is SIGKILLed (137)" premise is empirically false on
current macOS CI runners for an ad-hoc-signed CLI binary -- the binary has no
CS_KILL/hardened-runtime flag, so a tampered code page is not killed: it
executes the garbage and crashes with SIGILL (exit 132, run 28365724001), not
137. (remove-signature and corrupt-blob both ad-hoc re-sign on exec and run to
exit 0.) So no runtime exit code is a deterministic guard here, and
"tamper -> crash" is near-tautological (zeroed code crashes regardless of
signing).
Assert the real, deterministic integrity invariant instead: `codesign
--verify` REJECTS a tampered copy (the CodeDirectory page hashes no longer
match the modified code), while the untampered binary verifies cleanly (10a).
It is a pure userspace hash check -- no tampered code is executed. The copy is
separate, so the original binary stays intact for the 10e re-sign test.
Refs: github.com/garrytan/gstack#997, github.com/nodejs/node#40827
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Refines the previous 10c change on this branch. Garbling only the signature
blob still ran (exit=0 on dry-run 28360363173): since macOS 11 a binary with
a missing/invalid signature is ad-hoc re-signed on exec by newer macOS and
RUNS, so neither remove-signature nor a corrupt blob triggers the kill.
The reliable "tampered binary is SIGKILLed (137)" trigger is tampering the
SIGNED CODE while leaving the valid signature attached: the kernel validates
each executed page against the intact CodeDirectory hash, finds the mismatch,
and kills the process before user code runs. Zero the entry-point
instructions (LC_MAIN entryoff, extracted dynamically) plus a span of early
__text on a SEPARATE copy, leaving the Mach-O header + load commands intact so
it still parses. The original binary is untouched, so the later 10e re-sign
step stays valid. x86_64 keeps remove-signature (code signing is not enforced
there).
Refs: github.com/garrytan/gstack#997, github.com/nodejs/node#40827
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Smoke test 10c (Phase 10 binary security E2E) verifies that an arm64 binary
with an invalid code signature is SIGKILLed (exit 137). It did
`codesign --remove-signature` then ran the binary, expecting the kernel to
kill it. But since macOS 11, a binary with NO LC_CODE_SIGNATURE is ad-hoc
re-signed on exec by newer macOS and RUNS (exit 0) -- so the test went flaky
and then consistently red on updated CI runner images (dry-runs 28350650225
and 28354735368 both failed only here; every other job, including the full
cross-platform test matrix, passed).
Corrupt the signature blob in place instead, leaving the LC_CODE_SIGNATURE
load command intact: AMFI then sees "signed but invalid" and rejects the
binary before any user code runs (deterministic 137). Only the signature
blob is garbled (not the code), so the later 10e re-sign step stays valid --
it replaces the blob and the code is untouched. x86_64 keeps
remove-signature (code signing is not enforced there).
Refs: github.com/garrytan/gstack#997, github.com/nodejs/node#40827
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The per-file C cross-LSP resolver mutated the finalized, project-wide cross
registry that is shared read-only across the parallel resolve workers. Each
post-finalize add landed in a tail the hash index does not cover, so every
lookup linear-scanned an ever-growing tail -> O(files*defs) on large C
codebases. The Linux-kernel full index hung at "[4/9] Resolving" on 11 cores
for >6 min and never finished, plus a heap data race across workers.
Seal every shared cross-registry: CBMTypeRegistry gains a read_only flag, set
by all five {c,py,cs,ts,go}_build_cross_registry builders right after
finalize, and cbm_registry_add_func/_type no-op on a sealed registry. One
chokepoint guards every language, robust to any resolver mutation site. The
plain-C function-registration site is also skipped directly when the registry
is shared.
Add six cross-language invariant tests (c, c++, py, c#, ts, go): resolving
against a finalized shared registry must leave func_count/type_count
unchanged -- RED before the fix (C +1, C++ +2 post-finalize adds), GREEN
after.
Verified: full suite 5720 passed / 0 failed; Linux-kernel full index now
completes all 9 phases in 643s (4.88M nodes, 11.9M edges) instead of hanging
at phase 4.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
#581 (query-path memory growth) does not reproduce in our soak: a 10-min,
200-file query-only run shows RSS flat-to-shrinking and committed plateauing
(mimalloc already purges on free, purge_delay=0). Rather than ship speculative
mitigations for a leak we cannot measure, this adds the observability to find it
where it actually occurs, plus a continuous guard.
- diag: the diagnostics writer now appends a PERSISTENT NDJSON memory trajectory
(one sample / 5s: rss, committed, peak fields, page_faults, fd, queries), kept
on exit (rotates to a .1 generation past 8 MB) so users can send the time
series post-mortem. The previous latest-snapshot file was overwritten every 5s
and deleted on stop.
- ci: the soak now also runs a read-only query-leak leg (CBM_SOAK_MODE query-leak,
no reindex/mutate) on every platform including Windows, so a regression that
introduces a query-path leak is caught. soak-test.sh RESULTS_DIR is now
env-overridable so both legs keep separate artifacts.
- docs: README Troubleshooting and Diagnostics section + a bug-report field
explain the no-telemetry stance and how to capture and share the trajectory.
Build clean; unit suite 5714/0; trajectory verified to persist post-exit.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The CI lint gate (scripts/lint.sh --ci, clang-format-20) flagged 7 files whose
new lines were not run through the project formatter. Reformat only those lines
(line wrapping / continuation alignment) — no behavior change.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
#581 (query-path memory leak) is an OPEN bug the unit-scale fixture cannot yet
reproduce, so the test stays honestly RED rather than vacuously green. The
bug-repro board is a non-gating status board where reds ARE the expected state
(repro_main.c), so this is a tracked known-red TODO, not a hidden skip. Tag it
TODO(#581) for tracking; turning it green needs a real WAL/committed-pages
reproduction tier plus the query-path compaction fix.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The 9 no-resolvable-edge tests asserted only that no CALLS edge targets the
unresolvable callee via inv_no_calls_edge_to_qn, which returns PASS when the
fixture produces ZERO edges for any reason — so a green did not prove the
unresolvable call was actually extracted and correctly dropped (a vacuous-pass
false-guard risk, unlike the sibling assert_lsp_strategy which checks
callable_sourced > 0).
Add an exercised-check to all four helper copies (c_cpp/java_cs/go_py/ts):
require at least one callable-sourced CALLS edge. Give each of the 9 fixtures a
resolvable in-fixture control call (known / Helper.Known / c.Inc) so a real edge
forms. The no-edge-to-callee invariant is unchanged; the tests stay green but
can no longer pass when extraction silently produces nothing.
Repro board: 311 passed, 1 failed (the honest #581 RED) — no regressions.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
repro_issue581 was a FALSE GUARD. A 3-node fixture over 150 search_graph calls
cannot move process RSS (observed factor=1.00), so rss_end <= 3.0x rss_warmup
passed even on the leaking build, and an explicit PASS() escape fired when RSS
read 0. #581 is OPEN: there is no SQLITE_CHECKPOINT_TRUNCATE anywhere and
cbm_mem_collect is never called in the query path (only after index/delete). The
green was a lie. Replace the vacuous assertion with an honest FAIL that documents
the leak is unfixed and not reproducible in this tier (needs a real
WAL/committed-pages reproduction tier plus the fix).
repro_issue431 is actually FIXED: install_vscode_profile_configs (cli.c:3211)
scans Code/User/profiles/ and plans a per-profile mcp.json, and the test passes
via the real assertion. Its detection-unmet branch returned a VACUOUS PASS that
would hide a future regression; change it to SKIP_PLATFORM so a host where
detection cannot fire is honestly inconclusive, not falsely green.
Repro board: 311 passed, 1 failed (the honest #581 RED).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The Kotlin LSP resolves a method call on an unknown-typed receiver whose member
is a universal kotlin.Any method (toString/equals/hashCode) to kotlin.Any.<m>
and emits lsp_kt_any, but no kotlin.Any node existed in the graph, so pass_calls
had no target node and dropped the call (callable=0).
Add kotlin_builtins.c (mirror of py_builtins.c): inject kotlin.Any plus its
three universal methods into result->defs during cbm_run_kotlin_lsp, before the
pipeline mints def nodes. The lsp_kt_any call now targets the injected node and
forms a CALLS edge.
Update the kotlin grammar-label golden to include the 4 injected nodes, exactly
as python's golden already includes its injected builtins.
Un-skips repro_lsp_kt_any. Repro suite 312/0, unit suite 5693/0.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The call extractor dropped any callee matched by cbm_is_keyword(), and the
Python keyword set lists builtins (len, str, range, int, list, dict, print) to
suppress them as bare usages. That also dropped the textual CALL, so the LSP
resolution to the injected builtins.<name> node (py_builtins.c) had no call to
attach to and no CALLS edge formed (callable=0) for free-function and
constructor builtins. Method builtins (s.upper) were unaffected because method
names are not in the keyword set.
Add cbm_is_resolvable_builtin() — the builtins we mint a real graph node for —
and keep those callees in call extraction only; the keyword filter still
applies to usage and semantic extraction (its four other call sites are
unchanged). Every un-filtered builtin has a target node, so the new edge is
always callable-sourced, never Module-sourced.
Un-skips repro_lsp_py_builtin and repro_lsp_py_builtin_constructor.
Repro suite 311/0, unit suite 5693/0.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>