Commit Graph
238 Commits
Author SHA1 Message Date
Andrew Hundt c107eb1473 fix(smoke): isolate cache and MCP tool modes
Create a per-run SMOKE_STATE in scripts/smoke-test.sh and export CBM_CACHE_DIR and XDG_CONFIG_HOME before invoking the installed binary. Keep CLI stderr inside that cleanup tree so smoke runs neither inherit nor leave user cache/config artifacts.

Extend mcp_run with an explicit tool-mode argument: streamlined scenarios validate progressive disclosure while index_repository and get_code_snippet scenarios select classic mode. This prevents a persisted tool_mode=classic setting from producing "streamlined tool 'get_code' not found" in Phase 5.

Tests: rtk bash -n scripts/smoke-test.sh; installed-binary smoke completed all phases with 9,039 nodes, 24,079 edges, MCP reveal/classic calls, installer lifecycle, security, and no-orphan checks passing.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 11:02:03 -04:00
Andrew Hundt 7810ab55d7 feat(benchmark): calculate historical latency deltas
Add config_signature() and historical_delta_rows() to scripts/summarize-benchmark-results.py. Match baselines to latest candidates by exact config overrides and render baseline/latest ratios for incremental reindex, post-mutation fresh rebuild, and query latency beside both quality scores and the baseline correctness gate.

Add a matching-capability regression fixture in tests/test_summarize_benchmark_results.py. Verified 24 report/campaign tests and Ruff. The generated combined report now calculates 2.87x default, 2.73x rank-off, and 2.36x all-optional-off incremental gains over the 24ba checkpoint without concealing rejected upstream/trajectory quality.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 02:04:37 -04:00
Andrew Hundt d3ec0f19ed feat(benchmark): break out mutation reindex phases
Add mutation_reindex_details() to scripts/summarize-benchmark-results.py and render one per-candidate/scenario table row with the source mutation description, changed paths, publication route/reason, end-to-end incremental p50, internal indexing-work p50, post-mutation fresh rebuild p50, speedup, and canonical equality.

Pin the route_handler rendering contract in tests/test_summarize_benchmark_results.py. Verified 23 summarizer/campaign tests and Ruff. Generated final-performance-quality-report-v2.md directly from the six retained July 14 result JSON files while leaving the original report and campaign manifests unchanged.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 01:50:11 -04:00
Andrew Hundt d39388a63b feat(benchmark): render frontier cap crossovers
Add frontier_crossover_rows() to scripts/summarize-benchmark-results.py so each frontier pairs the largest configured fallback cap with the smallest exact cap. Report end-to-end p50, indexed-work p50, profiling-only RSS p50, fresh-full p50, the exact/fallback latency ratio, and a documented five-percent tie band.

Extend tests/test_summarize_benchmark_results.py with a three-cap fixture that proves the nearest fallback/exact pair is selected and rendered. Verified 22 benchmark summarizer/campaign tests and Ruff; regenerated the preserved 318-cell campaign with 318 complete and zero missing, corrupt, duplicate, or unplanned cells.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 01:28:24 -04:00
Andrew Hundt a2aa1250c6 feat(benchmark): expand resumable frontier-cap matrices
Add --matrix-spec to scripts/run-benchmark-campaign.py. Deterministically expand candidate revision/binary/build, capability profile, transport, scenario, frontier size, exact cap, and repetition into immutable cells; archive both the source spec and expanded plan before execution.

Include structured parameters and benchmark-incremental-speed.py SHA-256 in cell identity evidence. Keep exact command arrays, binary SHA-256 validation, atomic completion, retained attempts, disk guards, and report/manifests on the existing campaign path.

Teach frontier_coverage_gate() to accept deliberately configured low-cap cells only when they publish containment/full, report frontier_too_large, include affected_paths_truncated=true, and preserve the canonical graph.

Validated with 43 Python tests plus 15 subtests and Ruff. Real -O2 smoke cells proved one-attempt completion/resume and configured cap=4 versus five affected Go files produced incremental_containment with canonical equality and cleanup.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 01:15:52 -04:00
Andrew Hundt 6559ad0755 fix(incremental): distinguish scoped LSP fallback from cap overflow
Define CBM_PIPELINE_DELTA_REASON_SCOPED_LSP_GAP in src/pipeline/pipeline_internal.h and use it when JavaScript, TypeScript, TSX, PHP, C#, Java, Kotlin, or Rust require the existing correctness-preserving full rebuild. Report affected_paths_truncated=false instead of claiming frontier_too_large at 1/64 affected paths.

Extend scripts/benchmark-incremental-speed.py with fixtures for all 13 languages wired through cbm_pxc_has_cross_lsp(). Gate Go, C, C++, CUDA, and Python on observed exact-frontier expansion; gate the other eight on publish_kind=full and exact_reason=scoped_lsp_gap. A fixture that does not exercise its declared contract now fails the matrix.

Validated with the -O2 13-language matrix (13/13 contract and canonical graph gates, cleanup=true), ASan+UBSan pipeline suite (376 passed), language-contract suite (35 passed; breadth checked 159 grammars), benchmark Python tests (39 passed, 15 subtests), and Ruff.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 00:45:16 -04:00
Andrew Hundt 9478d79ffb fix(benchmark): retain profile RSS for frontier runs
Add Go, Python, and C-header inbound-frontier fixtures to scripts/benchmark-incremental-speed.py with an explicit --frontier-files denominator and single-definition mutations.

Teach build_index_result() to accept peak_mb from mem.phase, pipeline.done, or incremental.done. Emit terminal RSS fields from cbm_pipeline_run() and log_incremental_done() only while cbm_profile_active is true, preserving normal-run logging and avoiding memory queries outside experiment mode.

Validated with the -O2 CLI matrix (3/3 canonical graph gates, cleanup=true, peak RSS present on initial/incremental/fresh routes), the ASan+UBSan C runner (375 passed), benchmark Python tests (35 passed, 5 subtests), and Ruff.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 00:26:01 -04:00
Andrew Hundt a8836ebfe1 feat(benchmark): render categorical quality evidence
Compute retrieval MRR, canonical-graph fidelity, applicable-probe task success, and their equal-weight geometric mean in scripts/summarize-benchmark-results.py. Correctness failures remain hard REJECT decisions and cannot be averaged away.

Render named oracle criteria, expected evidence, rank, Hit@1/Hit@5, raw retrieval/graph/scenario counts, capability profiles, and explicit Pareto eligibility or dominance reasons. Link the generated Markdown to the NIST TREC reciprocal-rank definition at https://trec.nist.gov/pubs/trec14/papers/hummingbird.qa.robust.tera.pdf.

Validation: 32 benchmark/campaign/report unit tests passed; Ruff and scripts/check-source-safety.sh passed; preserved campaign JSON rendered successfully without modifying source artifacts.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-15 00:06:00 -04:00
Andrew Hundt 6fa4700b11 fix(benchmark): name the minimal indexing profile
Add --config-profile choices in scripts/benchmark-incremental-speed.py and record the selected profile beside its expanded config_overrides map.

Define minimal_indexing as auto_index_deps=false plus rank_enabled, similarity_enabled, semantic_edges_enabled, githistory_enabled, and httplinks_enabled set to false. Keep repeated --config values higher priority for controlled ablations.

Document the distinction between optional_graph_disabled and minimal_indexing in docs/BENCHMARK_CAMPAIGN.md. Validate with 29 Python unit tests, Ruff, and scripts/check-source-safety.sh.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 23:51:24 -04:00
Andrew Hundt 0639cd6ab3 fix(benchmark): generate bounded correctness evidence
Add correctness_findings() in scripts/summarize-benchmark-results.py to report canonical count mismatches, bounded left/right witnesses, and failed task-oracle expectations without copying unbounded payloads into Markdown.

Correct the response-size footnote to identify the exact default tool-response payload and disclose the one-case/five-oracle sample basis. Cover the generated evidence and measurement wording in tests/test_summarize_benchmark_results.py; 27 benchmark unit tests and Ruff pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 14:43:56 -04:00
Andrew Hundt bf0785efd3 fix(benchmark): read retained supervisor worker logs
Parse log= from the index.supervisor.profile_log line emitted by src/mcp/index_supervisor.c and stream measurement markers from that .worker-<pid>.log before trying the unrelated response logfile path. This matches the retained-log contract observed in the profiled arm64 pilot.

The regression fixture supplies a missing response logfile and a valid supervisor worker log, then verifies peak_rss_mb=320 and pipeline_done=81. All 27 focused pytest cases and Ruff pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 14:17:19 -04:00
Andrew Hundt 54a20097ef fix(benchmark): retain successful worker measurement logs
Set CBM_PROFILE=1 in build_env() for every candidate so index_supervisor.c preserves successful worker logs long enough for build_index_result() to stream mem.phase, pipeline.done, and incremental.done markers. Document that the profiling configuration is consistent across revisions and remains part of the auditable benchmark environment.

Validated with 27 pytest cases across the benchmark, summarizer, and campaign suites; Ruff and git diff --check pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 14:11:07 -04:00
Andrew Hundt 3038983e72 fix(benchmark): reap campaign trees and retain worker metrics
Run each cell from scripts/run-benchmark-campaign.py in an isolated process group. On timeout or SIGINT, signal the group, allow 30 seconds for harness cleanup, force-stop remaining descendants, write attempt.json, and release running.lock. This addresses the detached cbm-self-dogfood worktree left by an interrupted pilot.

Read at most 512 mem.phase, pipeline.done, and incremental.done lines from the response logfile in build_index_result() using a streaming scan. Replace the route_handler comment literal with an executable cbm_http_path_match(path, "/api/pan4-oracle") mutation and target the route oracle with name_pattern=pan4-oracle.

Validated with 26 pytest cases, including descendant timeout cleanup, bounded worker-log metrics, and executable route mutation. Ruff and git diff --check pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 14:04:22 -04:00
Andrew Hundt e8693d2443 fix(build): apply documented compiler and linker overrides
Append EXTRA_CFLAGS, EXTRA_CXXFLAGS, and EXTRA_LDFLAGS in Makefile.cbm after the optimized production defaults. Add EXTRA_CXXFLAGS to both ASan jobs in .github/workflows/_soak.yml so commit 87188913 sanitizer arguments instrument C++ sources as well as C sources and the final link.

Document the scripts/build.sh debug invocation in README.md while retaining -O2 and CBM_BIND_TS_ALLOCATOR=1 for ordinary builds. A forced make dry-run places distinct sentinels on the C compile, C++ compile, and link commands; bash -n, scripts/check-source-safety.sh, and git diff --check pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 13:47:39 -04:00
Andrew Hundt 33ef7249fe fix(benchmark): separate default response cost from quality JSON
Route run_cli_tool_call() and run_mcp_tool_call() through a default-format request for elapsed_ms, response_bytes, and response_token_estimate, then issue format=json only for quality scoring. Record quality_probe_elapsed_ms and quality_response_bytes so the second request cannot inflate the user-facing latency or replace TOON payload sizing.

This fixes the pilot JSONDecodeError: Expecting value at line 1 column 1 when search_graph returned its default TOON content. Document the two-request instrumentation contract in docs/BENCHMARK_CAMPAIGN.md.

Validated with 23 focused pytest cases across tests/test_benchmark_incremental_speed.py, tests/test_summarize_benchmark_results.py, and tests/test_benchmark_campaign.py; Ruff and git diff --check pass.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 13:42:37 -04:00
Andrew Hundt 02c2efa68a fix(lifecycle): cancel and reap auto-index process trees
Add cbm_proc_opts_t.cancel_requested so cbm_mcp_server_free() stops supervised auto-index work before joining. POSIX supervision kills the worker process group; Windows assigns the suspended worker to a kill-on-close Job and falls back to direct process termination when Job assignment is unavailable.

Short-circuit index_run_supervised() crash recovery after intentional cancellation, remove cancellation logs, and expose cbm_mcp_server_join_autoindex() for callers that explicitly need completion instead of shutdown.

scripts/smoke-test.sh now checks the six-tool streamlined surface separately from the _hidden_tools reveal. Validation: 18/18 subprocess ASan/UBSan tests passed, the supervised auto_watch MCP regression passed, source-safety passed, and the full isolated smoke suite ended with smoke-test: ALL PASSED including clean shutdown and no-orphan gates.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 13:14:43 -04:00
Andrew Hundt 73468dbabb test(benchmark): rank expected query results
Extend score_quality_oracles() in scripts/benchmark-incremental-speed.py with result rank, returned count, reciprocal rank, hit@1, and hit@5 for each applicable marker, changed-path, architecture, and route criterion.

Use mean reciprocal rank as the report quality/Pareto axis while retaining the all-applicable binary gate and passed/applicable counts. Render hit@1 and hit@5 beside exact response bytes, token estimates, query latency, incremental latency, and peak RSS.

Validated by 21 campaign/measurement/report unittest cases, including a rank-2 expected result with MRR 0.5, plus ruff check and git diff --check.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 12:09:22 -04:00
Andrew Hundt 1e567d4a25 fix(benchmark): reject harness-error result files
Require validate_result() in scripts/run-benchmark-campaign.py to reject top-level error reports, missing boolean derived.passed verdicts, and artifacts without non-empty cases or measurement objects before complete.json is written.

Keep accepted exit code 1 available for valid quality/performance gate failures while preventing caught RuntimeError reports from becoming resumable measurements.

Validated by 20 campaign/measurement/report unittest cases, including test_harness_error_report_is_not_marked_complete, and ruff check.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 12:03:56 -04:00
Andrew Hundt 7df50e1eb6 test(benchmark): retain resumable campaign evidence
Add scripts/run-benchmark-campaign.py with SHA-256 cell identities over revisions, binaries, release flags, capabilities, transports, scenarios, repetitions, harnesses, commands, environments, and timeouts.

Retain timestamped command/stdout/stderr/result/attempt artifacts; validate binary and result hashes before atomically writing complete.json. Reject live locks, record stale-lock recovery, enforce disk headroom, archive exact plan bytes and environment snapshots, and classify missing, corrupt, duplicate-attempt, and unplanned run directories in immutable manifests.

Regenerate reports atomically from validated completion records only. docs/BENCHMARK_CAMPAIGN.md documents default, rank_enabled=false, and all-optional-capabilities-off cells plus release-build metadata requirements.

Validated by 19 unittest cases and ruff check across the campaign, measurement, and report scripts; git diff --check passed.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 11:51:35 -04:00
Andrew Hundt 8bd307e8df test(benchmark): score query quality against response cost
Record canonical JSON payload bytes separately from CLI/MCP framing in scripts/benchmark-incremental-speed.py and attach the deterministic utf8_bytes_div_4_ceil token estimate to every query oracle.

Require every applicable marker, changed-path, architecture, and route oracle to pass. Render quality checks, query latency, response size, indexing latency, and peak RSS in scripts/summarize-benchmark-results.py; mark Pareto candidates only after correctness passes and all comparison axes are present.

Validated by 10 unittest cases across tests/test_benchmark_incremental_speed.py and tests/test_summarize_benchmark_results.py plus py_compile and git diff --check.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 11:41:49 -04:00
Andrew Hundt 4b2d1fde08 test(benchmark): retain resource and quality evidence
Parse the maximum peak_mb value from mem.phase records in scripts/benchmark-incremental-speed.py and store the tested binary path, size, and SHA-256 in every synthetic, matrix, and self-dogfood report.

Add scripts/summarize-benchmark-results.py to aggregate repeated JSON artifacts into a quality-first Markdown table. Canonical graph or task-oracle failures force a rejection even when measured speedup is high; accepted rows include p50/p95 latency, median speedup, maximum peak RSS, cleanup status, config overrides, and binary identity.

uv run --no-project python -m unittest tests/test_benchmark_incremental_speed.py tests/test_summarize_benchmark_results.py passed 6/6 contracts. The summarizer --help invocation passed.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-14 09:31:59 -04:00
Andrew Hundt 59def6aaeb merge(upstream): integrate main through Laravel route fixes
Merge upstream/main at 2469ecc3 into integrate-upstream-split-search-tools while retaining branch-specific incremental indexing, MCP API consolidation, and graph extraction behavior.

The staged tree adds upstream's Mojo and Perl grammar support, issue reproduction suites, Graph UI changes, Windows smoke coverage, TOON graph output, subprocess supervision, and Laravel facade route handling. Conflict resolutions in src/pipeline/httplink.c and pass_httplinks.c retain dynamic growth and IMPORTS edge identity; scripts/smoke-test.sh and scripts/soak-test.sh retain hermetic cache isolation.

Validation against the merge plus the follow-up working-tree reconciliation: ASan/UBSan MCP suite 216/216 passed. Follow-up semantic and concurrency fixes remain intentionally unstaged for the next focused commit.
2026-07-14 01:47:43 -04:00
Andrew Hundt f21871ee3a fix(soak): isolate idle resource measurements
Measure idle CPU from process CPU-time deltas instead of the lifetime average reported by ps. Expose user and system CPU counters in diagnostics, validate configurable idle and response limits, and fail closed when the server or its protocol pipe exits.

Disable session-CWD auto-indexing inside the explicit-index soak harness so unrelated repository work cannot contend for the pipeline lock, inflate cache/RSS results, or masquerade as idle CPU. Preserve crash/restart coverage and exact cleanup of harness-owned resources.

Validated with the ASan/UBSan MCP suite (171 tests), a bounded 30-second idle soak with crash recovery (0.0% idle CPU, 197 MiB RSS, stable cache/FDs), shell syntax and ShellCheck, source-safety lint, clang-format diff, configuration rejection cases, and diff hygiene.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-13 10:58:07 -04:00
Andrew Hundt 4dc927e10a fix(soak): make lifecycle evidence trustworthy
Restore the upstream query-only leak workflow and timeout budgets so multi-hour jobs are neither dormant nor truncated. Preserve mixed-workload behavior while adding a dedicated read-only mode across Linux, macOS, and Windows.

Run every soak in an isolated project/cache/FIFO root, clean owned servers and diagnostics on normal or abnormal exit, and convert MSYS paths before placing them in JSON or environment variables. Validate initialize and tool responses, retain failure state across crash recovery, inject the crash before waiting for an index response, and require a checked post-restart reindex.

Record cache, database, and WAL sizes alongside RSS and FD metrics. Use post-index peak/final ratios and long-run slope as the portable default memory gates; keep an explicit CBM_SOAK_RSS_MAX_MB override for measured platform budgets instead of the obsolete universal 200 MB ceiling.

Validation:

- bash -n scripts/soak-test.sh

- shellcheck -x scripts/soak-test.sh

- actionlint on soak workflows

- source-safety and diff hygiene

- zero-minute product lifecycle smoke: passed

- asynchronous crash/restart/reindex smoke: passed

- one-minute query-leak run: 106 RPCs, zero failures

- invalid config and failed-server cleanup paths: rejected and cleaned

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-12 18:45:28 -04:00
Andrew Hundt 3325f3ad2c fix(installer): restore Windows binary on failure
Make install.ps1 preserve an existing executable before replacement, treat nonzero native --version status as verification failure, and restore the prior binary when copy or verification fails. Abort safely if the existing executable cannot be preserved instead of risking an unrollbackable overwrite.

Remove the rename-aside backup only after the new binary verifies and place the complete download/extract/install flow under a finally cleanup so temporary cbm-install directories are removed on success, explicit exits, and unexpected errors.

Strengthen the existing Windows smoke path to seed an installed binary, fail on installer errors, and reject an orphaned .old backup after successful replacement.

Validation: bash syntax; source-safety and protocol stdout checks; isolated install security audit; repository PowerShell grammar parse with zero extraction errors; diff hygiene. Native PowerShell replacement execution remains covered by the Windows CI smoke job because PowerShell is unavailable on this host.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-12 17:44:19 -04:00
Martin Vogel 8f3ee086a7 Merge pull request #1058 from DeusData/fix/996-writer-wide-fopen
fix(writer): open the graph DB wide-safe for non-ASCII cache paths
2026-07-12 21:49:34 +02:00
Martin Vogel 0fa9f429ad test(smoke): platform-branch the shim check for the Windows .cmd form
The 8e gate compared uname -s to the bare literal MINGW64_NT, which
never matches the real value (MINGW64_NT-10.0-...), so the POSIX
branch silently ran on Windows and looked for the extensionless shim
that #929 no longer installs. Branch by platform prefix: Windows
expects the .cmd shim and the absence of the legacy extensionless
twin; POSIX unchanged. Content checks (never blocks, delegates to
hook-augment) now run on both.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-12 19:58:58 +02:00
Martin Vogel d9adfeb688 fix(writer): open the graph DB wide-safe for non-ASCII cache paths
cbm_writer_open used a raw ANSI-CP fopen for the hand-rolled SQLite
writer - the one file-creating call on the dump chain without a
UTF-8-to-wide conversion. On a non-ASCII Windows profile
(C:\Users\Kovacs Janos) extract and resolve succeeded but the dump
failed with pipeline.err phase=dump: fopen could not create graph.db
under the profile-derived cache dir while cbm_mkdir_p (already wide)
had happily created the directory around it. Routed through cbm_fopen,
matching #700 (pass readers) and #973 (canonicalization); POSIX is
byte-transparent.

New Windows guard test_non_ascii_cache_dump.py indexes an ASCII repo
into a non-ASCII CBM_CACHE_DIR and requires a query_graph readback
with Function count > 0 - proving the DB was written to and reopened
from the non-ASCII cache, not merely that no error surfaced. The
existing non-ASCII guard only varied the repo path against an ASCII
cache, which is why this class went unseen.

The USERPROFILE-sourced env encoding question (Layer 2 - _environ is
ANSI-CP, not UTF-8) is deliberately not bundled; it needs verification
on a real Windows box and will be filed separately if the guard alone
does not settle it.

Closes #996

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-12 19:57:47 +02:00
Martin Vogel 2f7befb9ad fix(pipeline): destroy TLS parsers at allocator-epoch boundaries
SIGABRT (free of a non-libmalloc pointer in ts_stack_delete) on the
second index_repository in one process, whenever a sequential run is
followed by a parallel one. The sequential path parses on the CALLING
thread and left its TLS parser alive; the next parallel run switches
the global tree-sitter allocator to the slab (cbm_slab_install), the
calling thread participates in extraction, reuses the stale parser,
and its per-file teardown frees mimalloc-epoch memory through
slab_free's foreign-pointer fallback -> plain free() -> libmalloc
abort. The v0.9.0 supervisor masks this on the default MCP path (fresh
worker process per index); the in-process path and every embedded
consumer died. Root-caused with allocator-epoch instrumentation: the
victim parser is created at epoch 0 in cbm_pipeline_pass_definitions
and deleted at epoch 1 in extract_worker.

Two boundary fixes:
- run_sequential_pipeline destroys the calling thread's parser at run
  end (also bounds its memory like the parallel workers already do);
- cbm_parallel_extract destroys the calling thread's parser BEFORE
  cbm_slab_install, so no parser ever crosses the allocator switch.

Guards: prod-tier smoke invariant inv_second_index_inprocess
(sequential-then-parallel double index over stdio, RED with signal 6
on the unfixed binary — ASan builds mask the allocator seam, so the
unit tier cannot catch this class) + suite-tier fork-isolated
index_second_inprocess_run_survives_issue773 pinning the in-process
double-index contract. Repro 5/5 clean after the fix (was 5/5 abort).

Closes #773

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-11 12:51:29 +02:00
Martin Vogel 751d2293f9 Merge pull request #1013 from DeusData/feat/compact-tool-output
feat(mcp): TOON compact tool output — ~10x fewer response tokens across the query surface
2026-07-11 02:13:00 +02:00
Martin Vogel 0fd175cf54 feat(mcp): TOON output for trace/query/search_code/architecture + context-bomb defaults
Completes the compact-output pass across the remaining query tools
(consumers are LLM agents; every response byte is context tokens):

- trace_path: callees[N]{qn,hop}/callers[N]{qn,hop} tables (risk/test/
  args columns per flags); the per-hop JSON key envelope was 84% of the
  payload. 3,589B -> 1,634B on a representative trace.
- query_graph: rows[N]{cols}: table (columns double as the header);
  format:"json" restores columns/rows arrays.
- search_code compact mode: results/raw/dirs tables; drops the
  field (duplicate of the qn tail). 2,488B -> 1,135B for 17 hits.
- search_code full mode: per-hit source capped at a 60-line window
  anchored on the first match, with source_start/source_truncated
  markers — uncapped whole-symbol dumps ran to 142KB per response; the
  complete symbol stays one get_code_snippet call away.
- get_architecture: default (no aspects) is now a summary (languages,
  packages, entry_points + always-on totals/label/type counts) with an
  aspects_hint — the old default rendered EVERYTHING including the full
  file_tree (94KB -> 4.5KB -> 2.8KB with TOON tables); all sections
  emit as TOON tables, aspects:[...] and ["all"] keep full access
  (52KB vs 94KB even for the explicit full dump).
- get_code_snippet: drops the property-blob enrichment (41% of the
  response; signature/docstring are literally in the source; fp/sp/bt
  never belonged there). Metrics remain reachable via search_graph
  fields=[...].
- list_projects: per-project 12-field git block (mostly null) replaced
  by the branch name; name/root/nodes/edges/size kept.
- search_graph description: in/out documented as TOTAL all-edge degree,
  NOT caller/callee counts (agent evals mis-read it as callers).
- format:"json" escape hatch on every converted tool.

Guards: tool_output_byte_budgets pins absolute byte ceilings on default
search/trace outputs (a property blob sneaking back into row emission
blows them immediately); snippet_enriched_properties inverted to pin
the no-spill contract. Suite 5,984 green; smoke updated to the TOON
contract end-to-end.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-10 21:18:56 +02:00
Martin Vogel 4843a34065 feat(mcp): TOON compact output for search_graph — ~90% fewer response tokens
Tool responses land in an LLM agent's context window, where the legacy
per-node JSON objects were dominated by data no agent can use: the
similarity/semantic pipeline intermediates fp (~450B minhash hex), bt
(body-token bag) and sp (structural profile) plus 14 complexity metrics
were grafted onto every result by enrich_node_properties — ~64% of every
node emission, ~370 tokens per search hit.

search_graph now defaults to TOON (Token-Oriented Object Notation,
toonformat.dev): scalars as 'key: value' lines and results as a
'results[N]{qn,label,file,lines,in,out}:' header + one row per hit.
Published benchmarks show equal-or-better LLM retrieval accuracy at
40-60% fewer tokens than JSON; rows now also carry line ranges, which
the legacy output lacked.

- fields:[...] opts into per-node property columns (complexity,
  signature, docstring, ...); fp/sp/bt are blocklisted even there
- format:"json" restores the legacy verbose objects byte-identically;
  include_connected forces it (nested neighbor lists)
- BM25 and semantic modes emit the same table shape (rank/score column)
- semantic-only calls no longer run the unfiltered regex search that
  silently prepended up to 200 enriched nodes (102.8KB -> 4.2KB)
- default limit 200 -> 50 (BM25 100 -> 50): cheap first page, page via
  has_more/offset as before
- new emitter module src/mcp/compact_out.{c,h} (string builder + TOON
  scalar/table emission with spec quoting rules)

Measured on a real index (14-hit regex search): 20,793B -> 1,327B
(-94%); BM25 19,667B -> 5,081B; semantic 102,837B -> 4,181B. A typical
discovery session drops ~50.9K -> ~4.7K tokens combined with the
follow-up slices.

Guards: tool_search_graph_toon_never_leaks_internal_fields (sentinel
fp/sp/bt never emitted, even when requested), rewritten
tool_search_graph_includes_node_properties pins default-lean/fields-
opt-in/json-escape-hatch; suite 5983 passed, smoke green.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-10 08:23:19 +02:00
Martin Vogel 10390f184c chore(lsp): add Perl reference to originality-check manifest
Perl LSP landed in #461; scan its implementation against PerlNavigator
(MIT, the leading OSS Perl language server) for defensive
copy-detection, same rationale as the phpactor entry. Verified clean:
no verbatim string/comment overlap.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-09 23:43:41 +02:00
Martin Vogel c97f6828f8 test(smoke): matcher guard tracks Read inclusion for the coverage note
Check 8d still locked the matcher to the pre-#963 'Grep|Glob' and failed
the pr-smoke leg on all platforms. The matcher now includes Read (the
augmenter injects the coverage note when a not-fully-indexed file is
read; structurally non-blocking, so the issue-#362 gate hazard cannot
recur). The guard now pins the exact new matcher and still rejects
Search/catch-all creep.

Refs #963

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-08 23:25:39 +02:00
Martin Vogel 71e86a69e0 fix(smoke/install): pass explicit CBM_ARCH to install.ps1 (arm64 Phase 13)
install.ps1 kept detecting arch=amd64 on windows-11-arm because it runs under x64
emulation, where neither $env:PROCESSOR_ARCHITECTURE nor .NET OSArchitecture (on
Windows PowerShell 5.1) reports the real Arm64. Add a CBM_ARCH env override to
install.ps1 (wins over auto-detect; also a genuine escape hatch for emulated
invocations) and have smoke Phase 13 pass the authoritative DL_ARCH. Deterministic
-- no reliance on in-process detection under emulation. Same emulated-arch class
as #907 (uname) and #908.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-06 11:25:28 +02:00
Martin Vogel 73a636f2a0 fix(smoke): derive arch from CI matrix, not emulated uname (arm64 12a)
The persistent windows-11-arm smoke Phase 12a failure was a 404, not a network
error: it requested codebase-memory-mcp-windows-amd64.zip on the arm64 leg. Cause:
DL_ARCH came from `uname -m`, which on windows-11-arm is an emulated x86_64 MSYS2
uname reporting "x86_64" -> wrong (amd64) archive -> 404 (server has arm64). Phase
14 worked because the binary's own detect_arch() is native. Prefer SMOKE_ARCH
(passed from the smoke workflow's matrix.arch) over uname; fall back to uname for
local runs. This is the real cause the earlier curl/proxy/ipv4 attempts masked --
the 404 was swallowed by 2>/dev/null until #905 surfaced it.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-06 09:56:58 +02:00
Martin Vogel 2a664aa03e fix(smoke): --noproxy for local download + surface curl errors (arm64 12a)
Phase 12a swallowed curl's stderr (2>/dev/null), so the persistent windows-arm64
"curl download failed" was undiagnosable. Two changes: (1) add --noproxy '*' so
curl never routes the local 127.0.0.1 test server through a proxy env var -- a
strong candidate since the app's own WinHTTP downloader reaches the server in
Phase 14 while only msys2 curl fails, instantly; (2) print curl's stderr on
failure so the actual reason is visible if it persists. Harmless on all
platforms (the smoke server is always local).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-06 09:20:49 +02:00
Flipper 14142b33ee fix(win): deliver non-ASCII CLI paths end-to-end (wide argv + CreateProcessW)
Closes #423, #20. A non-ASCII repo path passed to
`codebase-memory-mcp cli index_repository "<json>"` was mangled on Windows, so the
command failed with "repo_path is required" instead of indexing the real directory.
POSIX is unaffected (argv is already UTF-8). Two boundaries had to be fixed:

1. argv read: main() took only the narrow `int main(int argc, char **argv)`, so the
   CRT handed it argv in the active ANSI code page. On Windows main() now rebuilds
   argv from the wide command line (GetCommandLineW + CommandLineToArgvW) and
   converts each element to UTF-8 (cbm_wide_to_utf8).

2. worker spawn: the index supervisor (and the UI index thread) spawned the worker
   via CreateProcessA, which re-interpreted the UTF-8 command line through the ANSI
   code page -- re-mangling the path at the parent->worker boundary even after (1).
   Both spawns now build a wide command line (cbm_utf8_to_wide) and use CreateProcessW.

Without (2) the default (supervisor-enabled) path stayed broken while only the
in-process path worked, so this is the complete end-to-end fix, not just argv.

Promotes tests/windows/test_cli_non_ascii_arg.py from an opt-in known-red to a green
Windows guard (scripts/test-windows.ps1 + the test-windows-guards CI job). The guard
now exercises the DEFAULT supervised path (it drops the suite's CBM_INDEX_SUPERVISOR=0
so it actually crosses the worker-spawn boundary). Verified on native Windows: RED
before the fix, GREEN after (a non-ASCII repo indexes, nodes>0). Also adds -lshell32
explicitly (main.c now uses CommandLineToArgvW) and refreshes RED_TEST_ANALYSIS.md.

Signed-off-by: Flipper <jacobphilipp@ymail.com>
2026-07-05 18:51:23 +02:00
Andrew Hundt 0bfaabd538 test(benchmark): keep fastapi probe syntactically valid
Insert the FastAPI benchmark probe as an APIRouter method instead of a top-level function before an indented method declaration. The old mutation produced invalid Python and contaminated scoped-LSP overlay diagnostics with parser fallout.

Compile the mutated source before writing it so future FastAPI probe changes fail fast with a precise error instead of producing misleading benchmark data.

Validation: uv run python -m py_compile scripts/benchmark-incremental-speed.py; uv run python -m py_compile /private/tmp/cbm-fastapi-valid-probe-current-20260704T2250/fastapi_insert_probe/repo/fastapi/routing.py; bash scripts/check-source-safety.sh; git diff --check; FastAPI matrix /private/tmp/cbm-fastapi-valid-probe-current-20260704T2250.json passed with canonical_equal=true and publish_kind=full.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-04 22:48:42 -04:00
Andrew Hundt 56a24bb0ae test(benchmark): add fastapi matrix scenario
Add a real FastAPI matrix scenario that copies git-tracked HEAD files from an existing checkout into an isolated case repo, applies a routing.py probe insertion, and records source git metadata. Cloning is explicit opt-in via --clone-missing-real-repos; normal runs reuse --fastapi-repo, CBM_FASTAPI_REPO, or common local paths.

Current evidence: /private/tmp/cbm-fastapi-matrix-current-20260705T-postpatch.json passed with canonical_equal=true, publish_kind=full, exact_reason=frontier_too_large, and speedup_full_rebuild_over_incremental=0.43653250773993807. This proves the safe fallback path and preserves the FastAPI scoped-LSP performance blocker as a rerunnable benchmark target.

Validation: uv run python -m py_compile scripts/benchmark-incremental-speed.py; uv run python scripts/benchmark-incremental-speed.py --binary build/c/codebase-memory-mcp --matrix --matrix-scenarios fastapi_insert_probe --fastapi-repo /private/tmp/cbm-fastapi-cli-inbound-debug-20260705T-repo --work-root /private/tmp/cbm-fastapi-matrix-current-20260705T-postpatch --out /private/tmp/cbm-fastapi-matrix-current-20260705T-postpatch.json --timeout 240 --include-logs; bash scripts/check-source-safety.sh; git diff --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-04 21:55:05 -04:00
Martin Vogel e549b44daf Merge pull request #865 from DeusData/chore/remove-libgit2
chore: drop optional libgit2 dependency (keep git log fallback)
2026-07-05 01:55:17 +02:00
Martin Vogel 2e055d771f chore: drop optional libgit2 dependency, keep git log fallback
libgit2 is licensed GPLv2-with-linking-exception, and this project is
deliberately GPL-free (see scripts/license-policy.json). libgit2 was only
an OPTIONAL, faster git-history code path guarded by HAVE_LIBGIT2 and
auto-detected via pkg-config; it always shipped with a popen("git log ...")
fallback that release binaries already used (they were built without
libgit2). Making that popen fallback the SOLE git-history implementation
means there is NO change to shipped behavior.

Changes:
- pass_githistory.c: collapse the HAVE_LIBGIT2 #ifdef so only the popen
  parse_git_log remains; drop the now-unused <git2.h>/<time.h> includes.
- cbm.c / cbm.h / main.c / subprocess.c / index_supervisor.h: remove the
  libgit2 mimalloc allocator bind and its >=1.7.0 version guards; keep the
  tree-sitter + sqlite3 binds; correct the comments.
- Makefile.cbm: remove the optional-libgit2 pkg-config detection block,
  the REQUIRE_LIBGIT2 error gate, and all LIBGIT2 CFLAGS/LIBS/FLAGS.
- .github/workflows/_test.yml: drop the REQUIRE_LIBGIT2 test-matrix leg
  plus its libgit2-dev/pkg-config apt install (lower CI cost, no new
  gating, no trigger change).
- flake.nix: drop libgit2/pkg-config from the dev shell (now-dead dep).

HAVE_LIBGIT2 no longer exists anywhere in the tree.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-05 00:37:59 +02:00
Martin VogelandAndy Zehady a27680c3a9 feat(graph-ui): dead-code filtering, node code preview, GitHub deep-links
Distilled from #789 (safe features only). Adds backend dead-code classification
(status + in_calls in the layout JSON), a GET /api/repo-info endpoint for GitHub
deep-links, and the frontend dead-code filters, node code preview, and deep-links.

The render-cap revert was dropped (kept 2000 for DEFAULT_MAX_NODES/HARD_MAX_NODES
and GRAPH_RENDER_NODE_LIMIT); the sidebar regex-search refactor was omitted.

Security fixes over the original: repo-info strips credentials from any returned
remote_url; the legitimate https blob-URL construction is allow-listed so the
static gate passes; libgit2 is not re-initialized/shutdown per request (reuses the
process-wide init from cbm_alloc_init); deep-link path segments are URL-encoded.

Co-authored-by: Andy Zehady <azehady@ciroos.ai>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-04 22:19:53 +02:00
Martin Vogel 738892194b Merge pull request #639 from Flipper1994/windows-red-tests-20260627
test(windows): add red regressions for platform-specific failures
2026-07-04 19:43:27 +02:00
Andrew Hundt 170aa7d830 feat(store): support additive overlay rows
Split active overlay readability from file replacement tombstones. Replacement overlay publishes still hide canonical file rows, while the new additive publish API layers safe overlay facts without tombstoning the file.

Update the active-overlay CTE, node-view summary, and benchmark mirror so additive overlay rows are visible while canonical rows for the same file remain visible.

Add a store canary proving an additive overlay symbol is readable without creating a file tombstone or hiding existing canonical rows.

Validation: uv py_compile benchmark harness passed; ASan/UBSan test-runner build passed; store_nodes suite passed 108; source-safety passed; diff check clean; production build passed.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-04 11:52:11 -04:00
Andrew Hundt ce009783da fix(store): dedupe active overlay edges
Project active overlay edges as logical graph edges instead of per-file overlay ownership rows. Cross-file edges can be owned by multiple changed files in one overlay generation for cleanup and compaction bookkeeping, but AI-facing active views must not inflate degree or schema counts.

Mirror the production active-edge semantics in the self-dogfood benchmark and add a store regression for duplicate cross-file overlay edge ownership.

Validation: CBM_ONLY_SUITE=store_nodes build/c/test-runner; make -f Makefile.cbm cbm; self-dogfood full matrix at /private/tmp/cbm-overlay-active-edge-dedupe-full-self-dogfood-20260704T.json; source-safety at /private/tmp/cbm-overlay-active-edge-dedupe-source-safety-20260704T.log.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-04 10:10:42 -04:00
Andrew Hundt 793ede109e fix(pipeline): keep header deltas off active overlay
Guard C-family header changes from active overlay publication until active edge parity is proven against fresh rebuilds. Bounded header edits can still use exact base-delta publication, preserving correctness without forcing every safe case to full rebuild.

Add a focused C-header incremental canary that verifies no overlay publish, exact frontier expansion, exact publish stats, and fresh-rebuild equality. Clean up stale direct-frontier helper code from the failed cap64 overlay experiment.

Validation: CBM_ONLY_SUITE=pipeline build/c/test-runner -> 323 passed; make -f Makefile.cbm lint-source-safety -> OK; self-dogfood cap16/cap64 store_pipeline_batch passed canonical equality but still fell back with frontier_too_large at 2.81x/2.74x.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
2026-07-04 09:47:04 -04:00
Martin Vogel fbd7287f4c fix(supervisor): wrap only marked host binaries; watchdog in worker mode
The index supervisor gate (cbm_index_supervisor_should_wrap) wrapped ANY
embedder of cbm_mcp_handle_tool: it resolved the CURRENT executable and
re-invoked it as '<self> cli --index-worker index_repository ...'. In a
test binary 'self' is the test runner, which ignores those args and
re-runs its suites instead, producing recursive spawn chains (observed
11-minute hangs, and multiplied kernel VM-map/memory load during the
2026-07-04 host panics). The gate is now opt-in: only a process that
called cbm_index_supervisor_mark_host() — the real binary's main(),
before any subcommand dispatch — may spawn workers. Embedders index
in-process by default; the test mains additionally pin
CBM_INDEX_SUPERVISOR=0 as belt-and-suspenders. Guarded by
index_supervisor_gate_requires_marked_host_issue845 (fork+alarm harness,
new spawn-count test hook; red on the old gate, green without the env
kill switch on the new one).

A supervised worker whose supervisor died also kept indexing as an
orphan: the parent-death watchdog only ran in MCP-server mode. run_cli
now starts the same watchdog (detached; on ppid change it only writes
to stderr and _exit(0)s, so it is safe outside server mode) when it
detects --index-worker. Guarded by tests/test_worker_watchdog.sh
(scripts/test.sh Step 5b): a CBM_TEST_HANG_ON busy-spin pins the worker
mid-index, the wrapper parent is killed, and the worker must self-exit
within 15s — red without the wiring, green with it.

Fixes #845.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-04 14:38:25 +02:00
FlipperandClaude Opus 4.8 0eb2c58913 test(windows): convert fixed reds to green guards, wire CI, rewrite drive test
Rebased onto current main and reworked in response to review. Three of the four
Windows reds were fixed upstream since the branch was cut at b075f05, so they are
now green regression guards; the fourth stays a genuine known-red.

- test_non_ascii_path.py (#636/#357): green guard - fixed by #700 (per-pass
  readers now route through cbm_fopen -> _wfopen). Re-verified green on main.
- test_hook_augment.py (#618): green guard - fixed by #619 (cbm_is_walkable_abs_path
  accepts drive-letter X:/ cwd). Re-verified green on main.
- test_ui_drive_listing.py (#548): rewritten. The fix exposes drives via a new
  roots[] field, not the dirs[] array the old test asserted (which would stay red
  against fixed code). Now asserts every fixed drive is in roots and browsable.
  Re-verified green on main (drives C:/D:/E:).
- test_cli_non_ascii_arg.py (#423/#20): unchanged - main() is still narrow-argv
  with no wide command line, so this remains genuinely red (the keeper).

- scripts/test-windows.ps1: split green guards (gate CI) from opt-in known-reds;
  add -GuardsOnly; run indexing in-process (CBM_INDEX_SUPERVISOR=0) so a guard
  reflects the path/hook/drive fix under test, not the index-worker spawn path.
- .github/workflows/_test.yml: new test-windows-guards job builds the product+UI
  binary (scripts/build.sh --with-ui) and runs the guards with -GuardsOnly so
  #700/#619/#548 stay enforced on Windows CI.
- RED_TEST_ANALYSIS.md: refreshed to record the landed fixes and current status.

Signed-off-by: Flipper <jacobphilipp@ymail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 13:22:41 +02:00
Flipper a4dbe89941 test(windows): add red regressions for hook augmenter and UI drive picker
Extends the Windows red-test suite with two more reproduced, Windows-specific
failures. No production fixes.

- windows_hook_augment_emits_context (integration, #618): the PreToolUse
  Grep/Glob augmenter `hook-augment` emits empty stdout for every payload on
  Windows. src/cli/hook_augment.c gates on POSIX-style absolute paths
  (cwd[0] == '/' and the walk-up loop's dir[0] == '/'), which a Windows
  drive-letter cwd never satisfies, so the graph augmentation never fires. A
  control search_graph confirms the symbol is indexed.

- windows_ui_picker_reaches_all_drives (integration, #548): the UI directory
  picker's GET /api/browse?path=/ returns no entries and never enumerates
  logical drives, so drives other than the system drive (D:\, E:\) cannot be
  selected. handle_browse in src/ui/http_server.c uses opendir without a
  GetLogicalDriveStrings root case. Needs a UI build (cbm-with-ui) and >1 drive;
  otherwise it reports a precondition (exit 2).

Also records additional ruled-out seed areas (get_code_snippet sanitizes
non-UTF-8 to U+FFFD #530.3; stdio handshake/flush works #513/#530.1/#635; mapped
subst-drive indexing keeps the DB #227/#367) and cross-platform items left out
of this Windows-only PR (#530.2 nested gitignore, #530.5 .git/info/exclude,
#530.4 libgit2 build, #581 memory soak).

Signed-off-by: Flipper <jacobphilipp@ymail.com>
2026-07-04 12:59:30 +02:00