Commit Graph
2366 Commits
Author SHA1 Message Date
Andrew Hundt 2aaf44d730 fix(cross-repo): honor name override, report missing projects, join full-URL routes
Four defects found while exercising cross-service route linking end to end:

1. index_repository mode=cross-repo-intelligence parsed the name override and then ignored it, deriving the project from repo_path, so it matched (or auto-created) a different project than the one indexed under --name. handle_cross_repo_mode now resolves name via cbm_project_name_from_path + cbm_validate_project_name exactly like indexing, and the derived-name path no longer leaks the cbm_project_name_from_path allocation.

2. cbm_cross_repo_match opened project stores with create-on-open, so a never-indexed source or target name silently materialized an empty <name>.db and reported success with zero matches (observed as a database literally named ["*"].db). Missing sources set source_missing and run nothing; missing named targets are skipped and counted in targets_missing; neither creates a database. The MCP response surfaces targets_missing and returns an actionable error for a missing source.

3. cbm_cli_build_args_json wrapped a JSON-array-shaped value ('["*"]') as one literal array element; array-typed flags now parse all-string JSON array values into elements while plain values and repeated flags accumulate as before.

4. A caller using a full URL literal (requests.get("http://users-svc:5000/api/users")) minted a Route named by the raw URL that never merged with the registration's canonical /api/users Route, so caller->Route and handler->Route stayed disconnected. Route identity and display names now strip scheme/authority through cbm_pipeline_route_url_path (shared with pass_cross_repo, replacing its duplicate cr_url_path), and phase 2a attaches handlers to already-minted method-variant routes across ANY (GET registration covers the ANY variant; ANY registration covers each concrete verb) with RN_SOURCE_METHOD_VARIANT ownership for incremental cleanup.

Verification: new tests cli_build_args_json_json_array_value, tool_cross_repo_mode_honors_name_override, pipeline_full_url_call_joins_canonical_route, and repro_issue523_missing_projects_not_created each failed before their fix and pass after; suites under ASan/UBSan: cli 236, mcp 272, pipeline 385, parallel 36, edge_types_probe 57, route_canon 15, httplink 39, incremental 164, repro_issue523 6/6; full make -f Makefile.cbm test exits 0.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-21 18:53:32 -04:00
Andrew Hundt fdf3d5aab8 fix(install): recognize legacy enabled-true JSON MCP entries
Pre-consolidation installer releases wrote local-array MCP entries as {"enabled": true, "type": "local", "command": [binary]}. The strict ownership matcher rejects unknown members, so every reinstall reported 'error: agent_config agent=KiloCode op=mcp_install path=~/.config/kilo/kilo.jsonc' for a config the installer itself had written, and owned removal left the stale entry behind.

Add CBM_JSON_LIKE_VALUE_TRUE to the shared object matcher (literal true only, capture forbidden) and declare 'enabled' as an optional field in cbm_json_mcp_ownership_fields. Command ownership is still required; an entry with 'enabled': false or any other unknown member remains user-modified and is preserved, not overwritten.

Verification: new cli_json_mcp_migrates_legacy_enabled_true_entry test failed before the change and passes after; CBM_ONLY_SUITE=cli (235 passed), config_json_like (30), config_toml_edit (34), config_yaml_edit (43), agent_profiles (10), agent_clients (26), configlink (9) under ASan/UBSan; full make -f Makefile.cbm test exits 0; isolated install --force -y over the real legacy kilo.jsonc shape completes with zero agent_config errors and rewrites the entry canonically while preserving user keys.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-21 16:25:52 -04:00
Andrew Hundt e0537103b2 fix(install): pass installed binary path to Codex profile uninstall
The Codex agent-role renderer added in 7e6e867051d3 requires a command transport binary path for direct-access profiles, but uninstall_cli_agents built its cbm_tiered_profile_set_t for Codex without binary_path. cbm_render_graph_profile then returned NULL for each tier, every uninstall logged three 'agent_config op=agent_render' errors for the codebase-memory-scout/codebase-memory/codebase-memory-auditor TOML profiles, and cbm_cmd_uninstall exited 1, failing tests/test_cli.c cli_uninstall_removes_codex_json_hook_only.

Hoist the existing cbm_agent_installed_binary_path() call above the profile uninstall and pass installed_binary, matching the Codex install call site and the Kiro, Gemini, and Claude uninstall call sites.

Verification: CBM_ONLY_SUITE=cli make -f Makefile.cbm test (234 passed, ASan/UBSan); full make -f Makefile.cbm test exits 0 with the previously failing test now passing.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-21 15:59:42 -04:00
Andrew Hundt 078d6e72bf fix(install): load Hermes hooks and Codex agent roles
Hermes hook installation rejected the client's default plugins.enabled: [] setting because yaml_sequence_line_has_unsupported treated flow-sequence brackets anywhere in the document as structural ambiguity. Codex 0.144.6 also rejected all three generated agent role files with 'invalid transport' because their per-role MCP tables specified enabled_tools without a command transport.

Allow flow-sequence brackets while retaining fail-closed handling for block scalars, malformed target paths, ownership conflicts, symlinks, and concurrent edits. Render self-contained Codex MCP tables with the installed command, tier-specific --tool-profile arguments, and read-only enabled_tools. Recognize the exact prior transport-less role documents during upgrade and uninstall so modified user profiles remain preserved.

Verification: CBM_ONLY_SUITE=config_yaml_edit make -f Makefile.cbm test (43 passed, ASan/UBSan); CBM_ONLY_SUITE=agent_profiles make -f Makefile.cbm test (10 passed, ASan/UBSan); isolated Hermes install with plugins.enabled: []; live install across detected harnesses; codex doctor --summary (17 ok, 0 warn, 0 fail); scripts/check-source-safety.sh.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 22:53:48 -04:00
Andrew Hundt 861146d922 fix(cli): restore preset dispatch and semantic smoke checks
Commit de041755 added six atomic capability presets, but the current cbm_cmd_config dispatcher no longer routed 'config preset list' or 'config preset apply <name>'. Restore those commands through the existing cbm_config_apply_preset transaction, preserve environment-over-database priority, and return status 1 when an override prevents the requested effective configuration.

Keep the upstream-compatible four-row config list while reporting effective branch defaults, including auto_index=true. Replace nine stale 'Sessions and Subagents' heading assertions in scripts/smoke-test.sh with one check for the installed delegation contract emitted under 'Freshness and Delegation'.

Files: src/cli/cli.c; tests/test_cli.c; scripts/smoke-test.sh.

Verification: 234/234 CLI tests passed under ASan/UBSan; the -O2 production binary listed/applied presets and rejected an overridden tool_mode with status 1; the isolated agent-config install/uninstall smoke passed; scripts/check-source-safety.sh and git diff --cached --check passed.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 14:57:00 -04:00
Andrew Hundt c15b887c89 fix(install): restore Hermes delegation context guidance
Upstream's Hermes integration requires parent graph evidence to be passed through the delegate_task context argument because delegated child context is isolated. The consolidated skill text removed both actionable terms while scripts/smoke-test.sh retained that contract, producing FAIL 8x-i: Hermes delegation skill missing.

Restore the Hermes-specific instruction in src/cli/cli.c and assert delegate_task plus context remain present in tests/test_cli.c.

Verification:
- CBM_ONLY_SUITE=cli CBM_ONLY_TEST=skill_files_content build/c/test-runner: 1 passed
- CBM_ONLY_SUITE=cli build/c/test-runner: 233 passed
- bash scripts/smoke-test.sh build/c/codebase-memory-mcp --agent-config-only: Hermes checks 8x, 8x-i, and 8x-ii pass; the broader smoke then stops at the separate Kimi 8ak check

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 14:13:30 -04:00
Andrew Hundt d664dbc486 fix(install): migrate owned Claude and Codex state
src/cli/cli.c:3926 records the byte-exact SessionStart script emitted by the streamlined auto-index release, and cbm_install_session_reminder_script now accepts both released installer-owned variants while preserving edited files.

src/cli/config_toml_edit.c:987 adds a narrowly selected managed-block path that permits TOML child-before-parent ordering. cbm_upsert_codex_mcp at src/cli/cli.c:2247 uses it to replace the owned mcp_servers.codebase-memory-mcp root without deleting user-owned tools.query_graph approval policy.

tests/test_cli.c:4492 and tests/test_cli.c:5131 cover exact Claude hook migration plus idempotent Codex root migration with descendant policy preservation.

Verified: 233/233 CLI ASan/UBSan tests; 34/34 config_toml_edit ASan/UBSan tests; scripts/check-source-safety.sh; git diff --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 02:58:08 -04:00
Andrew Hundt ecebf00ada fix(benchmarks): prefer exact graph equality over stale metadata
graph_gate_for_publish_kind previously selected a scoped stale-view gate before checking exact canonical equality. This mislabeled exact semantic-disabled and minimal-indexing results and excluded them from Pareto analysis.

Return the canonical_graph gate when the full graph matches, and make summarize_group apply the same precedence when reprocessing retained result JSON produced by older harness versions. Preserve scoped stale-view handling for genuinely unequal graphs.

Add regression coverage in tests/test_benchmark_incremental_speed.py and tests/test_summarize_benchmark_results.py. Verified 122 benchmark/report tests, Ruff format and lint, git diff --check, and scripts/check-source-safety.sh.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 02:30:56 -04:00
Andrew Hundt e500b89a96 test(mcp): align fixtures with advertised tool arguments
tests/test_integration.c now sends trace_path depth instead of the never-advertised max_depth key. tests/test_depindex.c exercises dependency-aware trace_path through its supported graph contract without the search-only include_dependencies switch.

tests/test_input_validation.c requires strict rejection and supported-argument guidance for query_graph label. tests/test_index_resilience.c rebuilds its index_status arguments after reusing the buffer for query_graph, so it verifies persisted ignored paths instead of an unrelated validation error.

Verification: affected ASan/UBSan suites passed 6 index_resilience, 36 depindex, 52 input_validation, and 28 integration tests. The escalated full ASan/UBSan suite passed 7204 tests with one Windows-only skip and zero failures; scripts/check-source-safety.sh passed.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 01:23:32 -04:00
Andrew Hundt bf3d27c130 fix(mcp): refresh inline schema after external publication
A long-lived server cached query_graph's tools/list description after opening a project store. When a sibling CLI, MCP, or HTTP worker atomically replaced that database, graph queries reopened the new file but tools/list could continue advertising the old labels indefinitely because no in-process publication flag was available.

query_graph_tool_description at src/mcp/mcp.c:2959 now asks resolve_store to perform the existing O(1) backing-file identity check before serving a cached description. resolve_store marks the description stale on a replacement, then the request-owning thread closes the old read-only handle and rebuilds schema; unchanged relists perform no graph scan, database copy, write, or cross-process lock. mcp_copy_schema_project_name copies only the bounded project name so reopening cannot invalidate the argument pointer.

tests/test_mcp.c:11664 extends the atomic-publication fixture with distinct before/after labels and relists before any graph query. The test failed because AfterExternalLabel was absent, then passed 1/1 under ASan/UBSan. Verified scripts/check-source-safety.sh, git diff --check, git clang-format --diff, and staged private-path scan.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 00:43:20 -04:00
Andrew Hundt 393d3a297f fix(mcp): report blocked automatic indexing by active tool mode
Previously, build_project_list_error_srv discarded whether automatic indexing was disabled or stopped by auto_index_limit and always recommended index_repository, even when streamlined tools/list hid that tool. Double-quoted path examples could also invalidate the nested structured JSON response.

Record request-thread-owned block state in maybe_auto_index and cbm_mcp_auto_index_within_limit. mcp_index_recovery_action at src/mcp/mcp.c:3937 now uses the existing profile/reveal authorities: classic or revealed streamlined mode calls index_repository directly, unrevealed streamlined mode calls _hidden_tools and refreshes tools/list first, and profiles without that tool use the CLI. Existing synchronous first-use recovery still runs before any user action is emitted. Centralize streamlined/classic config values in src/cli/cli.h:383.

tests/test_mcp.c:9617 covers auto_index=false, bounded file-limit rejection, classic, and revealed streamlined behavior; each response must parse as JSON with structuredContent and must not claim auto_indexing. Verified: focused ASan/UBSan test 1/1; scripts/check-source-safety.sh; git diff --check; git clang-format --diff; staged private-path scan.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-20 00:32:11 -04:00
Andrew Hundt b6e8c8975f fix(mcp): reopen query stores after external publication
Before this change, src/mcp/mcp.c:3638 trusted a cached SQLite handle until an in-process notification arrived. A sibling CLI or MCP publisher cannot send that notification, so atomic database replacement left a long-lived server reading the old unlinked inode. The search_code handler at src/mcp/mcp.c:13580 also bypassed the canonical startup-index join and could return project not found or not indexed while indexing was active.

Add portable path identity reads in src/foundation/compat_fs.c:74 and :735, record identity only for query-only stores in src/store/store.c:1365, and reopen a replaced generation from resolve_store on the request-owning thread. Existing atomic writer publication remains unchanged; the read path adds one constant-time metadata probe and no database write, graph scan, process-wide lock, or unrelated-project serialization.

Route search_code through resolve_project_store, report its resolved target in JSON and TOON, release syntax-kind thread caches on every auto-index exit, and notify publication only for responses accepted by cbm_mcp_index_response_published.

TDD fixtures: tests/test_mcp.c:6891, :9517, and :11534. Verification: ASan/UBSan MCP 270/270, tool consolidation 113/113, CLI 232/232; focused TSan tests passed; macOS leaks reported 0 leaks/0 bytes for startup indexing and external replacement; source safety, git diff --check, and git clang-format --diff passed.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 23:44:20 -04:00
Andrew Hundt a620b17b16 perf(cypher): hash aggregate group lookups
Before this change, execute_return_agg and with_agg_find_or_create compared each binding against every existing group. A 600-distinct-group fixture performed 179,700 string comparisons, and production queries over the 20,423-node repository store took 3.373 s for direct qualified-name grouping and 2.793 s through WITH.

Add one aggregate_group_index_t backed by foundation/hash_table.c for both execution paths. Aggregate entries own exact-length keys that remain stable when result arrays grow; the Verstable table borrows those keys and maps them to first-seen array indexes. If index allocation fails, execution retains the exact linear fallback. This changes group lookup from O(rows x groups) to expected O(rows), preserves deterministic emission, and removes the fixed 1 KiB inline key from every group.

tests/test_cypher.c adds a 600-group RETURN/WITH fixture that crosses the 256-entry growth boundary, checks exact ordered results, and caps logical lookups at 1,200. Existing null/empty, node-identity, deadline, JSON/TOON, and MCP compatibility suites remain green.

Verification: Cypher ASan/UBSan 188/188; Cypher TSan 188/188; MCP, tool-consolidation, and CLI suites green; focused macOS leaks 0 leaks/0 bytes; source-safety and git diff checks pass. Five optimized repetitions measured 0.393 s median direct grouping and 0.413 s median WITH grouping on the same store.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 22:19:05 -04:00
Andrew Hundt e9f3ffa88b fix(mcp): reject undeclared tool arguments
Previously, tools/list left top-level input objects open and cbm_mcp_handle_tool dispatched raw JSON without checking property names. A search_code call containing repo_path could therefore ignore the caller's intended project and return a plausible empty result. The canonical schemas also omitted the implemented index_repository.format and index_status.verbose inputs.

Add mcp_add_tool_input_schema to close every classic, streamlined pre-reveal, streamlined post-reveal, and _hidden_tools input schema. Add mcp_validate_tool_argument_keys before dispatch so direct, stdio, HTTP, and raw-JSON CLI calls return an isError:true result naming the unknown key and canonical supported keys. Keep the public schema registry as the property authority; output schemas remain open.

Preserve only verified, unambiguous raw-JSON compatibility: string-valued project_name/project_id/projectName map to project; query_graph.cypher maps to query per 32a3820c; search_code search_in=source remains from the 3d133a3c to 39539eff split. Other search_in values name search_graph, and ignored query_graph label input is rejected.

MCP Tool Execution Error behavior follows https://modelcontextprotocol.io/specification/2025-11-25/server/tools. Closed no-argument schemas follow https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1613.

Verification: ASan/UBSan MCP 267/267; TSan MCP 267/267; CLI 232/232; tool consolidation 113/113; HTTP 44/44 with one platform skip; production smoke 35/35; four focused macOS leaks checks each 0 leaks/0 bytes; scripts/check-source-safety.sh; optimized real-store CLI query_graph and typo-rejection dogfood.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 21:46:24 -04:00
Andrew Hundt fced498a32 fix(cypher): preserve null and entity identity in aggregates
Previous behavior:
- OPTIONAL MATCH null bindings were encoded as empty strings, so count(expr), count(DISTINCT expr), and collect(expr) counted absent values.
- WHERE and coalesce treated a valid JSON empty string as null.
- WITH aggregation grouped distinct same-named nodes together by display name.
- edge_prop used process-global rotating scratch buffers across concurrent requests.

Changes:
- src/cypher/cypher.c adds presence-aware json_extract_prop_ex, node_prop_ex, edge_prop_ex, and binding_get_virtual_ex lookups plus one null bit per bounded binding slot.
- with_agg_accumulate and ret_agg_accumulate skip null expressions while count(*) retains row semantics; WHERE and coalesce preserve valid empty strings.
- group_key_append keys nodes and edges by canonical store ID and length-prefixes scalar values, preventing name and delimiter collisions.
- edge projection scratch storage is thread-local; aggregate null vectors follow existing group cleanup ownership.
- tests/test_cypher.c covers OPTIONAL null aggregates, post-WITH counts, numeric aggregate defaults, empty/null/missing properties, predicates, coalesce, and same-name node grouping.

Complexity and lifecycle:
- Property presence adds no per-row allocation and aggregation retains its existing row/group traversal order.
- Binding storage grows by CYP_MAX_VARS booleans; edge scratch remains fixed-size per thread.
- The one per-group null vector is released with the existing aggregate state.

References:
- https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf
- https://neo4j.com/docs/cypher-manual/25/functions/aggregating/
- https://neo4j.com/docs/cypher-manual/current/values-and-types/working-with-null/

Verification:
- CBM_ONLY_SUITE=cypher build/c/test-runner: 187 passed under ASan/UBSan.
- CBM_ONLY_SUITE=mcp build/c/test-runner: 262 passed under ASan/UBSan.
- bash scripts/check-source-safety.sh: passed.
- git diff --cached --check: passed.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 20:47:31 -04:00
Andrew Hundt 5d164c7efb test(cli): assert exact query row-cap contract
tests/test_cli.c:6554 still required the word 'upstream' after commit aefbec05 changed query_max_rows from an unsafe scan prefix to an exact post-selection result ceiling. That stale wording failed the CLI sanitizer suite even though the registry described the corrected behavior.

Assert the public invariants instead: query_max_rows is a result-row cap, lowering it does not change which rows match, and Cypher LIMIT cannot bypass it. The focused contract passes 1/1 and the complete CLI plus agent-client ASan/UBSan gate passes 232/232; scripts/check-source-safety.sh and git diff --check also pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 19:40:56 -04:00
Andrew Hundt 9c9132650e fix(smoke): correlate MCP responses across notifications
scripts/smoke-invariants.sh now reads JSON-RPC stdout until the response id matches the request, skips id-less notifications within one total deadline, and fails immediately on malformed stdout or a different response id. This prevents notifications/tools/list_changed emitted after index_repository from being misclassified as the search_graph response and shifting the remaining tool sweep.

The classic registry check now derives its count from the named tool list and invokes check_index_coverage, matching src/mcp/mcp.c. Production verification at b713d032 changed from 31 passes/3 false failures to 35 passes/0 failures; bash -n, scripts/check-source-safety.sh, and git diff --check passed. Git history identifies upstream commit cee59ee2 as the original cross-platform smoke authority.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 19:31:42 -04:00
Andrew Hundt 277b8f55c2 fix(pipeline): reextract ObjectScript consumers after include changes
src/pipeline/pipeline_incremental.c now detects changed, deleted, and renamed .inc macro context and expands the affected set to discovered ObjectScript files before dirty-ledger and publication routing. This matches cbm_build_macro_table_from_files, whose repository-wide macro table has no per-include consumer map, while leaving other-language increments on the existing O(changed-files) path and reusing exact-delta bounds plus containment fallback.

tests/test_pipeline.c extends the cleanup-safe ObjectScript oracle to prove changed and deleted includes replace or remove stale CALLS edges and remain graph-equal to a fresh rebuild. Verification: pipeline ASan/UBSan 384/384; focused changed/deleted include TSan passed; focused macOS leaks reported 0 leaks for 0 total leaked bytes; scripts/check-source-safety.sh passed; git diff --check passed.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 19:22:31 -04:00
Andrew Hundt 8f0f6fc834 fix(pipeline): retain ObjectScript macros during incremental extraction
Previously, src/pipeline/pass_definitions.c populated the sequential result cache without ctx->macro_table, so pass_calls reused unexpanded $$$ calls. Exact incremental runs also extracted only the changed .cls slice without unchanged .inc definitions, dropping the CALLS edge that a clean rebuild produced.

Route sequential extraction through cbm_extract_file_with_options_ex, declare cbm_build_macro_table_from_files once in pipeline_internal.h, and let parallel extraction borrow an immutable caller-owned table. pipeline_incremental.c now builds repository macro context only when the changed slice contains ObjectScript, releases it on every return path, and otherwise preserves changed-file setup complexity.

tests/test_extraction.c verifies the expanded callee remains scoped to MyApp.Caller.Run. tests/test_pipeline.c verifies full resolution, exact incremental publication, CALLS-edge retention, clean-rebuild graph equality, and default fixture cleanup.

Verification: ASan/UBSan pipeline 382/382, extraction 313/313, parallel 36/36; focused TSan passed; macOS leaks reported 0 leaks for 0 total leaked bytes; bash scripts/check-source-safety.sh; git diff --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 19:04:23 -04:00
Andrew Hundt b3a7dc5dc1 feat(benchmark): automate versioned resumable campaigns
Previously scripts/run-benchmark-campaign.py required callers to hand-build binaries, author a plan or matrix spec, and choose a campaign root. Timestamp-only artifact names did not distinguish source time from execution time, repeated preparation rebuilt candidates, and absolute checkout paths prevented portable resume.

Add no-argument quick and --full campaign presets, peeled commit/tree identities, detached candidate worktree reuse, canonical Makefile.cbm CFLAGS_PROD capture, streamed production build logs, binary hashing, clean tracked-tree checks, and tamper-aware candidate cache records. Name new campaign evidence with v0001 definition, runset content ID, explicit commit time, and explicit generation time; identity_version=2 normalizes canonical path mappings while legacy cells keep their prior identity document.

scripts/benchmark-incremental-speed.py now uses explicit sortable UTC filenames for retained failure and scaling evidence. tests/test_benchmark_campaign.py covers tag peeling, worktree reuse, stale binary rebuild, quick/full matrix construction, path remapping, bounded names, clean-tree policy, and legacy identity compatibility.

Verification: uv run python -m unittest tests.test_benchmark_campaign tests.test_benchmark_incremental_speed tests.test_summarize_benchmark_results tests.test_autotune (159 passed); ruff check on all three changed files; git diff --check; bash scripts/check-source-safety.sh.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 17:49:09 -04:00
Andrew Hundt 4d44007764 fix(benchmarks): reject live caches and surface invalid runs
scripts/benchmark-incremental-speed.py validates each candidate cache before setting CBM_CACHE_DIR. It rejects the inherited live cache and directories containing an existing project database, preventing cross-version recovery logic from renaming or mutating user data.

scripts/summarize-benchmark-results.py adds separate bold graph, result/quality, and run/lifecycle error columns to the headline table. Requested cleanup failures now produce REJECT: lifecycle cleanup, so historical speedups and Pareto selection remain unavailable to invalid runs.

tests/test_benchmark_incremental_speed.py and tests/test_summarize_benchmark_results.py reproduce both prior contracts. Verification: 149 benchmark/campaign/report/autotune unittests; Ruff format check on four changed files; scripts/check-source-safety.sh.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 16:59:46 -04:00
Andrew Hundt 0a988e0fa4 fix(cypher): apply row caps after exact selection
Previous behavior: scan_pattern_nodes() limited unlabeled candidates to max_rows * 10 before WHERE evaluation; execute_return_simple() projected max_rows bindings before ORDER BY; and an explicit Cypher LIMIT could bypass max_rows. An exact named-function lookup beyond the seed prefix returned zero rows, while a descending order query ranked only the prefix.

Push mandatory, non-negated file_path CONTAINS conjuncts into SQLite with literal instr() semantics, while retaining the full C evaluator as semantic authority. Restrict bounded initial scans to prefix-preserving query shapes; predicates, relationships, aggregation, DISTINCT, ORDER BY, SKIP, and later stages evaluate the complete eligible set after exact SQL reduction.

Make max_rows and query_max_rows authoritative result ceilings: Cypher LIMIT may lower but not bypass them. Align the shared MCP definition and CLI config registry so streamlined, classic, and CLI surfaces describe the same contract.

TDD covers a 65-node exact lookup beyond the former 50-node prefix, global ORDER BY selection, unsafe OR pushdown exclusion, literal percent/underscore CONTAINS behavior, configured cap enforcement, and streamlined/classic schema parity. Verification: 531 Cypher/companion and 612 MCP/schema/companion ASan/UBSan tests passed.

Retained optimized large-graph validation passed graph and quality gates: query_graph 2.367 ms, exact incremental indexing 363 ms at 71 MB peak, full indexing 6494 ms at 1332 MB peak, and 17.89x full-over-incremental speedup. The benchmark artifact remains untracked.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 16:38:27 -04:00
Andrew Hundt 1bb6fa6b9e perf(store): skip active overlay CTE on clean graphs
Previous behavior: cbm_store_get_overlay_node_view_summary() materialized active_node_candidates and applied ROW_NUMBER ownership ranking for every read, even when overlay_generations contained no overlay_ready row. On the retained 15,664-node graph this added about 120 ms to search_graph, search_code, route, and other MCP reads.

Use the existing indexed cbm_store_count_overlay_generations() authority to gate the active view. When no ready generation exists, reuse cbm_store_count_nodes() for the exact canonical and total counts; projects with ready overlays retain the complete tombstone and ownership CTE.

Add an sqlite3_trace_v2 regression guard in tests/test_store_nodes.c that rejects active_node_candidates on the clean path while requiring the canonical count query. The same test continues to verify exact counts for successive ready overlays.

Verification: CBM_ONLY_TEST=overlay_node_view_summary make -f Makefile.cbm test-focused TEST_SUITES=store_nodes; ASan/UBSan store_nodes, mcp, and tool_consolidation suites; optimized self-dogfood MCP smoke with search_graph median reduced from 120.149 ms to 3.825 ms and search_code from 131.946 ms to 10.436 ms while the exact incremental gate passed.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 15:48:01 -04:00
Andrew Hundt 8fa20d99f6 fix(benchmarks): separate one-shot and repeated query latency
Previous behavior: each query oracle measured one default-format call and one JSON quality call, then reports treated the first call as query latency. One-shot schema context, watcher startup, and cache warming could land on either call, producing apparent 16 ms versus 261 ms regressions from the same underlying path.

Run three canonical JSON trials after the default-format call for both MCP and CLI. Retain every elapsed value and canonical payload hash, report min/median/max plus byte-equality, and preserve the first JSON timing for backwards compatibility. The summarizer now labels one-shot default latency separately and uses the repeated JSON median for cross-candidate query tables.

Add fake-client timing/hash coverage in tests/test_benchmark_incremental_speed.py and cold-versus-repeated aggregation coverage in tests/test_summarize_benchmark_results.py. Verification: 144 benchmark, campaign, and report tests pass; ruff format reports all four changed files formatted; git diff --check passes.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 15:29:55 -04:00
Andrew Hundt 736e1b72af fix(benchmarks): reject unapplied disabled capabilities
Previous behavior: a matrix profile could set capabilities such as auto_index_deps=false as descriptive metadata while leaving config_overrides empty. The archived plan then claimed an ablation that the benchmark command never applied; retained worker timings exposed dependency and PageRank phases still running.

Make expand_matrix_spec fail closed when config_profile=default claims a disabled capability without the identical false config override. Named profiles remain compatible because their canonical settings are resolved by benchmark-incremental-speed.py.

Add test_default_profile_rejects_disabled_capability_without_config_override in tests/test_benchmark_campaign.py. Verification: all 26 campaign-runner tests pass and git diff --check passes.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 15:22:12 -04:00
Andrew Hundt 68736f83c8 fix(benchmarks): read persisted derived-view freshness
Previous behavior: self-dogfood graph validation inferred every stale derived view from task-specific MCP warnings. A changed-file query does not mention semantic_edges, so exact incremental runs with intentionally deferred semantic edges were rejected as canonical graph failures despite derived_view_state recording semantic_edges as stale.

Add persisted_stale_views() in scripts/benchmark-incremental-speed.py to read the canonical SQLite freshness ledger through a read-only connection and union it with response-local warnings. Missing legacy derived_view_state tables remain compatible, while core node, edge, and file-hash mismatches are still rejected after excluding only declared SEMANTICALLY_RELATED rows.

Add tests/test_benchmark_incremental_speed.py coverage for project-scoped stale/complete rows and retain tests/test_mcp.c coverage proving moderate incremental_exact publication persists semantic_edges=stale and query_graph reports it for semantic queries.

Verification: 76 benchmark harness tests passed; 262 ASan/UBSan MCP tests passed; focused exact-moderate MCP test passed; git diff --check passed. The repository-wide lint-format target still reports pre-existing formatting violations outside these changes.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 14:58:47 -04:00
Andrew Hundt 4963f924dd test(mcp): measure capability contracts and cleanup
Add run_mcp_surface_parity() coverage for classic discovery, streamlined discovery before _hidden_tools, and relisting in the same process after reveal. Compare complete tools/list contracts, validation-equivalent get_code aliases, capability outcomes, bounded hidden-handler recognition, and notifications/tools/list_changed without presenting dispatch recognition as behavioral proof.

Make McpClient cleanup record subprocess return codes and require both reader threads and server processes to be reaped. Close SQLite snapshot and cloned-fixture connections explicitly so ResourceWarning-as-error runs detect ownership regressions.

Render capability outcomes before tool counts in scripts/summarize-benchmark-results.py, retain response bytes and single-observation latency with explicit evidence limits, and document the parity mode in docs/BENCHMARK_CAMPAIGN.md.

Verification: 141 warning-clean benchmark/campaign/summarizer tests; optimized build/c/codebase-memory-mcp parity smoke passed for 12 capability groups with full post-reveal classic contract parity and reaped lifecycle; ruff check; source-safety; git diff --cached --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 14:28:38 -04:00
Andrew Hundt 617f72783d fix(benchmarks): isolate PageRank autotune campaigns
Replace scripts/autotune.py's global config mutation, mutable local-repository lookup, architecture resource request, and source-tree result file with build_matrix_spec() over scripts/run-benchmark-campaign.py.

Bind each plan to the full revision, binary SHA-256, compiler, flags, harness hash, candidate-default/rank-disabled controls, and PageRank parameter profiles. Store content-addressed manifests under the ignored durable campaign root and transfer execution with os.execv so the shared runner owns isolation, resumption, logs, and cleanup.

Add tests/test_autotune.py for shared-runner plan validation, paired profile expansion, and absence of the removed resources/read, atexit, Path.home, and database-deletion paths.

Verification: uv run python -W error::ResourceWarning -m unittest tests.test_autotune (3 tests); ruff check scripts/autotune.py tests/test_autotune.py; git diff --cached --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 14:27:53 -04:00
Andrew Hundt 45c11176c9 merge(upstream): retain streamlined APIs with indexed impact analysis
Merge upstream/main 7d6cdb23 into api-consolidation 0fcb7e10 as a tested capability superset.

Retain the branch's streamlined, classic, and hidden-reveal MCP surfaces; JSON/TOON output; read-only Cypher execution; semantic search; dependency indexing; PageRank; incremental indexing; and modular harness installer. Integrate upstream ObjectScript routine/UDL extraction, macro and return-type call resolution, Python alias and C++ header resolution, atomic SQLite publication, SCC/cycle reporting, Cypher deadlines, and bounded multi-source impact traversal.

Fix merge-specific defects in handle_detect_changes project validation, cbm_pipeline_release_objectscript_tables ownership, no-sanitizer test defines, focused-suite registration, portable config publication, and installed streamlined guidance. The guidance keeps auto-index/get_code/query_graph discovery while adding coverage tiers and removes the stale fixed row ceiling.

Verification: ASan/UBSan full runner 7,155 passed with 27 sandbox-loopback cases separately passing in the escalated HTTP suite (44 passed, 1 Windows-only skip); TSan 449 passed; incremental leak gate 514 passed with 0 leaks; MCP 261; CLI 232; extraction 313; tool consolidation 113; Cypher 179; SQLite writer 16; configuration editors 127; integration 28; language contracts 38; PageRank 60. scripts/check-source-safety.sh and git diff --cached --check pass.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 14:09:36 -04:00
Andrew Hundt 051dfacc3b test(tool): remove username from prefix collision context
tests/test_tool_consolidation.c:cross_project_search_not_confused_by_prefix now describes the path-derived project-name collision without embedding a local username, repository path, or private graph size. The executable myapp/myapp-other-project fixture and prefix-boundary assertions are unchanged.

Verification: cross_project_search_not_confused_by_prefix passed (1 selected, 349 filtered); scripts/check-source-safety.sh passed; git diff --check passed; tracked-source scan found no athundt, justtalk, processtree-rs, or private feedback filenames.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 08:59:11 -04:00
Andrew Hundt ce9593da09 test(mcp): pin dynamic query schema freshness
tests/test_tool_consolidation.c adds query_graph_description_uses_ready_overlay_view, which proves tools/list describes the ready overlay that query_graph executes instead of canonical rows replaced for the same file.

tests/test_mcp.c adds mcp_published_schema_refreshes_description_once and extends mcp_delete_project_sends_list_changed. These protocol fixtures prove publication reopens the store, repeated publication emits one notifications/tools/list_changed event, and project deletion removes cached vocabulary from the next tools/list response.

Verification: 590 MCP/consolidation tests; full ASan/UBSan suite (6867 passed, 1 skipped); TSan suite (449 passed) plus all three exact freshness cases; macOS leaks reports 0 leaks for 0 bytes for each exact case.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 08:53:37 -04:00
Andrew Hundt 77c38ce863 fix(cypher): preserve qualified names across aggregate WITH
Previous behavior:
- with_add_vbinding_var stored a WITH alias in cbm_node_t.qualified_name to share allocation ownership.
- RETURN g.qualified_name after WITH g, COUNT(*) therefore returned the alias literal g instead of the indexed qualified name.

Changes:
- Track heap-owned variable aliases with binding_t.var_name_owned.
- Copy and free aliases independently from cbm_node_t fields.
- Leave aggregate node stubs eligible for the existing id-based indexed property fetch.

Tests:
- cypher_exec_with_node_groupvar_prop reproduces g versus test.ValidateOrder and now passes.
- build/c/test-runner cypher: 519 passed.
- make -f Makefile.cbm test: 6865 passed, 1 skipped under ASan/UBSan.
- macOS leaks-at-exit exact regression: 0 leaks, 0 leaked bytes.
- Optimized -O2 query_graph returns qualified names in both TOON and JSON.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 08:02:40 -04:00
Andrew Hundt 8849410e2d fix(cypher): execute post-WITH stages without truncating fan-out
Previous behavior:
- src/cypher/cypher.c parsed MATCH after WITH without executing it against projected bindings.
- OPTIONAL MATCH predicates were applied after null extension, multi-key ORDER BY was not represented, and relationship expansion silently capped each seed at ten paths.

Changes:
- Add next_stage ownership and iterative execution/free traversal for MATCH and OPTIONAL MATCH after WITH, including UNION transfer and active-overlay checks.
- Apply OPTIONAL predicates to candidate paths before null extension.
- Represent up to 32 projected ORDER BY keys and use stable O(rows log rows) merge sorts with O(rows) pointer/index scratch.
- Replace fixed-factor node and relationship buffers with ceiling-bounded geometric growth shared by canonical and active-overlay paths.
- Keep classic and streamlined query_graph descriptions aligned with the implemented grammar.

Tests:
- make -f Makefile.cbm test: 6865 passed, 1 skipped under ASan/UBSan.
- build/c/test-runner cypher: 519 passed.
- build/c/test-runner mcp tool_consolidation: 588 passed.
- scripts/check-source-safety.sh and scripts/check-nolint-whitelist.sh pass.

Semantics checked against Neo4j Cypher documentation:
https://neo4j.com/docs/cypher-manual/current/clauses/with/
https://neo4j.com/docs/cypher-manual/current/clauses/match/
https://neo4j.com/docs/cypher-manual/current/clauses/order-by/

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 07:34:54 -04:00
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 ad9d637402 feat(config): add default_response_format (toon/json) canonical enum and key
query_graph and related tools previously had no config-controlled
default output encoding, and the TOON/JSON format enum lived nowhere
canonical, risking drift between the wire strings different call sites
might use. Add cbm_mcp_output_format_t {CBM_MCP_OUTPUT_TOON,
CBM_MCP_OUTPUT_JSON, CBM_MCP_OUTPUT_INVALID} plus the "toon"/"json" wire
string constants to mcp.h with a drift-prevention comment, as the single
source of truth for every response-format call site.

Add the default_response_format config key to CBM_CONFIG_REGISTRY in
cli.c (default "toon", validated to "toon" or "json" only via
cbm_config_set), so `codebase-memory-mcp config set default_response_format
json` persists a default and CBM_MCP_OUTPUT_INVALID.

Verification: make -f Makefile.cbm test — 6850 passed, 1 skipped, 0
failed, ASan/UBSan clean.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 01:10:36 -04:00
Andrew Hundt 580ecdd161 refactor(store): structured relationship facts and declared property-key registry
get_schema/get_schema_overlay_view previously returned relationship
patterns as preformatted "(A)-[T]->(B) [Nx]" strings via
schema_format_rel_pattern, forcing every consumer to re-parse text to get
at the source label, edge type, target label, or observed count
individually. Replace it with a structured cbm_schema_relationship_t
{source_label, edge_type, target_label, observed_count} array
(schema_collect_rel_patterns_from_stmt, store.h/store.c), and centralize
the previously-duplicated SCHEMA_MAX_JSON_KEYS/SCHEMA_REL_PATTERN_LIMIT
enum constants as CBM_STORE_SCHEMA_PROPERTY_KEY_LIMIT and
CBM_STORE_SCHEMA_RELATIONSHIP_PATTERN_LIMIT beside the existing schema
caps in store.h. Add const-correct cbm_store_schema_node_base_properties/
cbm_store_schema_edge_base_properties accessors so base-column lists have
one owner instead of being redeclared per caller.

Add a declared node/edge property-key registry
(schema_declared_node_property_keys/schema_declared_edge_property_keys,
~107 entries, sorted and duplicate-free) enumerating every JSON property
key any pipeline pass or git-context writer can emit on a node or edge,
exposed via cbm_store_schema_declared_node_property_keys/
cbm_store_schema_declared_edge_property_keys. Each row carries a trailing
comment naming its writer for provenance. The registry is a maintenance
contract, not a runtime dependency: nothing outside the new contract test
reads it. Document the four places that must stay in sync (writer,
store.c table, store.h accessor comment, contract test) in both store.h
and CLAUDE.md.

Add tests/test_schema_declared_property_keys.c: indexes mixed-language
fixtures (Python/TypeScript/Rust) plus a best-effort real git repo
(git init/commit via the portable cbm_git_drain_command, matching
test_git_context.c's tolerance for "git not available") to also
forward-verify the ~10 git_context registry rows, asserts every
discovered non-base property key is in the declared registry for its
entity kind, asserts both tables are sorted/duplicate-free, and reverse-
pins a sample of registry keys to catch dead rows. Cap-blindness guarded:
a label/type that hits the discovery limit fails loudly instead of
passing on unprovable completeness.

Register the new suite in Makefile.cbm (TEST_STORE_SRCS) and
tests/test_main.c.

Verification: make -f Makefile.cbm test — 6850 passed, 1 skipped, 0
failed, ASan/UBSan clean. make -f Makefile.cbm test-leak — 0 leaks for 0
total leaked bytes.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-19 01:10:25 -04:00
Andrew Hundt b835b5e8b2 fix(cypher): defer unbound relationship predicates
execute_single previously evaluated the complete WHERE tree while only the first MATCH alias was bound. A target-alias predicate such as NOT callee.file_path CONTAINS "tests/" therefore rejected every seed before CALLS expansion and returned zero rows.

Add allocation-free three-valued partial evaluation in src/cypher/cypher.c: bound leaves prune early, unbound node or edge aliases remain unknown, and AND/OR/NOT/XOR preserve boolean semantics and safe short-circuiting. Register eval_expr_partial in src/foundation/recursion_whitelist.h.

Add four relationship-target and mixed-alias regressions in tests/test_cypher.c. Verified 159/159 Cypher tests under ASan/UBSan, source-safety and whitelist checks, an -O2 production build, and the formerly empty hotspot query returning 20 bounded rows.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 19:16:09 -04:00
Andrew Hundt a4b3a71e81 fix(cypher): reject unconsumed query tokens
cbm_parse previously returned success when parse_return_item stopped before postfix list indexing or an arbitrary trailing token. A query containing labels(n)[0] could therefore ignore its alias, aggregate, ordering, and limit and execute as an unbounded projection.

Require TOK_EOF after parse_post_where in src/cypher/cypher.c and transfer the parent cursor after a recursively validated UNION tail. Route both list-index rejection paths through one diagnostic that gives supported labels(n) and n.label/count(*) rewrites.

Add parser regressions in tests/test_cypher.c. Verified 155/155 Cypher tests under ASan/UBSan, all three UNION tests, source-safety, an optimized CLI rejection, and the supported five-row node-label aggregation.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 18:50:08 -04:00
Andrew Hundt 7dd1e27677 fix(config): document auto_index as enabled by default
docs/CONFIGURATION.md:82 previously listed auto_index=false even though CBM_CONFIG_REGISTRY in src/cli/cli.c defaults the key to true. This could lead users to assume streamlined first-use indexing was disabled.

tests/test_cli.c:3837 now derives the expected Markdown row from CBM_CONFIG_REGISTRY, so future default changes must keep the configuration table synchronized.

Verification: CBM_ONLY_SUITE=cli build/c/test-runner (151 passed); bash scripts/check-source-safety.sh; git clang-format --diff HEAD -- tests/test_cli.c.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 18:15:46 -04:00
Andrew Hundt 292299857e fix(mcp): skip unfiltered rows for semantic-only JSON
search_graph previously ran an unfiltered graph search before a JSON semantic-only lookup, so an empty vector result returned unrelated nodes and paid an unnecessary graph-result-page scan. Detect semantic-only requests, skip cbm_store_search and cbm_store_search_overlay_view, and return explicit empty semantic_results with recovery guidance.

Define query_graph as an early compositional tool for effective, computationally efficient custom Cypher. Mark examples, labels, properties, and LIMIT as optional guidance; retain server caps and dependency-ranking instructions; make truncation hints offer narrowing or cap changes without prescribing LIMIT.

Install portable sandbox recovery guidance that distinguishes MCP approval from shell sandbox authorization.

Verification: ASan/UBSan MCP 228 passed; tool_consolidation 103 passed; token_reduction 50 passed; CLI 150 passed; source-safety passed. The repository-wide clang-format target remains red on pre-existing files; changed-line formatting issues were corrected.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 17:56:39 -04:00
Andrew Hundt d6d22d6950 fix(reports): scope Pareto dominance to matched workloads
summarize_group now derives a Pareto workload identity from scenario, report mode, index mode, corpus metadata, and task-set hashes. mark_pareto_frontier compares candidates only when that identity matches, preventing small semantic canaries from dominating full-repository measurements.

Add test_pareto_does_not_compare_different_workloads in tests/test_summarize_benchmark_results.py. Verified with 140 benchmark campaign/measurement/report tests, ruff check, and regenerated retained composition output.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 15:39:37 -04:00
Andrew Hundt ef5f2e3009 test(pipeline): cover Python-Rust and Rust-TS-JS indexing
tests/test_pipeline.c lacked persisted CALLS coverage for a Python entry point importing PyO3 functions and lacked incremental-versus-fresh oracles for mixed Python/Rust and Rust/TypeScript/JavaScript repositories.

Add pipeline_python_pyo3_import_resolves_rust_function_calls plus two mixed-language incremental tests. The tests require Python edits to publish as CBM_PIPELINE_PUBLISH_INCREMENTAL_EXACT, require Rust and TypeScript scoped-LSP gaps to publish a full rebuild with reason scoped_lsp_gap, and compare each resulting graph with a fresh fast rebuild.

Route setup_lang_repo and setup_incremental_repo through cbm_resolve_cache_dir, reject truncated paths, and remove partially created fixture trees on setup errors. Assertion-aborted fixtures now remain inside the test runner cache lifecycle and are retained only through CBM_TEST_ARTIFACT_DIR.

Verification: all three focused tests passed with the ASan/UBSan runner and the current-source TSan runner. Apple leaks reported 0 leaks for 0 total leaked bytes for each focused test. A real MCP 2025-11-25 trace_path request against a mixed Python/Rust repository returned cli_main -> _run_cli_command and cli_main -> serve_mcp with isError=false and clean EOF shutdown.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 14:55:27 -04:00
Andrew Hundt f4dbb9a37b test(typescript): preserve calls through barrel re-exports
The pipeline had separate coverage for TypeScript re-export IMPORTS edges and direct cross-file calls, but no end-to-end guard proved that their composition resolves a caller through a barrel file to the implementation symbol.

Add pipeline_typescript_barrel_reexport_call_resolves_implementation in tests/test_pipeline.c. Its three synthetic files use nested hyphenated directories, a multiline named import, an async awaited call, and an export-from barrel; cross_file_call_exists must find callerOperation -> targetOperation in the persisted database.

Verification: the focused test passed with the ASan/UBSan runner and the current-source TSan runner. Current-source TSan also passed watcher unwatch, replacement, deferred-free, publication-generation, supervised-response, and pipeline-lock contention guards. git clang-format reported no changed-line edits and git diff --cached --check passed.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 14:23:39 -04:00
Andrew Hundt c129c77ad8 test(pipeline): verify incremental parity across 13 languages
Incremental behavior previously lacked one table-driven oracle covering every supported parser/LSP language family. That left exact-upsert and scoped-LSP fallback routing vulnerable to language-specific drift even when individual fixtures passed.

Add run_incremental_language_oracle_case in tests/test_pipeline.c. Each synthetic fixture performs a full fast index, edits source to add a callable symbol, runs incremental indexing, asserts the publication kind and fallback reason, and compares the resulting canonical graph with a fresh fast rebuild.

Cover Go, C, C++, CUDA, and Python exact publication plus JavaScript, TypeScript, TSX, PHP, C#, Java, Kotlin, and Rust scoped_lsp_gap full fallback. The helper releases pipelines, configuration, and project storage through one cleanup path; ordinary passes and failures remove fixtures, while CBM_TEST_ARTIFACT_DIR retains failed fixtures inside the runner-owned cache.

Verification: the ASan/UBSan test runner passed incremental_cross_lsp_language_matrix_matches_fresh_rebuild (13 cases) and eight adjacent exact, C-header fallback, Python/JavaScript scoped-LSP, delete, rename, overlay publish-failure, and overlay extraction-failure tests. git clang-format reported no changed-line edits and git diff --cached --check passed.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-18 14:13:04 -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
Martin Vogel 7d6cdb23ef Merge pull request #1164 from DeusData/feat/test-sharding
Parallel test execution: per-suite processes with a zero-loss gate contract
2026-07-18 15:21:09 +02:00
Martin Vogel bc3594b728 fix(test): pin XDG_CONFIG_HOME in the three env-coupled cli tests
The parallel runner's isolated-process execution exposed three cli tests
that were passing for the wrong reason on Linux: cbm_zed_config_dir (and
the VS Code/vendor path resolvers) prefer $XDG_CONFIG_HOME over
$HOME/.config, CI runners export it, and an isolated `test-runner cli`
process therefore resolved config paths OUTSIDE the test fixture — the
tests only stayed green in the all-suites single process because an
earlier suite's env mutations leaked into them (hidden cross-suite
coupling, deterministic failure in isolation, macOS unaffected since its
resolvers do not consult XDG).

- cli_detect_agents_finds_zed: save/set XDG_CONFIG_HOME to the fixture's
  .config for the detection call, restore after.
- cli_vscode_profile_mcp_uninstall: same pin alongside its existing
  HOME/PATH/APPDATA handling.
- cli_durable_profiles_follow_current_vendor_paths: XDG_CONFIG_HOME joins
  its unset-and-restore env list.

All 222 cli tests pass locally with a hostile XDG_CONFIG_HOME exported.
No production code changed; no assertion weakened.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 14:10:06 +02:00
Martin Vogel 95659b3955 test-par: serialize the mcp suite too (both alarm-deadline tests live there)
Flake audit: the whole corpus has exactly two wall-clock test deadlines —
the EOF-shutdown alarm(5) and the issue-832 supervisor-spawn alarm(60),
both in the mcp suite (the latter already flaked once under heavy local
load). Serializing mcp costs ~6s of tail and closes the starvable-deadline
class completely; every remaining parallel-wave suite is deadline-free.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 13:39:42 +02:00
Martin Vogel b0a1287794 test(so): document the POSIX GLR-cap branch as a bounded smoke, per falsification
Follow-up correcting d8a78e8, whose message claimed a broken merge cap
crashes "within the first second" so the 10s alarm could not mask it.
Falsification disproved that claim AND found the guard was vacuous on
POSIX from the start:

- With CBM_TS_STACK_MERGE_MAX_DEPTH deleted outright, the ORIGINAL
  unbounded test stayed GREEN (220s full grind, no crash) on macOS ASan.
- Small thread stacks (2 MB, then 512 KB) did not change that.
- Instrumenting the vendored recursion showed why: the recursive merge
  path is NEVER ENTERED for this input in the observable window — max
  depth stays 0. There is nothing for a stack bound to catch here.

So the cap-regression DETECTION lives in the Windows in-process branch
(real ~1 MB native stack, where #913 actually manifested — exercised by
the windows CI leg). The POSIX branch is, and always was, a bounded
no-crash smoke of the pathological parse; the 10s alarm makes it
deterministic (12-15s across three runs, was 2-218s machine-dependent)
without any detection loss, because there was no POSIX detection to
lose. The experimental pthread machinery is dropped; the test comment
now carries the falsification data so no future reader mistakes the
POSIX branch for a cap guard.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 13:31:18 +02:00
Martin Vogel d8a78e8cf4 fix(test): deterministic ceiling for the GLR merge-cap regression guard
perl_glr_deep_parse_recursion_capped wandered between 2s and 218s for the
IDENTICAL input: past the merge cap the ambiguity-exploded GLR parse grinds
at an environment-dependent rate (allocator/scheduler state in the forked
child), and the test paid for however long that grind ran. It was the
critical path of every parallel run and the dominant chunk of the
sequential one.

The guard's actual signal is the CRASH, not the grind: when
CBM_TS_STACK_MERGE_MAX_DEPTH regresses, the recursive stack-merge
overflows within the first second at this depth (same profile as the
sibling pre-guard probe, rc=139 in under a second). The forked child now
arms a 10s alarm whose handler exits cleanly — a real SIGSEGV/SIGBUS
still terminates the child by signal first and stays visible to
WIFSIGNALED, so the RED path is untouched; only the pointless remainder
of the grind is skipped. Windows (in-process branch) is unchanged.

Suite b: 13s across three consecutive runs (was 2-218s). Full parallel
run: 97s wall, 6,374 passed / 0 failed / 1 skipped, union guard clean —
vs 351s sequential on the same machine (3.6x).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 13:12:20 +02:00