Commit Graph

4 Commits

Author SHA1 Message Date
Martin Vogel 50392a4188 fix(scripts): use /usr/bin/env bash shebangs (NixOS has no /bin/bash)
On NixOS (and other non-FHS systems) /bin/bash does not exist, so
scripts with an absolute shebang fail to run. Switch the remaining
holdouts to /usr/bin/env bash: eleven scripts/*.sh,
test-infrastructure/run.sh, and the three Claude Code hook scripts
emitted by src/cli/cli.c (gate, session reminder, subagent reminder).

Distilled from PR #674, with parser-test coverage preserved: the
infra_parse_shell* fixtures in tests/test_pipeline.c intentionally keep
#!/bin/bash so absolute-path shebang extraction stays covered, and
tests/repro fixtures are untouched.

Also replace the GitHub-PAT-shaped fixture string flagged in the #674
thread with an obviously fake placeholder (ghp_FAKE...) that still
matches the ghp_ + 36-alnum secret detector.

Co-authored-by: Sandro Jäckel <sandro.jaeckel@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-03 20:11:49 +02:00
Austen Constable 5f19454724 Fix search_graph query= multi-minute latency: two-step FTS5 subquery
Flat BM25 queries of the form:
  SELECT ... FROM nodes_fts JOIN nodes WHERE MATCH ? AND project=? ORDER BY bm25() LIMIT N
block FTS5 WAND/MaxScore early-exit — the outer JOIN+WHERE is invisible to
the FTS5 planner, so it scores every matching document before any filter fires.
On a large codebase with 100K+ matches this causes 2–16 minute queries.

Fix: two-step subquery.  The inner FTS5-only query:
  SELECT rowid, bm25(nodes_fts) FROM nodes_fts WHERE MATCH ? ORDER BY bm25() LIMIT 2000
can early-terminate because no outer predicate blocks it.  The outer query
then joins and filters at most BM25_INNER_LIMIT (2000) candidates.

The count query uses the identical inner-limit subquery, so it benefits too.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 19:50:50 +02:00
Austen Constable 54951bc18f Remove internal project references from benchmark script
Make project a required CLI argument instead of a hardcoded name,
and remove internal query strings used during development testing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 19:50:50 +02:00
Austen Constable dd0ce4981e Fix search_graph name_pattern= performance: regex cache, LIKE pre-filter, cheap count
Three compounding bugs caused 1.5–8.5s latency on name_pattern= searches against
large projects (216K nodes), now reduced to ~0ms query time (cold-start dominates):

Fix 1 — regex compiled once per statement, not once per row
  sqlite_regexp / sqlite_iregexp now use sqlite3_get_auxdata / sqlite3_set_auxdata
  to cache the compiled cbm_regex_t for the lifetime of the statement. Previously
  cbm_regcomp + cbm_regfree ran for every row scanned.

Fix 2 — LIKE pre-filter cuts rows reaching the regex
  Wire cbm_extract_like_hints (already implemented but dead) into search_where_basic
  via a new where_add_like_hints helper. For .*Controller.* this prepends
  n.name LIKE '%Controller%', letting the idx_nodes_name index satisfy the LIKE
  clause first and passing only matching rows to iregexp(). Added search_like_pool_t
  to manage the malloc'd LIKE strings across both statement executions.
  ST_SEARCH_MAX_BINDS raised 16 → 32 to accommodate extra bind slots.

Fix 3 — count query no longer runs per-row edge subqueries
  The count SQL previously wrapped the full SELECT (which includes two correlated
  subqueries for in_deg / out_deg) in SELECT COUNT(*) FROM (...), executing those
  edge counts for every matching row even though the count needs none of that.
  Non-degree-filter path now uses SELECT COUNT(*) FROM nodes n WHERE <same WHERE>,
  which has no per-row subqueries. Degree-filter path retains the wrapped form
  since it needs those columns for the filter.

Benchmark on home-ubuntu-dev-sis (216K nodes, 509MB DB):

  Query                                BEFORE    AFTER   speedup
  name_pattern=.*Controller.*          3099ms    508ms     6×
  name_pattern=.*Service.*             2006ms    506ms     4×
  name_pattern=.*Repository.*          2006ms    508ms     4×
  name_pattern=specificFuncName        1506ms    507ms     3×
  label=Method + name_pattern=.*get.*  8509ms    509ms    17×
  name_pattern=.*Approve.*             1506ms    507ms     3×
  name_pattern=.*authorize.*           1506ms    509ms     3×

The ~500ms floor is cold-start I/O (opening a 509MB file from disk). In the
long-running MCP server process the warm-cache query time is sub-millisecond.

All store search tests pass including pagination, degree filter, and extract_like_hints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 19:50:50 +02:00