Commit Graph
208 Commits
Author SHA1 Message Date
Martin Vogel 897aa7ab1d test(smoke): echo the install --dry-run output on the dry-run-mode failure
The Windows dry-run failure prints an error (which passes the
install/skill/mcp/agent check) but no "dry-run" indicator, and the
smoke never echoed the captured output on THIS branch, so the actual
error stayed hidden. Echo it. (Root: Windows install routes through
cli_windows_managed_install, which errors on launcher/payload source
verification before emitting a dry-run plan - the next run will name
which check fails.)

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-19 07:10:25 +02:00
Martin Vogel 53d867dfc1 test-infra: per-suite timeout + arm64-safe mingw image (reliability)
Two reliability defects that bit this session: a wedged suite could
hang the runner (and the single local build slot) forever, and the
mingw image failed to build on an arm64 Docker host because its i386
Wine packages are unsatisfiable.

- run-tests-parallel.sh now caps each suite with `timeout --kill-after`
  (900s default, 3600s for the legitimately-slow incremental suite),
  where `timeout` exists; a killed suite fails loudly instead of
  blocking. Hosts without `timeout` run uncapped as before.
- Dockerfile.mingw makes Wine best-effort: the cross-COMPILE toolchain
  builds on any arch, and only the .exe-execution step needs Wine, so a
  Wine-unavailable host still gets a working compile check.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-19 00:42:19 +02:00
Martin Vogel d15a74a468 fix(smoke): capture daemon diagnostics on an MCP no-response failure
pr-smoke's mcp_run discarded the frontend's stderr (2>/dev/null), so a
first-session daemon start/connect failure - which with the daemon
architecture surfaces ONLY on stderr, stdout being reserved for
JSON-RPC - showed up as a bare "no initialize response (id:1)" with
nothing to diagnose (exactly what an isolated ubuntu pr-smoke failure
just produced). Keep the frontend stderr and, on an id:1/id:2 miss,
dump it alongside the durable cbm-daemon.log and daemon-conflicts.ndjson
tails from the cache root. No behaviour change on success.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 22:37:27 +02:00
Martin Vogel 1ce2dbec5c Merge verified compiler cache into the daemon branch
Combines the ccache steps with this branch's Windows hardening: the
protected temp root and profile staging keep their roles, the cache
steps slot alongside them, and the strictly per-ref keys apply
unchanged. The daemon branch's own CI legs warm from the second push
on.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 20:53:23 +02:00
Martin Vogel 129d8f7524 perf(ci): verified compiler cache for test and smoke legs, local and CI
ccache with CCACHE_COMPILERCHECK=content everywhere: every cache entry
is keyed on the CONTENT of the compiler binary plus the fully
preprocessed translation unit, so a hit is provably the identical
compilation - a stale, foreign, or corrupted cache can only MISS, never
return wrong output. No CCACHE_BASEDIR and no path rewriting: debug
info and sanitizer report paths stay exact.

Locally scripts/env.sh routes compilers through ccache's masquerade
directories when present (opt-out CBM_NO_CCACHE=1) - $CC keeps its
plain name, so verify_compiler, make, and link lines are untouched.

CI caches are strictly per-ref by policy on top of GitHub's own branch
scoping: keys embed github.ref, so no base-branch fallback - a new PR
builds cold once and only its own pushes warm it. Cached: the four
test jobs and the pr-smoke matrix. Release builds in _build.yml stay
deliberately uncached.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 20:51:56 +02:00
Martin Vogel c634c86d1a ci(windows): protected per-user temp root + explicit owner on staged guards
Completes the handoff task for the Windows harness: the native test job
ran with the MSYS-shared /tmp and the runner's inherited
LocalAppData\Temp ACLs, which the daemon's strict private-filesystem
checks reject by design. The test-windows job now creates a per-user
root under the profile with an owner-stamped, protected current-SID
DACL and routes TEMP/TMP (native form) and TMPDIR (POSIX form) through
it before scripts/test.sh runs.

The guards script already hardened its root DACL but ownership is never
inherited on Windows: bundle copies created by the admin-group runner
token can come out Administrators-owned, and the launcher's exe policy
requires the exact current user as owner. Stamp the current SID on the
staged bundle dir and both executables explicitly.

Job topology, gates, and triggers are unchanged; the new step is a
sub-second pwsh block in the existing test-windows job.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 16:58:10 +02:00
Martin Vogel 5dfce38daa fix(launcher): name the object and check behind every security refusal
The launcher answered every ownership/ACL problem with one generic
"launcher ownership or access policy is unsafe", which made the CI
guard failures (and any field report) undiagnosable: the same message
covers an untrusted owner SID, a mutation-capable ACE, a reparse point,
a hard-link clone, and a plain open failure across the whole ancestry
walk. Record the failing check at its site - offending path, owner or
ACE SID in SDDL string form, rights mask, Win32 error - and print it
once alongside the refusal. No policy changes; refusals stay refusals.

The interactive MCP smoke now also dumps daemon-conflicts.ndjson next
to the daemon log tail on failure, so an admission conflict names
itself instead of surfacing as a bare 30 s client timeout.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 16:57:58 +02:00
Martin Vogel 712d689efb fix(test): serialize the daemon-family suites in the parallel runner
The six suites that spawn coordinated worker subprocesses or bind local
endpoints (index_supervisor, daemon_application, daemon_runtime,
daemon_frontend, daemon_bootstrap, daemon_ipc) ran in the parallel wave,
where the saturated 3-4-core CI runners starve their fixed readiness
deadlines: index_supervisor waits at most 3 s for a worker marker while
the worker is a full re-exec of the ASan runner plus the admission
handshake. Both macOS legs failed the same four readiness assertions
deterministically and ubuntu-latest added four daemon_frontend and one
daemon_runtime timing failures, while an idle machine passes the same
suites 6/6 in the wave.

Moving them to the quiet serial tail follows the runner's existing rule
for this class (cli/subprocess/watcher/...) and also keeps the shared
per-account coordination namespace free of cross-suite admission
traffic. Same suites, same tests, same union-guarded totals - only the
schedule changes. Locally re-verified: 6742 passed, 0 failed, 2 skipped
(120 suites, 17 serial-tail).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 16:45:06 +02:00
Martin Vogel 7696b1486a fix: resolve remaining platform CI regressions
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 15:41:57 +02:00
Martin Vogel 3bd9e872ac Merge origin/main into feat/shared-coordination-daemon
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>

# Conflicts:
#	Makefile.cbm
2026-07-18 15:28:23 +02:00
Martin Vogel ad2874bcff fix: close remaining daemon coordination races
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 15:26:38 +02:00
Martin Vogel f35b10b79b fix: harden cross-platform daemon startup smoke
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 14:38:07 +02:00
Martin Vogel 5d8a9421fb Merge remote-tracking branch 'origin/main' into feat/shared-coordination-daemon
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>

# Conflicts:
#	src/mcp/mcp.c
#	tests/test_cli.c
2026-07-18 13:48: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 5fdbd7b1f4 Merge main (tree-format program #1154) into the parallel-test branch 2026-07-18 12:48:48 +02:00
Martin Vogel c8e07a2f9f fix(test-par): CRLF-proof the suite list, serialize deadline-sensitive suites
First CI validation round of the parallel runner, three findings:

- Windows failed ALL 104 suites with "0 passed, 1 failed" each: the CRT
  emits --list-suites lines as CRLF, so every dispatched name carried a
  trailing CR ("arena\r" is an unknown suite). The union guard caught it
  loudly, exactly as designed. The driver now strips CR when writing the
  suite list.

- The ubuntu-gcc legs had 3 real failures in the cli suite: it spawns
  subprocesses with fixed deadlines, and a fully saturated 4-core runner
  starves those deadlines into flakes. Deadline-sensitive suites (cli,
  subprocess, watcher, incremental, httpd, ui, index_resilience, and the
  stack_overflow family) now run SEQUENTIALLY after the parallel wave on a
  quiet machine — same suites, same tests, same gates, only the schedule
  differs; the union guard checks the combined result set.

- Failing suites now print every FAIL site with context (the tail-30 of a
  long suite log hid which tests actually failed on CI).

Local: totals unchanged (6,361 passed / 0 failed / 1 skipped), union guard
clean.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 12:24:07 +02:00
Martin Vogel ad58e71bea fix: stabilize cross-platform daemon coordination
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 11:48:59 +02:00
Martin Vogel bd939fc49a perf(test): run suites as parallel processes — same gates, ~2.5x faster
The runner executed all 104 suites sequentially (~10 min locally, the bulk
of every CI test leg). The suites are process-isolated already (per-process
mkdtemp HOME sentinel; two full parallel runs produced zero cross-suite
failures), so the serialization was pure convention.

- test-runner --list-suites: prints every registered suite, one per line,
  emitted by the SAME macro table that executes suites — the list cannot
  drift from the run set by construction.
- scripts/run-tests-parallel.sh: runs each suite as its own process
  (jobs = CPU count; CBM_TEST_PAR_JOBS overrides) under a ZERO-LOSS
  CONTRACT: a union guard fails the gate if the set of suites that
  produced a result differs from --list-suites (nothing can be silently
  dropped, a newly added suite is picked up automatically); per-suite
  pass/fail/skip are summed into the sequential runner's exact summary
  format; any suite crash, failure, or omission exits nonzero. Per-suite
  wall times are printed for balance tracking.
- make test-par: the parallel target. make test (sequential) is unchanged
  and remains the escape hatch (CBM_TEST_SEQUENTIAL=1 in test.sh).
- scripts/test.sh: builds, then routes through test-par — every CI test
  leg gets the speedup with zero workflow-topology change (same jobs,
  same gates, same billing; the legs just finish sooner).
- The stack_overflow suite is split into a/b/c (7+6+7 of its 20 tests,
  pure re-registration): as one suite it was the wall-clock critical path
  of any parallel run — every other suite finished underneath its ~4
  minutes.

Measured locally (Apple Silicon, ASan runner): sequential ~10 min vs
parallel 232 s, totals identical (6,361 passed / 0 failed / 1 skipped),
union guard clean. The TSan leg keeps its dedicated subset runner
(sequential) — out of scope here.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 11:05:17 +02:00
Martin Vogel cc5f71bdff fix(hook,smoke): teach the last two consumers the tree output shape
The PR CI caught two consumers the format migration missed — both parsed
tool output by its old shape and failed silently or with a wrong count:

- hook-augment built its PreToolUse additionalContext by reading the
  legacy 'results' object-array from search_graph format:json; after the
  json-tree reshape that key no longer exists, so the hook emitted nothing
  (caught end-to-end by tests/windows/test_hook_augment.py). The parser
  now walks groups -> qn_prefix/file + column-ordered rows. A new local
  test feeds the parser the LIVE server envelope, so any future drift
  between response shape and hook parser fails in the normal suite on
  every platform — not only in the Windows CI guard. Reproduced RED
  (ctx NULL, the exact CI failure) before the fix, GREEN after.

- smoke-test.sh carried six old-shape parsers (callers[N], rows[N]{,
  clusters[N], semantic[N]{, and the assert_toon_table helper); the tree
  headers never matched, so trace verification read 0 callers. All six
  updated to the tree contract; the full smoke script passes end-to-end
  locally.

cli suite 222 passed; lint clean; scripted smoke ALL PASSED.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 10:36:08 +02:00
Martin Vogel 98a28473db fix: harden cross-platform daemon startup
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 10:15:22 +02:00
Martin Vogel b8a75d142c fix: stabilize cross-platform daemon launch
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 03:16:37 +02:00
Martin Vogel cb896a3de4 fix: harden cross-platform daemon startup
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 02:43:14 +02:00
Martin Vogel 4693b625e0 fix: stabilize cross-platform daemon smoke
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 02:22:37 +02:00
Martin Vogel 83c137d2a5 feat: complete shared daemon lifecycle
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 01:26:08 +02:00
Martin Vogel 0e00ef5702 feat: coordinate concurrent CBM sessions
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-16 19:20:46 +02:00
Martin Vogel 2a6a49246f test: make path extraction newline-safe
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-13 03:08:29 +02:00
Martin Vogel e93f19d7fb test: normalize Windows instruction paths
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-13 02:57:34 +02:00
Martin Vogel 75aaf417aa test(cli): expand tiered agent smoke coverage
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-13 02:18:33 +02:00
Martin Vogel a3903caa0b feat(cli): expand agent integration coverage
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-13 01:50:02 +02: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
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
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
Flipper 0d3d0fb553 test(windows): add red regressions for platform-specific failures
Adds Windows-only red tests and analysis for native Windows failures found
during a Windows red-test campaign. This change contains no production fixes.

- windows_non_ascii_repo_path_preserves_definitions (integration): byte-identical
  TypeScript fixtures indexed under non-ASCII parent paths (Latin-1 accents,
  Cyrillic, CJK, Greek) extract zero definitions and only File/Folder nodes
  (5 nodes / 4 edges) versus the ASCII baseline (12 nodes / 20 edges / 5
  definitions). The pipeline source readers open files with fopen() on a UTF-8
  path, which the Windows CRT interprets in the active ANSI code page; directory
  discovery already uses the wide API, so files are listed but never parsed.

- windows_cli_non_ascii_repo_path_is_honored (integration): the documented
  `cli index_repository` entrypoint rejects a non-ASCII repo_path because main()
  does not read the wide command line, so argv arrives in the ANSI code page.

Both reproduce at the product surface (real MCP process, real stdio, real SQLite
DB), are deterministic, and pass on Linux/macOS. A PowerShell runner builds the
binary and runs the suite; standard-library Python only. See
tests/windows/RED_TEST_ANALYSIS.md for environment, commands, ruled-out seed
areas, and suspected fix locations.

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