56 Commits

Author SHA1 Message Date
Martin Vogel 7f3e30e1e1 fix(daemon): let a shipped build relocate the rendezvous via CBM_RUNTIME_DIR
The daemon/CLI rendezvous directory is created under %LOCALAPPDATA% (Windows) or
/tmp -- /private/tmp on macOS -- and every ancestor of it must pass the
private-directory walk. That ancestry is not always acceptable, and when it is
not, EVERY invocation fails, `config list` included, so the settings surface
cannot be reached either:

    codebase-memory-mcp: secure daemon endpoint could not be created

#1623 narrowed the Windows side of this by admitting AppContainer package and
capability SIDs on ancestors, and named the remainder explicitly: a live local
group, Authenticated Users inherited from a secondary volume root, and orphaned
unresolvable SIDs still refuse, and "those need CBM_RUNTIME_DIR or a separate
change". #1621 is the POSIX shape of the same dead end -- /private/tmp/cbm-daemon-<uid>
refused with no way to move it.

There was no way to move it in a shipped build. The only relocation hook,
CBM_TEST_DAEMON_RUNTIME_PARENT, is compiled out unless CBM_ENABLE_TEST_SEAMS is
defined, so a test build started while the shipped build did not; CBM_CACHE_DIR
is no help either, because it moves the cache and never the rendezvous.

CBM_RUNTIME_DIR names the parent directory the rendezvous is created under. It
does NOT relax the check: the directory it names goes through exactly the same
validation as the default -- ancestors owned by you or root, not world-writable,
no allow-ACL; the rendezvous directory itself still forced to owner-only -- and a
value that fails is refused rather than silently replaced by the default. The
operator only chooses an ancestry that passes. cbm_safe_getenv never truncates,
so no half of an over-long value can become a runtime parent.

The override is resolved in cbm_daemon_bootstrap_endpoint_new(), the one function
every product endpoint goes through: the daemon, the MCP client, the local CLI,
the index worker, and the install/update/uninstall activation path in cli.c. No
call site can silently keep the default, and the detached daemon inherits the
value with the rest of its environment. An explicit parent still wins, so the
compile-time test seam and the lifecycle guards' isolated namespace behave
exactly as before.

Approach and variable name from #1576 by Leonardo trindade miranda, resolved one
layer lower so the activation path is covered too.

Refs #1574
Refs #1621

Co-Authored-By: Leonardo trindade miranda <tmonestudio@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-14 20:04:08 +02:00
Martin Vogel 98d7dbab01 feat(ci): add a third release candidate (debug-stripped) as an extra VT draw
Release evidence from run 31744302624 shows the tolerated Microsoft `!ml`
verdict is close to a coin flip per byte image rather than a property of the
code. Across the eight targets the stripped and unstripped candidates of the
SAME linker output disagreed on four, and in both directions:

  linux-amd64    stripped microsoft-ml   unstripped clean
  darwin-arm64   stripped clean          unstripped microsoft-ml
  linux-arm64    stripped clean          unstripped microsoft-ml

If the classifier were keying on something intrinsic to our code the siblings
would agree; they do not. So each variant is close to an independent draw, and
5 of 16 candidates drew the flag.

Two draws is not always enough. On that run linux-amd64-portable came back
microsoft-ml on BOTH candidates, leaving no clean binary to ship for that
target. A third independent draw at a ~31% observed per-candidate hit rate takes
the both/all-flagged case from roughly 1-in-10 per target to roughly 1-in-30.

The third candidate is `--strip-debug` (Apple: `-S`): debug information removed,
symbol table kept. Behaviourally identical to the other two — same linker
output, only metadata differs — but a distinct byte image, which is all
VirusTotal needs to scan it as its own file. Verified on the real v0.10.4
candidates: linux-amd64 gives three distinct hashes (294,634,656 /
294,623,208 / 293,746,096 bytes) and darwin-arm64 likewise, with the ad-hoc
signature verifying after strip.

Selection is unchanged in spirit and now ordered: smallest artifact first
(stripped, debug-stripped, unstripped), take the first CLEAN one, and only if
every candidate drew the tolerated verdict ship the smallest flagged one. A
hard verdict on any candidate still blocks the release before selection.

Cost is 24 objects per release instead of 16.

Derivation enforces that all three hashes differ — identical candidates would be
one draw wearing three hats, and the selector would believe it had alternatives
it does not have. Public claims in README, SECURITY.md and docs/index.html
updated from "both stripped and unstripped" to the three candidates.

All three release contract tests pass, including the native derivation test
which exercises the real strip and codesign path on this host.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-13 23:52:21 +02:00
Martin Vogel db26e9615e fix(ci): stop gating the release on how many VirusTotal engines answered
The build+VT dry-run (run 31744302624) blocked all eight targets on:

  BLOCKED: objects/3eb486a4... completed with only 48/75 decisive engines (< 50)

That object is the windows-arm64 STRIPPED candidate with ZERO detections. It
was classified "hard" — the same bucket as a real malware verdict — purely
because 48 engines answered instead of 50. Its own sibling scanned clean with
66.

How many engines return a decisive result is VirusTotal fleet availability on
the day, not a property of our binary. These artifacts are ~300 MB and many
engines skip or time out at that size, so the count sits near the floor and
moves run to run: on this run one object landed at 48 and the other fifteen
spread 59-68. A floor there makes shipping a lottery decided by someone else's
infrastructure, which is exactly what our CI-determinism rule forbids, and it is
not something we can influence by fixing anything on our side.

Classification now depends only on what engines FOUND:
  hard        - two or more engines, any non-Microsoft engine, any label that is
                not `!ml`, or anything suspicious. Still blocks the release.
  microsoft-ml- exactly one Microsoft `!ml`. Tolerated and disclosed.
  clean       - nothing flagged.

The decisive-engine count is still recorded per candidate in the results
manifest and printed as a NOTE when it is below the reference value, so the
evidence is unchanged; only its power to block is gone. The selector keeps
rejecting an incoherent response (total < completed).

Public claims updated to match in README.md, SECURITY.md and docs/index.html —
all three promised "at least 50 decisive engines" as a publication requirement,
which would no longer have been true.

Contract test updated: a clean verdict now stands regardless of engine count,
and the below-reference NOTE must still be reported. All three release contract
tests pass (vt-gate-policy, release-gate-chain, candidate-derivation).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-13 23:45:06 +02:00
Martin Vogel 3904e59372 ci: select release binaries before smoke testing
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-13 19:16:19 +02:00
Martin Vogel 23e4fb0b4e fix(ci): scan extracted UI release files only
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 02:39:09 +02:00
Martin Vogel 8018561cfe fix(release): externalize runtime assets and harden VT verification
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 17:35:05 +02:00
Martin Vogel 7fa5b07718 feat: enforce one workspace boundary on every indexing entry point
Routes the MCP index_repository handler and the graph UI's POST /api/index
through a single decision function. The UI route previously checked only that
root_path was a directory, so an operator's configured boundary held on one
entry point and not the other; it now canonicalizes first and applies the same
policy, answering 403 with the reason.

The decision is two-tier, because a bare default-deny would refuse every
first run and a bare opt-in leaves the default open:

- Breadth is always enforced, with nothing configured. Filesystem, drive and
  share roots, top-level system trees, the home directory itself and
  credential directories are refused as indexing roots out of the box.
- Containment in a declared root applies once CBM_ALLOWED_ROOT is set or a
  grant exists, and is evaluated first so a path outside a configured root is
  reported as exactly that.

Three things the tests caught, each a real defect rather than a test fix:

- On macOS /etc, /tmp and /var are firmlinked under /private, so
  canonicalizing "/etc" yields "/private/etc" and counted two deep — sailing
  past a minimum of two, missing the very path being guarded. Depth now
  discounts a leading "private" component.
- An earlier draft refused any root containing the cache directory. That was
  over-claimed: the indexer only parses recognised source files and a graph
  database is binary SQLite it would never extract. Refusing a whole root is
  also the wrong remedy where the concern does hold — not walking the cache
  is. Removed, with the reasoning recorded at the site.
- Rewording the refusal to "outside every allowed root" broke an assertion
  matching "outside the allowed root", and that test's early return skipped
  its CBM_ALLOWED_ROOT cleanup, leaking the variable into every later test in
  the suite. The original wording is kept and guidance appended instead.
  Worth remembering: these contracts match strings, not properties.

Docs updated in the same change, since both env-var tables said "unset
imposes no restriction" and that is no longer true: CONFIGURATION.md and
README.md describe the two tiers, and CONFIGURATION.md lists the always-
refused roots along with the two limits that matter — this constrains scope
rather than sensitivity, and the credential list is a denylist that raises the
cost of a mistake rather than closing the class. SECURITY.md's supported-
versions table was still on 0.8.x and now reads 0.9.x.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-06 01:02:02 +02:00
Martin Vogel a8baa5fcb8 Merge pull request #1131 from gandie/docs/mention-ignore-opt-in
Mention un-skip option in docs
2026-07-31 02:39:38 +02:00
Martin Vogel 0baddf7887 daemon: permanent lifecycle, daemon-backed CLI/hooks, real-Windows hardening, long-path launcher transactions
Daemon lifecycle and Windows correctness, verified on a real Windows 11
ARM64 VM through the maintained test-infrastructure/vm drivers, plus the
macOS and Linux arm64 suites and the container lint gate.

Daemon lifecycle:

- daemon start/stop/status subcommands. `daemon start` launches a
  PERMANENT daemon (spawn shape is byte-exact argv; survives idle
  periods and session ends) and reports an already-active daemon
  instead of failing. Permanence is honored at every stop latch:
  last-committed-client disconnect, host initial-client window,
  coordinator release, and application final-session close — a
  permanent daemon also keeps admitting new sessions after its last
  one closes.
- daemon stop refuses while sessions are active and lists the blocking
  peers (pid/role) that must finish first; an idle daemon drains
  through the activation-shutdown machinery with the ACK ordered after
  connection interrupts. A second stop is idempotent. The wire ops are
  no-cohort first-frame requests with peer fingerprint authentication,
  so stop/status never conflict with an exact-build admission gate.
- One-shot CLI commands now execute through the daemon (index workers
  keep their local supervised path). A cold CLI run that had to spawn a
  temporary daemon prints a hint that `daemon start` removes the
  per-command startup tax; a warm daemon is recycled silently.
- Hooks are connect-only fail-open: with no daemon present the hook
  emits a visible, rate-limited notice (Claude-dialect systemMessage
  plus stderr for other dialects) and always exits 0 — augmentation is
  never allowed to block the caller's tool use.
- Version skew: a newer-build client automatically drains an
  older-build permanent daemon (strict semantic-version triples only;
  dev builds never auto-drain) and the build-conflict message names
  `cbm daemon stop` as the manual escape hatch.

Windows IPC/runtime (real-VM verified):

- ipc(win): persistent pending overlapped ConnectNamedPipe. The accept
  path used to destroy its listening pipe instance on every 20 ms poll
  timeout; a client attaching in the teardown window was severed or left
  on an orphaned pipe object whose HELLO no server handle could ever
  read, absorbing the connect until the client's own timeout expired.
  The pending connect now survives poll timeouts and nothing is
  destroyed while a client could be attaching.
- ipc(win): drain-before-close for final responses. Closing a named-pipe
  server handle can discard a just-sent response before the peer reads
  it (POSIX stream sockets never lose buffered data on close). A bounded
  cbm_daemon_ipc_connection_drain (read-until-peer-EOF; no-op on POSIX,
  immediate on interrupted connections) now precedes close in
  runtime_worker_finish and runtime_reject_inline, so hello-conflict,
  capacity and disconnect acknowledgements reliably reach the peer.
- runtime: CLOSE_INTENT wire frame. A Windows named-pipe client has no
  transport half-close, so close_begin now announces departure with an
  explicit frame (ordered after APPLICATION_CANCEL, before the local
  interrupt); the server releases the client's admission on receipt
  instead of waiting for the handle to close. Admission-drop timing is
  now identical to POSIX shutdown() semantics on every platform.
- runtime(win): client close cancellation. close_begin serializes with
  request publication under the send lock, best-effort sends the active
  token's APPLICATION_CANCEL frame, then interrupts local I/O; the
  server cancels MCP/subprocess work promptly. Contract tests accept
  both correct outcomes (interrupted transport or decoded CANCELLED).
- runtime: activation acknowledgement ordering. The activation ACK is
  the requester's license to act on "snapshotted and draining", so every
  connection interrupt is now initiated before the ACK is sent; a
  session could previously get one more request serviced after the
  requester observed the ACK.
- service(win): deadline-bounded private-file prepare. The conflict-log
  prepare retry loop (100 x Sleep(2), which rounds up to the ~16 ms
  timer granularity) burned ~1.6 s against permanently obstructed paths,
  stalling hello rejections past the client's timeout. The retry budget
  is now a 250 ms deadline; transient share collisions still retry.
- subprocess(win): cmd.exe /C payload encoder quotes metacharacters
  correctly (root cause of the git-on-Windows failure cluster).
- watcher: SHA-256 buffer sizing (CBM_SZ_64 -> CBM_SZ_128) and a native
  Windows stop/unwatch cancellation test with exact-image verification.
- httpd: send_all writes in bounded 64 KiB slices. A single giant
  nonblocking send() on Windows is absorbed wholesale into AFD kernel
  buffering regardless of SO_SNDBUF, so send deadlines and interrupts
  could never engage against a slow peer (and the full payload was
  pinned in nonpaged pool). Slicing restores a deterministic
  backpressure point; a test hook pins SO_SNDBUF for the deadline and
  interrupt tests.
- ui/http: shutdown lifecycle — interrupt checks, response-wide send
  deadline, explicit connection states, refusal to free a server while
  a listener-owned connection is active.

Windows long-path support:

- Central path-aware wide conversion (canonicalize via GetFullPathNameW
  and prepend the extended-length prefix for absolute paths >=240) at
  the compat chokepoints (cbm_fopen/compat_fs/mkstemp/mkdtemp), sqlite
  store opens, and the daemon build-fingerprint/log paths. Deep managed
  installs (a 64-hex generation directory routinely exceeds MAX_PATH)
  now index, stage and activate correctly.
- activation transaction: its own file APIs and the component-walking
  ancestry validators now operate in the extended-length namespace;
  the launcher path is canonicalized (and prefixed when deep) once at
  entry so every downstream exact-string comparison stays
  form-consistent.
- Executable self-resolution uses the wide APIs (GetModuleFileNameW,
  GetFileAttributesW) so non-ASCII install paths survive argv[0]
  resolution.

Windows launcher install/uninstall transaction:

- FileRenameInfoEx names are NUL-terminated in an over-allocated
  buffer. FileNameLength governs per the contract, but filter drivers
  read FileName as NUL-terminated and appended adjacent heap bytes to
  created names — a flaky, garbage-suffixed rename target. Both the CLI
  and the launcher rename helpers are fixed.
- Uninstall retires state via rename-aside (.cbm ->
  .cbm-retired-v1-<tag>-<pid>) with the retired tag shortened to 16 hex
  chars so the bare rename target stays under the FileRenameInfoEx
  NT-conversion ceiling at guard depths; 64 bits still uniquely
  identify the generation.
- When the running launcher's mapped generation backings pin .cbm
  against rename, the backings are relocated to activation-<pid>-N
  .retired tombstones beside the install (a mapped image may be renamed,
  never deleted; the launcher's liveness-guarded sweep reclaims stale
  tombstones). Every relocation is recorded, and a FAILED uninstall
  reverses the moves after restoring .cbm — via MoveFileExW with
  extended-length paths on both arguments, since the deep generation
  target is beyond the handle-based rename's bare-path reach — so a
  restored install keeps its generation backings and stays runnable.
- After a committed uninstall the retired tree's backings are relocated
  out so the tree is shallow enough for the detached cleanup's rd, and
  the cleanup's working directory strips the extended-length prefix
  (CreateProcessW lpCurrentDirectory silently ignores prefixed paths).
- Files created under Administrators-default-owner directories
  (CopyFileW destinations, CREATE_NEW tombstones, probe directories)
  are explicitly owner-stamped so the exact-owner validators hold on
  runner images; guard fixtures stamp hand-built trees the same way.

Diagnostics, tests and infra:

- diagnostics: discovery is now an always-delivered JSON control record
  (new cbm_log_control) that survives CBM_LOG_LEVEL suppression and
  paths containing spaces; placement honors $TMPDIR with /tmp fallback
  via a diagnostics-local helper; the soak parser reads the JSON record;
  documented in docs/CONFIGURATION.md. Red-first coverage for suppressed
  log levels, TMPDIR-with-spaces, and native Windows output-contract
  assertions.
- tests(win): daemon_ipc/daemon_frontend fixtures now build endpoint
  parents with production-shaped ancestry (LocalAppData on Windows, via
  th_secure_runtime_parent_new) — the runtime ancestry validation
  correctly refuses temp roots whose ancestors grant mutation rights to
  Authenticated Users (C:/msys64/tmp, GitHub-runner work dirs) — and
  drive the documented startup-owner publication flow before reading
  generation-bound endpoint addresses. This turns the 26 Windows
  failures previously visible in CI's full-test job green without
  weakening any validation.
- tests(win): the launcher guard covers the full permanent-launcher
  contract including failed-uninstall restore and immediate reinstall
  after uninstall; new daemon lifecycle and reworked hook-augment
  guards run the start/recycle/stop flow end to end.
- tests: CBM_SKIP_PERF is now actually consumed by the test runner
  (it was set by CI but never read, so perf suites ran everywhere);
  four throughput/bench suites are classified as perf, the heavy
  store_arch suite moved to the slow-timeout tier, and two
  wall-clock-sensitive assertions were rewritten as invariant checks
  with coarse hang-detector backstops.
- build/test infra: build-dir safety contract, UI dev-proxy security
  contract, soak daemon-recovery contract, path-safety helper, the
  Windows VM worktree-sync contract wired into scripts/test.sh, and
  vm/win.sh guards building its clean embedded-UI product in an
  isolated BUILD_DIR so it cannot clobber the incremental test build.
  provision-windows.sh now installs Node.js for the guards UI build.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-21 15:32:15 +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
Lars Bergmann 45c9b62d76 Adjust Precedence section intro
Signed-off-by: Lars Bergmann <L.Bergmann@mittwald.de>
2026-07-17 08:21:51 +02:00
Lars Bergmann ce90b3dd9d Mention un-skip option in docs
Signed-off-by: Lars Bergmann <L.Bergmann@mittwald.de>
2026-07-16 13:54:35 +02:00
Martin Vogel f0db224140 feat: add coverage-aware agent integrations
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-13 01:50:03 +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 5758e9bb4c docs(eval-plan): mark Appendix A chapters as drafts pending ground-truth regen
DCO / dco (push) Has been cancelled
The per-language chapters were LLM-drafted to establish format and
dimension mapping — their symbol targets are not yet ground-truth-derived
and must be regenerated at execution time per §3.1/§12. Make that status
explicit at the top of Appendix A so the drafted symbol names are not
read as validated.

Supersedes #420 (same diff; recreated for DCO author/sign-off).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-09 17:27:10 +02:00
Martin Vogel cc1608210d docs: document the CBM_ALLOWED_ROOT indexing boundary
Add CBM_ALLOWED_ROOT to the environment-variable tables in README.md and
docs/CONFIGURATION.md: when set, index_repository refuses a repo_path that
resolves outside it; unset imposes no restriction.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-07 22:20:23 +02:00
Martin Vogel 59da01e87f Merge pull request #484 from Bortlesboat/docs/config-reference-issue-359
docs(config): add a reference page for existing config files
2026-07-05 00:38:22 +02:00
Martin Vogel 23de5b23af docs: add .cbmignore how-to (syntax, precedence, negation semantics)
Documents the current behavior as implemented in src/discover/discover.c
and src/discover/gitignore.c: where .cbmignore is read from, the glob
features the parser supports, the layered precedence against built-in
skip lists / .gitignore hierarchy / git global excludes, and what
negation can and cannot override today. Planned negation unification
(un-skipping built-in dirs, non-negatable safety core, shared predicate
for auxiliary walkers) is listed in an explicitly not-yet-implemented
subsection. Linked from the README "Ignoring Files" section.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-03 19:22:19 +02:00
Martin Vogel 53ebeb4cf1 docs(security): coordinated-disclosure process + realistic reporting policy
Add docs/SECURITY-DISCLOSURE.md describing how vulnerability reports are
handled end to end (private fix, cross-platform validation, reporter
verification, patched release, then a GitHub Security Advisory with a CVE
and credit).

Update SECURITY.md: add GitHub private vulnerability reporting as the
preferred channel, replace the over-tight 48h/7-day commitment with honest
best-effort targets for a solo-maintained project, add a safe-harbor
statement, and refresh the stale supported-versions table (0.5.x -> 0.8.x).

Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
2026-06-22 09:19:51 +02:00
Andrew Barnes e7182cd3fe docs(config): add configuration reference for existing config files
Signed-off-by: Andrew Barnes <bortstheboat@gmail.com>
2026-06-17 09:08:32 -04:00
Martin Vogel 3ee9d5d112 Drop the nim grammar and refresh language counts
Removes the heaviest vendored grammar (66 MB) along with its language
wiring and test fixtures; the supported-language count moves to 158
across README, site, and manifest. The grammar manifest additionally
records the canonical-source decisions for the five
registry-disagreement grammars and the per-directory license files
restored earlier this week.
2026-06-12 02:17:39 +02:00
Martin Vogel 20bf70e238 Reword Hybrid LSP description; correct grammar count to 159
Describe the Hybrid LSP layer as a lightweight C implementation
structurally inspired by and compatible with major language servers,
rather than a clean-room re-implementation of their algorithms, across
README, site, and llms.txt. Drop intelephense from the reference list.
2026-06-12 00:02:30 +02:00
Martin Vogel ad5d45ab3d Document the v0.8.0 hybrid LSP lineup across README and site
Java, Kotlin, and Rust join the Hybrid LSP sections: badge and intro counts go 6 -> 9 language families, the capability tables gain the three new resolver rows (README + docs/index.html), the clean-room reference list adds Eclipse JDT and rust-analyzer, llms.txt and the landing-page JSON-LD/FAQ are aligned, the stale test-count badge moves to 5,577, and the sitemap lastmod is refreshed.
2026-06-11 23:44:34 +02:00
DeusData bd6d617bcf ci: deploy Pages via Actions, scoped to docs changes (#425)
The legacy "Deploy from a branch" Pages build ran Jekyll on every push to
main and failed on the planning docs under /docs — Jekyll's Liquid parser
hit `Unknown tag 'data'` in EVALUATION_PLAN.md, so the website build went
red after nearly every commit.

/docs is a hand-written static site (index.html + robots/sitemap/llms.txt),
so it never needed Jekyll. Deploy it with a GitHub Actions workflow that
uploads /docs as-is and only triggers on docs/** changes, and add a
.nojekyll guard. Switch the Pages source to "GitHub Actions" for this to
take over from the legacy build.

Co-authored-by: DeusData <joerge@datadice.io>
2026-06-08 18:26:11 +02:00
Martin Vogel 16034f21a8 docs(eval-plan): ground D1-D5 in the literature and external QA benchmarks
Address peer-review feedback on question provenance/external validity.

- Anchor the five dimensions to Sillito, Murphy & De Volder's catalogue of
  questions programmers ask during software-evolution tasks (FSE'06/TSE'08),
  with LaToza & Myers (2010) and Ko et al. (ICSE'07) as support; add a
  D1-D5 -> Sillito-group mapping table
- Adopt published repo-level QA benchmarks for the major languages: reuse and
  compare against SWE-QA (primary), with CoReQA, CodeRepoQA and RepoQA as
  references; note snippet-level sets (CodeQA/CS1QA/CoSQA) as the contrast
  that motivates structural retrieval
- Policy: anchor to Sillito; reuse SWE-QA for major languages; generate the
  multilingual remainder from independent LSP/git ground truth, never from
  the model under test
2026-06-06 11:49:48 +02:00
Martin Vogel e46d512a77 docs: rework benchmark plan into 159-language evaluation spec
Rename TEST_PLAN.md to EVALUATION_PLAN.md and rebuild it as a peer-review
evaluation specification covering all 159 supported languages.

- Graph (MCP) vs Explorer (grep/glob/read) per language, 5 bespoke questions
  mapped to 5 universal dimensions (D1-D5) for cross-language aggregation
- Sequential, main-channel execution: one language at a time, Explorer the
  only subagent, per-language index lifecycle (cold index -> eval -> delete)
  to avoid concurrent-indexing memory blowups; index time is a key metric
- LLM-as-a-Judge grading (single disclosed model, blind A/B, median of passes)
- Per-language node-type and edge-type histograms with every one of the 32
  edge types listed, zeros kept (a zero is a real extraction-gap signal)
- Cross-repo intelligence + semantic/similarity deep-dive for the 9 LSP
  languages, with recall/precision ground truth
- Single result tree (eval-results/), retiring the versioned-dir scheme
- Master repo assignment for all 159 languages; per-language chapters in
  Appendix A
2026-06-06 11:49:48 +02:00
DeusData 4b466396aa Delete docs/BENCHMARK_PYTHON.md 2026-06-05 18:55:34 +02:00
Martin Vogel 0338685c06 Advertise semantic search and correct language count across docs
Surface previously undocumented capabilities on the site, README, and
package metadata: semantic vector search (bundled nomic-embed-code
embeddings, fully local), SEMANTICALLY_RELATED / SIMILAR_TO clone
detection, cross-repo intelligence, data-flow tracing, change-impact
analysis, and ADR management. Broaden the cross-service entry to cover
gRPC/GraphQL/tRPC and pub/sub channels. Add a "star on GitHub" nav CTA.

GEO/SEO: add a question-style semantic-search section, extend the
JSON-LD featureList and FAQPage, add semantic keywords, and refresh the
sitemap lastmod.

Correct the language count to 159 (was 155/158) everywhere while keeping
the 157 vendored-grammar figure, update the Linux-kernel index stats to
the measured 4.81M nodes / 7.72M edges, and make trace_path the canonical
tool name (trace_call_path noted as an alias).
2026-06-05 18:48:41 +02:00
Martin Vogel efec2ceffe docs: rebuild landing page, update facts and add GEO/SEO + live releases
- Update to current capabilities: 158 languages, 11 agents, 14 MCP tools,
  Hybrid LSP, infrastructure-as-code indexing, single static C binary
- Add dedicated Hybrid LSP section and per-language resolution table
- Remove the GitNexus comparison
- Restructure with answer-first, question-based headings for AI answer engines
- Add JSON-LD structured data (SoftwareApplication, Organization, WebSite,
  BreadcrumbList, FAQPage) with arXiv preprint citation
- Add a live GitHub releases widget (client-side, graceful fallback)
- Add prominent research-preprint callout under the hero
- Centered nav, "by DeusData" subtitle, inline SVG favicon
- Welcome AI crawlers in robots.txt, refresh sitemap, add llms.txt
- Fix broken benchmark link
2026-06-01 21:12:06 +02:00
DeusData 25ba4472d5 Delete docs/TS_LSP_INTEGRATION_PLAN.md 2026-05-30 23:14:46 +02:00
DeusData fd5cbedd1a Delete docs/PLAN_PHP_LSP_INTEGRATION.md 2026-05-30 23:14:26 +02:00
DeusData eea63b2f27 Delete docs/CROSS_FILE_ARCHITECTURE.md 2026-05-30 23:14:16 +02:00
DeusData 19be397b05 Delete docs/PHP_LSP_PRE_FLIGHT.md 2026-05-30 23:14:03 +02:00
Martin Vogel d1143fbdce feat(lsp): TypeScript / JavaScript / JSX / TSX hybrid LSP resolver
Adds clean-room ts_lsp resolver covering single- and cross-file type
resolution for TS, JS, JSX, TSX. Mirrors the structure and entry-point
pattern of the existing Go and C/C++ LSP hybrids (single-file
cbm_run_ts_lsp + batch cbm_run_ts_lsp_cross), with TS-specific surface:

- Hash-table indexed type registry (FNV-1a, lazy via cbm_registry_finalize)
- Partial structural relater for NAMED / BUILTIN / LITERAL / TUPLE /
  UNION / INTERSECTION / FUNC / TEMPLATE / TYPE_PARAM / ALIAS
- Conditional types T extends U ? X : Y with union distribution
- 'infer X' constraint solver, keyof / typeof / T[K] type-position parsing
- Flow-sensitive narrowing (instanceof / typeof / discriminated unions),
  optional chaining, non-null assertion
- TS utility-type passthrough (Partial, Pick, ReturnType, Awaited, etc.)
- Function overload resolution by argument types
- Async iterables + 'for await ... of'
- Curated DOM stdlib subset + comprehensive base stdlib
- Mode flags: js_mode (.js/.jsx with JSDoc inference), jsx_mode (JSX
  expressions), dts_mode (.d.ts ambient declarations, no resolved calls)

Touches existing files only with additive changes:
- type_rep.h/.c: new TS-specific CBMTypeKind values + constructors
- type_registry.h/.c: TS-specific fields, hash indexes, cbm_registry_finalize
- cbm.c: dispatch for JS/TS/TSX languages
- lsp_all.c, Makefile.cbm, tests/test_main.c: include + suite registration
- README.md: feature list mention

291 ts_lsp test cases. CBM_LSP_DISABLED=1 disables the resolver
(matches CBM_LSP_DEBUG / CBM_LSP_DISABLED knobs in go_lsp / c_lsp).
2026-05-10 14:44:50 +02:00
Martin Vogel e6f4aa74bd Merge origin/main into worktree-php-lsp-integration
Resolved 5 conflicts where the php-lsp branch and main both added LSP
infrastructure but with different APIs:

- Makefile.cbm: kept both TEST_PHP_LSP_SRCS and main's py_lsp/scope/
  type_rep/stack_overflow test source variables in ALL_TEST_SRCS.
- internal/cbm/cbm.c: kept both #include lsp/php_lsp.h and lsp/py_lsp.h;
  the per-language dispatch already supports stacking PHP after PYTHON.
- internal/cbm/lsp_all.c: kept both lsp/php_lsp.c + php_stdlib_data.c
  AND lsp/py_lsp.c + python_stdlib_data.c in the compilation unit.
- src/pipeline/pass_calls.c: took main's version entirely.
  find_lsp_resolution + resolve_single_call(ctx, call, lsp_calls, ...)
  is language-agnostic — it matches any CBMResolvedCall whose caller_qn
  hits the textual call's enclosing function and whose callee_qn
  short-name hits the textual callee. PHP LSP populates the same
  result->resolved_calls array via cbm_run_php_lsp, so the unified
  resolver handles PHP for free. Dropped the php-branch's
  qn_short_name + lsp_override_resolution helpers as superseded.
  Locked confidence floor at main's CBM_LSP_CONFIDENCE_FLOOR = 0.6f
  (was 0.5f on the php branch).
- tests/test_main.c: kept all suite externs and RUN_SUITE invocations
  for php_lsp + py_lsp/py_lsp_bench/py_lsp_stress/py_lsp_scale +
  scope + type_rep.

Auto-merged: src/pipeline/pass_parallel.c.

Brings in 52 commits from main since the branch was forked at c768b61
(#207 watcher follow-up): #208 through #281, dependabot bumps, Pine
Script, AUR docs, INHERITS edges, get_architecture wiring, codeql
revert, the entire python-lsp subsystem.

Suite: 3249 passed, 0 failed (was 2971 on main; +278 new tests from
php-lsp matches the branch's claimed test count).
2026-05-09 17:14:14 +02:00
Martin Vogel 50b218e5d5 docs(benchmark): final PHP-LSP numbers — 278 tests, ~10k LoC 2026-05-09 16:06:20 +02:00
Martin Vogel 65964b4267 docs(benchmark): refresh PHP numbers — 248 tests, 47k CALLS edges 2026-05-09 15:02:49 +02:00
Martin Vogel 7a77a178ec docs(py_lsp): final results — 43/43 stress patterns PASS, zero gaps
BENCHMARK_PYTHON.md updated through Round 10. The stress-test suite
went from 22 patterns (19 hard / 3 KNOWN GAP) to 43 patterns (all
hard-asserted PASS). Bench held at 100% (52/52) across the surface
expansion.

Patterns added in Round 10 cover the framework / real-world cases
that distinguish parity-with-LSP from passing-toy-fixtures:
SQLAlchemy 2.0 Mapped[T], Pydantic BaseModel, async with / async
for, Callable[..., R], next(iter), **kwargs annotation, dict-literal
dispatch tables, lambda call-site inference, recursive types,
classmethod-to-method chains, dataclass default_factory, nested
match patterns.

The original Phase 11 plan called this category "compiler-grade".
The actual line where compiler-rebuild starts is now narrower:
constraint solving for ParamSpec, code-flow narrowing across
basic blocks, custom metaclass __call__, and full descriptor
protocol. None of those are needed for what's resolved on the
bench fixture or the stress suite.
2026-05-09 14:16:53 +02:00
Martin Vogel 97764d48b4 docs(py_lsp): final results table through Round 9
Updates BENCHMARK_PYTHON.md to capture the full trajectory:
- Bench: 81% (65 LOC) -> 79% (136 LOC) -> 95% (136 LOC) -> 98%
  (178 LOC) -> 100% (178 LOC, 52/52)
- 22 stress-test patterns probed; 19 hard-asserted, 3 documented gaps
- Documented gaps: function-as-dict-value, match sequence pattern,
  lambda parameter inference

The hard-asserted patterns include the full LSP surface a user would
expect: NamedTuple, TypedDict, Protocol, ABC, with-as, except-as,
post-early-return narrowing, tuple unpacking, dict.items
comprehension, slice typing, decorator factories, property setters,
Self chains, diamond MRO, recursive types, closures, generator
delegation, async-gen for, long fluent chains.
2026-05-09 12:49:02 +02:00
Martin Vogel 3ec56b7bff docs(benchmark): refresh PHP-LSP numbers post-Phase-4t/v 2026-05-09 11:45:45 +02:00
Martin Vogel 0be2ac338c docs(py_lsp): final results table + stopping-point doc
BENCHMARK_PYTHON.md ends the parity push with the in-process bench
results table (81% -> 79% -> 95% across rounds), an explicit
stopping-point list of features that would require building a
constraint solver / code-flow engine / plugin system, and an
"achievable next steps" list for future iterations that stay below
the compiler-rebuild line.

The 81% -> 79% jump going from a 65-line fixture to 136 lines is
deliberate: the smaller fixture was biased toward the resolver's
strengths. The 79% -> 95% jump comes from instance-attribute typing
(Round 4) which closed the largest single gap on real-world code
shapes.

Total: 11 LSP commits on this branch (Phases 0-11 plus Rounds 1-4),
2892 / 0 passing, 95% resolution on the parity fixture.
2026-05-09 03:39:05 +02:00
Martin Vogel ee380eb551 docs(benchmark): record Phase-4 PHP-LSP outcome on laravel/framework
Edge counts (laravel/framework):
  - Total edges:  196,979 (baseline) -> 152,614 (-22%)
  - CALLS edges: ~83,000 (baseline) -> 50,150 (-40%)

The drop is from suppressing name-fallback misroutes when the receiver
is statically typed to a vendor class that isn't indexed. The Phase-4
capabilities (generic templates, narrowing, property tracking, full
parent-walk method dispatch, expanded stdlib) cover most of phpactor's
type-resolution surface for in-project receivers.

100 unit tests, all passing. Total project tests: 2913 / 0 failed.
2026-05-09 02:55:05 +02:00
Martin Vogel a8c9ace4a5 feat(py_lsp): Phase 11 benchmark methodology + integration bench
Final phase of Python LSP integration. Adds:

- docs/BENCHMARK_PYTHON.md — methodology document covering target
  metrics (per-file LSP overhead < 10 ms, resolution ratio ≥ 40% on
  application code, ≥ 70% with stdlib, < 5% spot-check false positive
  rate), the declared accepted-failure-class ceiling (metaclass
  __call__, __getattr__/__getattribute__, dynamic setattr, runtime
  importlib, monkey-patching, eval/exec, descriptors beyond the
  standard property/classmethod/staticmethod, dynamic type() classes),
  and instructions for running scripts/benchmark-index.sh against the
  largest Python repos in datadice (falkemedia: 232 files, api-hub:
  188 files).

- tests/test_py_lsp_bench.c — in-process benchmark wired into
  scripts/test.sh. Runs cbm_extract_file on a 65-line Python fixture
  covering imports, dataclasses, classmethods, single-inheritance with
  super(), typed parameters, attribute chains, and stdlib calls.
  Asserts soft floor (resolved >= calls/4) and hard time budget
  (< 50 ms under sanitizers). Initial run reports:

    bench: 65 lines, 16 calls, 13 resolved (81%), 2.45 ms

  81% beats the 40% application-code target and the 70%
  application-plus-stdlib target. 2.45 ms under ASan + UBSan is well
  inside the 10 ms (no-sanitizer) production budget.

This wraps the 11-phase plan documented in PYTHON_LSP_PLAN.md. Total
test count: 2874 / 0 (100% pass). Repository-level benchmarks via
scripts/benchmark-index.sh remain a manual operation against a fresh
production binary; methodology is now in place for when that runs.
2026-05-09 00:47:00 +02:00
Martin Vogel b8926fa52b docs(benchmark): record PHP-LSP attribution-correctness outcome
PHP score moves from 9/12 (75%) to 10/12 (83%); the two remaining
PARTIALs (Q6 search recall, Q8 trace tool disambiguation) are non-LSP
issues tracked separately per PHP_LSP_PRE_FLIGHT.md §3 and §4.1.

Headline change is a graph-correctness win on laravel/framework: the
collide-set $prompt->value() misroute is gone, and unindexed-vendor
receiver calls no longer produce wrong name-fallback edges (total
edges drop ~14% from 196,979 to 169,268, trading recall for precision).
2026-05-08 23:33:26 +02:00
Martin Vogel b24f34aaa0 docs(php-lsp): plan PHP Light Semantic Pass and pre-flight findings
Pre-flight against laravel/framework establishes that the PHP graph has
systemic call-attribution drift on short names shared between a global
function and a method (e.g. value(), make(), get()). Spot-check shows
~20-30% of typed-receiver method calls misroute to the global helper
because the existing extractor cannot resolve receiver type.

Plan adds internal/cbm/lsp/php_lsp.{c,h} as a third sibling to go_lsp.c
and c_lsp.c. In-process C type resolver, no PHP runtime, no new
dependencies. Uses already-vendored tree-sitter-php grammar and the
existing CBMArena / CBMScope / CBMType / CBMTypeRegistry primitives.

Phase 1 success metric is collide-set attribution correctness, not
benchmark tier alone. Q6 (text search) and Q8 (trace tool
disambiguation) tracked as separate tickets.
2026-05-08 22:17:25 +02:00
Martin Vogel babbebc022 Move TEST_PLAN.md to docs/ 2026-03-26 15:14:57 +01:00
Martin Vogel f52376b982 Update: 2586 tests, 66 languages everywhere, tre vendored hash 2026-03-26 11:53:35 +01:00
Martin Vogel c38853f999 Remove Go-era cruft, update THIRD_PARTY.md for pure C project
- Delete cmd/codebase-memory-mcp/assets/ — skills and codex instructions
  are embedded as C string literals in src/cli/cli.c since v0.5.0
- Move BENCHMARK.md to docs/ to reduce root clutter
- Fix setup scripts: replace stale go build with scripts/build.sh
- THIRD_PARTY.md: replace Go module dependencies with vendored C
  libraries (sqlite3, mimalloc, mongoose, yyjson, xxhash, tre),
  fix CGo reference to static compilation
2026-03-22 01:09:12 +01:00
Martin Vogel f6b9b39c60 Fix snippet tests: align with simplified get_code_snippet handler
The snippet handler now uses exact QN + suffix matching only (no fuzzy/auto-resolve).
Updated 3 tests to match: fuzzy_suggestions, fuzzy_last_segment, auto_resolve_enabled.
2026-03-20 13:45:17 +01:00
Martin Vogel 78e1df2021 Refine messaging: seconds for avg repos, clarify AST (64 langs) vs LSP hybrid (Go/C/C++) 2026-03-19 23:44:35 +01:00