Commit Graph
20 Commits
Author SHA1 Message Date
Andrew Hundt 9a7d83275b feat(mcp): self-healing query_graph hints, one notification authority, live tools/list schema
Response-format consolidation: add cbm_mcp_response_format/
cbm_mcp_invalid_response_format so every response-generating handler
reads the format argument or default_response_format config through one
function instead of duplicating the enum-mapping logic, and migrate
query_graph's legacy-JSON detection to the canonical
cbm_mcp_output_format_t enum. Fix the regression this surfaced: TOON-path
_context/session_project delivery (toon_append_context_once) was
serializing the one-shot context object as a raw escaped JSON string
inside the TOON stream instead of native TOON fields, silently dropping
it for any TOON-consuming client. Add toon_append_context_model plus its
scalar/table helpers to map the same format-neutral context model
inject_context_once already builds into native TOON output.

Self-healing zero-row hints: query_graph_no_rows_hint reparses the
already-executed query on the zero-row path only, walks MATCH/OPTIONAL
MATCH patterns and EXISTS predicates in WHERE and post-WITH-WHERE
clauses (hint_walk_expr_exists_types/hint_walk_where_exists_types), and
probes each referenced label/edge type against the store via
cbm_store_schema_label_observed/cbm_store_schema_type_observed
(schema_probe_one_row: fail-open on any non-SQLITE_DONE outcome, so a
probe error never produces a false accusation). Unobserved names are
named in the response; a bounded hint_seen_t/hint_seen_add set (capped
at CBM_STORE_SCHEMA_HINT_VOCAB_LIMIT) dedups names repeated across
patterns/UNION branches so "Klass, Klass." can't happen and avoids
re-probing a name already resolved. The vocabulary summary and the
generic zero-row fallback no longer claim WHERE-clause predicates were
verified, since only labels/edge types are ever probed — only the
labels/edge types claim is made.

One invalidation authority: every graph-mutation path now calls
the single cbm_mcp_server_notify_index_published (flags-only,
any-thread-safe) instead of ad hoc local staleness bookkeeping —
sync auto-index, in-process index_repository, in-process
autoindex_thread, overlay compaction (gated on compacted>0),
index_dependencies. handle_delete_project's notify is gated on the
mutation actually succeeding (!is_error): a delete of a nonexistent
project is a no-op and must not stale the tools/list description cache
for nothing. The request-thread-only drain
(mcp_drain_tools_list_changed) is factored once and shared by both
transports (line-delimited and Content-Length-framed), gated so a
notification published before any tools/list has ever been served is
suppressed rather than sent for a client that never asked.

Shared overlay-aware schema-view selector (mcp_get_current_schema/
mcp_overlay_view_ready) ends the schema/overlay-readiness dance
previously triplicated across handle_get_graph_schema,
build_resource_schema, and the query path, so query_graph's advertised
vocabulary can never diverge from the view a query actually runs
against. The selector takes an mcp_cypher_vocabulary_t level instead of
a bool: MCP_CYPHER_MATCH_VOCABULARY (labels/edge types/observed
patterns with counts — enough to compose executable MATCH clauses;
never parses property JSON) vs MCP_CYPHER_FULL_QUERY_VOCABULARY (adds
per-label/type property keys for WHERE/RETURN authorship; O(total
property rows) json_each discovery, so only the docstring cache and
get_graph_schema request it).

Route the two remaining canonical-only cbm_store_get_schema callers
through the selector at MATCH_VOCABULARY level: inject_context_once
(first-response _context previously paid the full property-key scan for
counts it never rendered, and could advertise canonical labels a ready
overlay had tombstoned) and build_resource_architecture (rendered only
rel_patterns yet fetched full property keys, canonical-only). The
architecture resource's mixed-read-model disclosure moves
relationship_patterns into the overlay-aware list accordingly.

build_query_graph_tool_description resolves the project's real
store via resolve_store(srv, project) instead of trusting srv->store
as-is: on the very first tools/list of a fresh session for an
already-indexed project, srv->store was still the empty default
in-memory store (maybe_auto_index detects "already indexed" and skips
reindexing without ever opening it), so the schema section rendered
silently empty until some other tool call happened to resolve it first.
The project name is snapshotted into a local buffer before the
resolve_store call rather than passed as a live alias to
srv->current_project, because resolve_store can call
reap_stale_store internally, which frees srv->current_project when a
deferred invalidation is pending — aliasing it produced a
heap-use-after-free (caught by the ASan full-suite run, confirmed fixed
by rerun).

Docstring clarity for newcomers: the live schema section now embeds its
notation legend in the section header ("Labels name{extra property
keys}[count]:") so the compact schema is self-explanatory in every
tools/list response, and the query_graph/get_architecture/search_code
format parameters gain the brief description their three siblings
already had, all naming default_response_format.

Tests: response-format and TOON-context regressions in test_mcp.c/
test_tool_consolidation.c/test_input_validation.c/test_token_reduction.c;
zero-row hint coverage including no-hidden-tool-name, missed-graph shadow-
project probing, empty-project no-crash, TOON/JSON format parity,
UNION/repeated-name dedup, and the previously-untested EXISTS-predicate
walk; notification coverage for every notify call site including the
delete_project no-op negative direction and the "notify before first
tools/list is suppressed" negative direction; a cold-start regression
test seeding a real on-disk project store and calling tools/list as the
literal first request of a fresh session; overlay-consistency tests for
the two rerouted callers (first_response_context_uses_ready_overlay_schema,
resource_arch_rel_patterns_use_ready_overlay) proving _context and the
architecture resource report the active-overlay vocabulary, not
tombstoned canonical labels/types.

Docs: README.md documents the zero-row hint behavior and the
default_response_format config example; docs/CONFIGURATION.md adds the
default_response_format row to the important-keys table.

Verification: make -f Makefile.cbm test (full ASan/UBSan suite) run on
this exact tree after the final revisions. Earlier iterations of this
content additionally passed make -f Makefile.cbm test-leak (0 leaks for
0 total leaked bytes) and manual checks against the live MCP binary
(zero-row hint names real schema on two repositories; the query_graph
tools/list docstring shows the live schema on the very first request of
a fresh session); revisions since those checks are docstring text, the
vocabulary-enum rename, and the two selector reroutes, all covered by
the full-suite rerun.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 01:42:38 -04:00
Andrew Hundt 5a13131e5d fix(mcp): return fresh watcher and wildcard query results
A supervised watcher reindex could replace a project database while the request thread retained a read-only handle to the unlinked prior generation. The watcher also treated any non-null worker response as success, including status=error, and shutdown freed g_server before an in-flight watcher callback had joined.

Pass the server through cbm_mcp_index_run_supervised_path, defer cached-store retirement through store_stale, classify indexed/degraded responses before committing watcher baselines, and join the watcher before freeing the server while keeping the watcher object alive for autoindex teardown.

Normalize glob-compatible wildcards before POSIX regex compilation so valid-but-ambiguous inputs such as alpha?orker and alternations return the intended symbols across name_pattern, qn_pattern, and pattern. Preserve explicit group, class, escaped, and dot-regex constructs.

Place validation fixtures under the isolated test cache, clean runner-owned caches after both passing and failing runs by default, and reuse CBM_TEST_ARTIFACT_DIR as the explicit failed-run retention path.

Verification: CBM_ONLY_SUITE=input_validation build/c/test-runner (48 passed); CBM_ONLY_SUITE=mcp build/c/test-runner (227 passed); make -f Makefile.cbm cbm (O2 build and ad-hoc signature); MCP initialize plus watcher-active EOF shutdown exited 0; git clang-format reported no changed-line edits; git diff --check passed. The repository-wide lint-format target still reports pre-existing formatting debt outside this diff.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 14:03:39 -04:00
Andrew Hundt 98c8e2ea56 fix(runtime): preserve TOON contracts and index process ownership
Route filesystem project arguments through get_store_project_arg() so resolve_project_store() can auto-index paths without breaking #1025 unique-tail slug resolution or manage_adr path normalization. Append one-shot _context data to TOON search responses and keep query_graph compatible with in-memory stores.

Publish subprocess child PIDs atomically, migrate UI indexing to cbm_subprocess_run(), enforce spawned-process ownership on process-kill, use per-slot log files, and defer MCP store invalidation to the request thread. Replace signal-handler fclose(stdin) with atomic stop plus close(0), and give autoindex_thread its own store handle.

Treat recorded oversized-file skips as nonfatal while preserving OOM failures; clamp finish_data_flow_props() snprintf offsets; align TOON tests with explicit format=json assertions; use cbm_resolve_cache_dir() in index resilience tests.

Validated: ASan/UBSan MCP 216/216, input_validation 45/45, tool_consolidation 100/100, token_reduction 50/50, depindex 35/35; pipeline and subprocess suites passed; make -f Makefile.cbm cbm built and signed; lint-source-safety and source-safety-test passed.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 01:55:02 -04:00
Andrew Hundt 34db7cf044 fix(mcp): reuse canonical project paths
Consolidate tilde-aware path canonicalization for project slugging and path auto-index resolution. Add a graph-backed regression test for search_graph(project='~/repo') using an isolated fake HOME so explicit repo-directory params resolve to the same slug that indexing creates.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-01 20:30:59 -04:00
Andrew Hundt 13511cd7f8 test(portability): reuse filesystem wrappers in suites
Replace direct environment and filesystem cleanup calls in MCP, CLI, input validation, and HTTP link tests with existing cbm compatibility wrappers. Also updates the MCP publish metadata fixture to use the current three-file containment fallback instead of the now-exact new-folder path.

Validation: git diff --check; bash scripts/check-source-safety.sh; make -j8 -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=mcp ./build/c/test-runner; CBM_ONLY_SUITE=cli ./build/c/test-runner; CBM_ONLY_SUITE=input_validation ./build/c/test-runner; CBM_ONLY_SUITE=httplink ./build/c/test-runner.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-01 19:33:52 -04:00
Andrew Hundt 608b51374e fix: apply autoindex limits consistently
Use the bounded discover count for startup, first-use, and explicit path autoindexing so CBM_AUTO_INDEX_LIMIT cannot be bypassed by a later MCP handler path.

Move sync first-use indexing into resolve_project_store via a shared MCP-local helper, leaving REQUIRE_STORE responsible for joining an in-flight startup index and reporting missing stores. This avoids duplicate indexing attempts and duplicate over-limit warnings while preserving structured stderr logging.

Add an input-validation regression for over-limit explicit path projects and clarify the config help text to say the limit counts indexable files.

Verified: build/c/test-runner; build/c/codebase-memory-mcp; CBM_ONLY_SUITE=input_validation (44), mcp (113), discover (83), cli (104).
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 17:09:51 -04:00
Andrew Hundt a1c8bf9a0e fix: honor auto_index on first use
Share the MCP auto-index enablement decision across startup, synchronous first-use indexing, and explicit path indexing. Production servers still attach config and keep the registry default auto_index=true, while embedded/no-config servers remain manual unless CBM_AUTO_INDEX explicitly opts in.

This prevents search/trace/resource paths from bypassing auto_index=false after startup and avoids accidental full worktree indexing in test or embedded contexts. The path auto-index regression test now opts in explicitly and restores CBM_AUTO_INDEX afterward.

Validation:

- make -f Makefile.cbm build/c/test-runner

- CBM_ONLY_SUITE=mcp build/c/test-runner: 113 passed

- CBM_ONLY_SUITE=tool_consolidation build/c/test-runner: 94 passed

- CBM_ONLY_SUITE=input_validation build/c/test-runner: 43 passed

- git diff --check

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 11:43:46 -04:00
Andrew Hundt 0839cb60b1 fix: align streamlined MCP trace surface
Use trace_path as the single canonical call-tracing tool across classic and streamlined tool lists, with the streamlined list reusing the canonical schema to prevent drift. Revealing hidden tools now makes the classic-only tools discoverable through tools/list for clients that only call advertised tools.

Extend trace_path with the existing optional filters and modes exposed by the fork, validate cheap arguments before project resolution so malformed calls cannot trigger auto-indexing, and handle string-array allocation failures explicitly. Add focused coverage for schema parity, hidden-tool reveal, invalid trace modes, test filtering, risk labels, and exclude filters.

Verified with: make -f Makefile.cbm cbm; make -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=mcp build/c/test-runner; CBM_ONLY_SUITE=tool_consolidation build/c/test-runner; CBM_ONLY_SUITE=input_validation build/c/test-runner; CBM_ONLY_SUITE=token_reduction build/c/test-runner; git diff --check.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 09:40:25 -04:00
Andrew Hundt 60cad1ebbb fix: consolidate trace API and rank persistence
Use trace_path as the single public call-tracing tool across classic and streamlined MCP surfaces, while exposing existing semantic_query and search_code mode parameters in the advertised schemas. Keep the streamlined surface compact, but ensure the hidden-tool reveal covers the full classic capability set without trace aliases.

Persist PageRank, LinkRank, and node_degree rows with direct VALUES inserts instead of per-row SELECT probes. The node/edge project is loaded once with the graph rows so full-scope dependency attribution stays identical to the previous behavior.

Add focused coverage for dependency project attribution, streamlined parameter contracts, and targeted suite allowlist entries so these checks cannot silently run zero tests.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-28 08:36:56 -04:00
Andrew Hundt 74a5c63eff feat(mcp): split search_code_graph into focused tools (§4b)
Replace the consolidated search_code_graph mega-tool — which merged graph
search + source grep + cypher behind two nearly disjoint parameter
families — with the three existing focused tools as the DEFAULT surface:
search_graph (graph/structural), search_code (source grep), query_graph
(cypher). These are drawn from TOOLS[] (single schema source, DRY);
trace_call_path + get_code remain on the default surface. The
search_code_graph schema and its thin dispatch router are deleted; the
underlying handlers (handle_search_graph/search_code/query_graph) stay.

The _hidden_tools progressive-disclosure hint now lists 11 hidden tools
(was 14). Repointed three user-facing hint strings (handle_trace_call_path
missing-name and function-not-found; two unknown-tool path hints) from the
deleted tool to search_graph.

Tests updated to the new 5-tool surface:
- tests/test_mcp.c: mcp_tools_list + server_handle_tools_list assert the
  5 default tools present and search_code_graph absent.
- tests/test_tool_consolidation.c: renamed streamlined_mode_shows_3_tools
  -> ..._5_default_tools; repurposed the two search_code_graph dispatch
  tests into search_graph_dispatch / query_graph_dispatch (query_graph
  handler accepts both cypher= and query=); revised
  schema_compact_documented_as_graph_only (the graph-vs-source compact
  collision it warned about no longer exists post-split).
- tests/test_input_validation.c: remapped 16 dispatch calls by mode --
  cypher -> query_graph, source-grep -> search_code, graph/structural ->
  search_graph.
- tests/test_depindex.c: tool_index_dependencies_listed asserts
  search_graph (was search_code_graph).
- src/cli/cli.c: six config-description strings (search_limit, tool_mode,
  context_injection, compact, default_sort_by, default_include_dependencies).

Back-compat: blast radius is in-repo only (0 references in Go internal/cmd,
docs, or the CGO wrapper). External callers of search_code_graph must
switch to search_graph / search_code / query_graph, which are strictly
more capable. query_graph keeps its query= backward-compat alias.

Context -- of the three section-4 fixes in the merge plan, only section 4b
was a real change: section 4c (function<->class edges) was already
implemented (commit b950bf2 added the missing MEMBER_OF reverse-edge
coverage), and section 4a (passive resources) was already mitigated by
inject_context_once plus the get_graph_schema/get_architecture/index_status
tools -- both verified against code, not assumed.

Gate: 5981 passed / 2 failed (the 2 are the deferred B1 store-layer
incremental failures, unrelated to section 4b).

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-06-24 22:00:24 -04:00
Andrew Hundt a53c55f6b1 fix(mcp): auto-index path-based projects not under session_root (Bug #4)
Previous behavior: when project= was a full directory path (e.g.
"/path/to/react-grid-layout") and that path was NOT under srv->session_root
(e.g. it was in the parent project's .gitignore), codebase-memory returned
"project not found" instead of indexing the requested directory. The auto-index
guard only checked srv->session_root, so .gitignore-excluded sub-repos with
their own .git (passed as absolute paths) were never indexed on first query.

What changed:
- src/mcp/mcp.c: in resolve_project_store(), save the resolved filesystem path
  before expand_project_param() frees raw_project (expand_tilde + realpath into
  _raw_path local variable)
- src/mcp/mcp.c: add path-based auto-index fallback after the session_root block:
  when store is still NULL and _raw_path is an accessible directory, run
  cbm_pipeline_new(_raw_path, ...) to index it, then resolve the store by slug
- tests/test_input_validation.c: add path_project_auto_indexes_separate_directory
  test -- creates two temp dirs, queries the first to establish session_root,
  then queries the second (separate path) and asserts the sentinel function is found

Result: 2917 passed (was 2916); path-based separate-repo queries now auto-index
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-11 01:46:14 -04:00
Andrew Hundt 12c22e201b feat(config): add context_injection config key to enable/disable _context header
Adds boolean config key context_injection (default true, env CBM_CONTEXT_INJECTION)
that controls whether inject_context_once embeds the _context header in the first
tool response.

WHAT _context contains: node/edge counts, all node labels with counts, edge types
with counts, PageRank stats, and detected ecosystem (Go/Python/Rust/etc.).

WHEN TO DISABLE (context_injection=false):
  - Scripted/programmatic use: extra JSON adds noise to parsed output
  - CI pipelines: token cost is metered, model needs no schema context
  - Model has explicit codebase instructions via system prompt
  - Benchmarking: removes schema-query overhead from latency measurements

WHEN TO KEEP ENABLED (default):
  - Interactive AI sessions benefit from automatic situational awareness
  - Saves 2-3 round-trips otherwise needed for get_architecture/get_graph_schema

Usage:
  codebase-memory-mcp config set context_injection false   # per-install
  CBM_CONTEXT_INJECTION=false codebase-memory-mcp          # per-session

Implementation:
  - Config check placed BEFORE setting context_injected flag so toggling
    mid-session works (re-enable without server restart)
  - session_project is still always included regardless of this setting
  - cbm_config_get_bool reads CBM_CONTEXT_INJECTION env var automatically
    via cbm_config_get_effective (the standard env-override path)

TDD: config_context_injection_disabled, config_context_injection_enabled_by_default
Tests: 2274 passed, 0 failed
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 04:26:20 -04:00
Andrew Hundt 346cd4c900 fix(mcp): tilde expansion + bare "." detection in project path helpers
project_slug_from_path previously detected "~/..." as a path but passed it
directly to realpath(3), which doesn't expand tilde (that's a shell feature).
The slug was then derived from the literal "~" string, giving a wrong result.

Fix: extract expand_tilde() helper that expands "~" and "~/..." to
$HOME + rest using getenv("HOME"). "~user/..." is intentionally left
unexpanded (would require getpwnam — not worth the complexity for this use).

Also fix project_is_path() to detect bare "." (current directory) as a
path, in addition to the already-handled "./" prefix.

The three helpers are now:
  project_is_path(s)      — pure predicate, no heap allocation
  expand_tilde(s)         — tilde expansion, heap result or NULL
  project_slug_from_path(s) — full path→slug pipeline: tilde→realpath→slug

TDD: source_search_tilde_project_expands — writes a file under $HOME (via
  tmpdir), passes project="~/..." and verifies grep returns matches.
  Skips gracefully when tmpdir is not under $HOME.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 03:01:05 -04:00
Andrew Hundt 9da28251b5 refactor(mcp): DRY path→slug conversion + broaden path-arg fix coverage
DRY refactor:
  Extract project_is_path() and project_slug_from_path() helpers (mcp.c:1239)
  that encode the filesystem-path detection and realpath+cbm_project_name_from_path
  conversion in one place. Previously this logic was duplicated between
  expand_project_param Rule 0 and the get_project_root path-arg fix.
  Both sites now call the shared helpers.

  Documents known limitations: "." not detected (use "./"), "~" detected
  but realpath(3) doesn't expand tilde (use absolute path).

Bug coverage: handle_detect_changes and handle_manage_adr also call
  get_project_root with a raw project arg and were silently broken for
  path-style args. They are now fixed as a side-effect of the get_project_root
  fix (no code change needed — they already go through get_project_root).

New TDD tests (5):
  detect_changes_slug_project_finds_root — detect_changes doesn't error on
    valid slug project (regression for get_project_root slug path)
  manage_adr_slug_project_finds_root — same for manage_adr
  source_search_no_project_falls_back_to_session — search_in="source" with
    no project arg uses session_project (set via cbm_mcp_server_set_session_project)
  source_search_via_search_in_param — upgraded to write a real source file
    and assert matches array returned (stronger than previous dispatch-only check)
  source_search_path_project_normalizes_to_slug — slug project returns
    matches, not "project not found"

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 02:55:01 -04:00
Andrew Hundt bfe127ee68 fix(mcp): source search fails when project arg is a filesystem path
get_project_root(srv, project) passed the raw project string directly to
resolve_store() and cbm_store_get_project(). When project="/Users/.../repo"
(a path, not a slug), project_db_path() embedded slashes in the .db filename,
producing an invalid path like ~/.cache/.../Users/athundt/.../repo.db.

Fix: get_project_root now normalizes project args the same way
expand_project_param Rule 0 does — if the arg looks like a filesystem path
(starts with /, ~, ./, or contains /), use realpath() + cbm_project_name_from_path()
to convert it to a slug before looking up in the DB.

Also: get_project_root now falls back to srv->session_project when project
is NULL, so search_in="source" works without an explicit project arg when
the session project is already detected (e.g. after initialize).

Two new tests:
  source_search_via_search_in_param — upgraded: writes a real file in tmpdir
    and verifies grep returns matches (not just "dispatch fired")
  source_search_path_project_normalizes_to_slug — verifies slug project arg
    returns matches array, not "project not found"

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 02:37:44 -04:00
Andrew Hundt a86d3d5659 fix(mcp): 6 bugs found in post-implementation review
store.h:108 — comment typo: "AND" → "OR" for pattern OR-search field

mcp.c:1754 — indentation bug in mode-error free() block: free(file_pattern)
  had 1-space indent instead of 8 and a spurious blank line

mcp.c:2422 — OOM leak in QN lookup: when cbm_store_find_node_by_qn succeeds
  but calloc fails, qn_tmp heap fields (name/project/label/etc.) leaked.
  Fix: free_node_contents(&qn_tmp) in else branch. Added forward declaration
  of free_node_contents before handle_trace_call_path so it resolves.

mcp.c:2464 — "function not found" hint was wrong when only qualified_name
  passed. Old hint said "use name_pattern" but user passed a QN. New hint
  says "use pattern= to find the correct qualified_name".

mcp.c:472 — search_in schema description now documents the response format
  difference: graph mode returns {total,results:[{qualified_name,...}]},
  source mode returns {matches:[{file,line,content}],count}.

tests: 4 new TDD tests covering:
  - pattern glob wildcard auto-convert (*foo* → .*foo.*)
  - pattern invalid regex returns error with hint
  - trace: qualified_name takes priority over function_name when both given
  - trace: QN-not-found returns specific hint mentioning qualified_name
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 02:04:27 -04:00
Andrew Hundt 6da71c7973 fix(tests): correct 2 test regressions from api-consolidation
test_tool_consolidation:539 — resource_client_never_gets_context_across_multiple_calls
  - Cold-start fix (resolve_project_store) now opens the real indexed DB, so
    search results include node names containing "_context" as a substring
    (e.g. "inject_context_once", "gets_context_across_multiple_calls").
  - strstr(r, "_context") was a false positive: matched substrings in node
    names, not the injected "_context" JSON key.
  - Fix: check for "\"_context\":" (quoted JSON key + colon) which only
    matches the actual injected context object, not substrings in results.
  - Same fix applied to legacy_client_gets_context_only_on_first_call test.

test_input_validation:695 — trace_accepts_qualified_name_param
  - QN lookup calls cbm_store_find_node_by_qn(store, NULL, qn, &node).
    SQL "WHERE project = NULL" never matches (SQL null semantics).
  - Test setup uses cbm_mcp_server_set_project("validation-test") which sets
    current_project but NOT session_project, so cold-start project fix does
    not fire, leaving pe.value = NULL.
  - Fix: pass project="validation-test" in test args so pe.value is set.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-06 01:40:36 -04:00
Andrew Hundt 9b0537519f feat(mcp): add pattern OR-search+search_in+case_sensitive to search_code_graph; qualified_name to trace; compact/sort/include_deps config; cold-start session fix
- search_code_graph: pattern param (OR-search name+qn), search_in='source' dispatch,
  case_sensitive, summary=bool alias, compact/sort_by/include_deps config defaults
- trace_call_path: qualified_name param with QN-first lookup via B-tree,
  compact config default, accepts function_name OR qualified_name
- get_code: compact config default
- store: pattern OR clause in SQL builder, pattern field in search params
- inject_context_once: auto_indexing status + actionable guidance
- REQUIRE_STORE: improved auto-index failure error message
- schemas: added search_in, pattern, case_sensitive, summary, max_rows to search;
  qualified_name to trace; compact to get_code
- config registry: compact, default_sort_by, default_include_dependencies entries
- tests: 9 new TDD tests (pattern_or, case_sensitive, source_search, summary_alias,
  config_compact, config_sort_by, trace_qualified_name, regression)

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-05 00:40:56 -04:00
Andrew Hundt 6026fa6647 fix(mcp): accept sort_by calls/linkrank, auto-convert glob wildcards, fuzzy trace_call_path
Three bugs fixed in MCP tool validation layer:

1. sort_by: validator only checked 3 of 5 valid values (relevance/name/degree).
   Added missing strcmp for 'calls' and 'linkrank' at mcp.c:1625-1627.

2. name_pattern/qn_pattern: glob wildcards (*tool*) rejected as invalid regex.
   On regex compile failure, glob_to_regex() converts bare * -> .* and ? -> .
   before retrying. Valid regex patterns have zero overhead (no conversion).
   Applied to both name_pattern and qn_pattern validation blocks.

3. trace_call_path: exact case-sensitive name match too strict.
   On node_count==0, fallback to cbm_store_search with case_sensitive=false
   and min/max_degree=-1 (no degree filter). Uses found result's project for
   the re-query to handle NULL project correctly. Zero overhead on exact hit.

TDD: 10 new tests in test_input_validation.c covering all three bugs:
- f6_sort_by_calls_accepted, f6_sort_by_linkrank_accepted
- f9_glob_star_autoconverted, f9_glob_question_autoconverted,
  f9_valid_regex_still_works, f9_truly_invalid_pattern_still_errors,
  f9_qn_pattern_glob_autoconverted
- trace_case_mismatch_finds_via_fallback, trace_exact_match_still_works,
  trace_truly_missing_still_errors

All 2252 tests pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-04-03 21:15:01 -04:00
Andrew Hundt a775040358 fix(mcp,store,pagerank,pipeline): 18 bugs fixed, DF-1 degree precompute, pass_normalize, 11 TDD tests
Phases 1-8 from comprehensive plan (notes/2026-03-26-0013-plan-*.md):

Phase 1 — Input validation (F1,F4,F6,F7,F9,F10,F15):
  mcp.c: empty label→NULL, limit≤0→default, sort_by/mode enum validation,
  regex pre-validation via cbm_regcomp, depth clamp, direction validation

Phase 2 — B7 Cypher param fix + CQ-2 project expansion:
  mcp.c:handle_query_graph reads "cypher" first with "query" fallback,
  uses resolve_project_store for "self"/"dep"/path shortcuts

Phase 3 — DRY resolve_project_store in 5 handlers:
  handle_get_graph_schema, handle_index_status, handle_get_architecture,
  handle_get_code_snippet, handle_index_dependencies

Phase 4 — DF-1 degree precompute (100× faster queries):
  store.c: node_degree table DDL, search SELECT uses LEFT JOIN with
  HC-6 fallback to edge COUNT, cbm_store_node_degree reads precomputed
  table, arch_hotspots uses nd.calls_in, arch_boundaries adds behavioral types
  pagerank.c: is_calls field, degree accumulation during edge iteration,
  node_degree batch INSERT after LinkRank, OOM-safe allocations

Phase 5 — B2/B5 name-based caller fallback:
  pass_calls.c: 3-step resolution (exact QN → shared helper → Module)
  graph_buffer.c: cbm_gbuf_resolve_by_name_in_file DRY helper (HC-1)

Phase 6 — B17/B13 class-method edge repair:
  NEW pass_normalize.c: enforces I2 (Method→Class) and I3 (Field→Class)
  invariants via QN prefix + name+label+file fallback. O(M+F) runtime.
  pipeline.c: normalize pass before dump. Makefile.cbm updated.

Phase 7 — CBMLangSpec section_node_types field:
  lang_specs.h: added section_node_types (17th field)
  lang_specs.c: all 64 language specs updated with NULL initializer

Phase 8 — IX-1..3 indexing pathway fixes:
  mcp.c: autoindex_failed + just_autoindexed flags in server struct,
  REQUIRE_STORE captures pipeline return code, build_resource_status
  shows "indexing" state + failure detail + action_required hints

Additional fixes:
  G1: summary mode adds results=[] + results_suppressed=true
  CQ-3: Cypher + filter params produces warning

Tests: 2238 pass (11 new in test_input_validation.c covering F1,F6,F9,
F10,F15 edge cases, G1, CQ-3, IX-2). Updated test_store_nodes.c for
total degree. Updated test_token_reduction.c for G1 results key.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-03-26 01:59:37 -04:00