#581 (query-path memory growth) does not reproduce in our soak: a 10-min,
200-file query-only run shows RSS flat-to-shrinking and committed plateauing
(mimalloc already purges on free, purge_delay=0). Rather than ship speculative
mitigations for a leak we cannot measure, this adds the observability to find it
where it actually occurs, plus a continuous guard.
- diag: the diagnostics writer now appends a PERSISTENT NDJSON memory trajectory
(one sample / 5s: rss, committed, peak fields, page_faults, fd, queries), kept
on exit (rotates to a .1 generation past 8 MB) so users can send the time
series post-mortem. The previous latest-snapshot file was overwritten every 5s
and deleted on stop.
- ci: the soak now also runs a read-only query-leak leg (CBM_SOAK_MODE query-leak,
no reindex/mutate) on every platform including Windows, so a regression that
introduces a query-path leak is caught. soak-test.sh RESULTS_DIR is now
env-overridable so both legs keep separate artifacts.
- docs: README Troubleshooting and Diagnostics section + a bug-report field
explain the no-telemetry stance and how to capture and share the trajectory.
Build clean; unit suite 5714/0; trajectory verified to persist post-exit.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The ubuntu-24.04-arm test leg (native ARM64 Linux running the full suite under
AddressSanitizer + the FastAPI incremental index) exceeds the 60-minute job
timeout and was killed, failing ci-ok even though the suite was on track to pass.
ARM + ASan is legitimately 2-3x slower than x86-64.
Raise the substantial compute jobs (test, build, bug-repro board, soak's short
legs, security/codeql, cross-platform smoke, fast-repro) to a generous 240-minute
(4h) cap so a slow-but-correct runner can't false-timeout. Trivial aggregator
jobs (5/10/15m: setup-matrix, ci-ok, dco, lint, license-gate, quick smokes) and
the intentional soak caps (300/320m) are unchanged.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The native CLANGARM64 toolchain compiles the project fine, but linking the test
build fails: LLVM ships no AddressSanitizer runtime for aarch64-w64-windows-gnu
(libclang_rt.asan_dynamic.dll.a not found). ASan also cannot intercept under
x86-64 emulation, so Windows ARM64 has no working ASan by any toolchain.
Run the windows-11-arm leg with SANITIZE= (no sanitizer) so it builds and runs
the full suite natively as a real, required functional gate. ASan/UBSan coverage
is provided by the other nine legs, including native-ARM Linux/macOS; x86-64
Windows keeps full sanitizers.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
windows-11-arm is an ARM64 runner but the test matrix built it with the x86-64
msys2 environment (CLANG64), so the binary ran under Windows-on-ARM x86-64
emulation. AddressSanitizer's function interception cannot patch the emulated
system DLLs there (interception_win: unhandled instruction -> asan_malloc_win.cpp
CHECK failed), crashing in ASan init before any test ran. That was being hidden
behind optional:true.
Pin each Windows leg's msys2 environment + package arch to the RUNNER
architecture: x86-64 runners use CLANG64 (mingw-w64-clang-x86_64-*), the ARM64
runner uses CLANGARM64 (mingw-w64-clang-aarch64-*). The windows-11-arm binary is
now native ARM64, so ASan instruments native code and the leg is a real,
non-optional gate (matching smoke.yml, which already uses CLANGARM64 for it).
Every other matrix leg already used a toolchain matching its runner arch.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- repro_main: RUN_SUITE honors CBM_REPRO_ONLY (comma list of suite-name
substrings) for fast targeted validation of a single fix.
- fast-repro.yml: ASan single-platform (the no-sanitizer build crashed some
suites); single-platform is the speedup vs the 5-platform board.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- repro_main.c: redefine RUN_SUITE to print '[SUITE] <name> P passed, F failed'
so board/fast-lane output is greppable for which suites still have reds.
- bug-repro.yml: exclude qa/fast-** / qa/soak-** / qa/smoke-** from the board
push trigger (those branches run only their dedicated lane workflow).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Avoids waiting ~15min for the full 5-platform ASan board just to see whether a
fix dropped the red count. Pushing a qa/fast-** branch builds+runs test-repro on
ubuntu-latest without sanitizers (~5min). The full bug-repro.yml board stays the
comprehensive all-platform check.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
dry-run.yml and release.yml now pass broad_platforms:true to _test.yml + _smoke.yml,
which add (via a dynamic setup-matrix job) ubuntu-22.04 (older glibc / AlmaLinux
class), ubuntu-22.04-arm, macos-15, windows-2025, windows-11-arm on top of the core
set — a broader 'does it run everywhere' picture. The PR gate (pr.yml) and the
shipped release-binary targets (_build.yml) are unchanged. Broad-only legs are
tagged optional + continue-on-error, so a flaky/less-common runner is visible but
never blocks a release.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- scripts/smoke-invariants.sh: 30-check battery against the PROD binary —
--version/--help, MCP initialize handshake with stdin OPEN (#513), tools/list
(all 14), EVERY tool invocable with valid JSON-RPC + no crash, index→non-empty
graph, malformed-input resilience (bad JSON / empty / huge line / binary /
non-UTF8 / missing path), clean EOF exit, shared-lib resolution, install
dry-run. Bounded waits (read -t / timeout), no sleep loops; msys2-safe.
- .github/workflows/smoke.yml: runs it on the WIDEST runner matrix — ubuntu
22.04+24.04 (x64+arm64; 22.04 = older glibc / AlmaLinux class), macos
14/15/15-intel, windows 2022/2025 + windows-11-arm (experimental). A FAIL on
any platform is a binary a user would receive. workflow_dispatch + qa/smoke-**.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
#581 explicitly crashes Windows (50+ GB virtual memory → crash), so Windows is
the most important soak target — the earlier 2-platform cap (ubuntu+macos) missed
exactly where the bug manifests. Expand to the full matrix: linux amd64+arm64,
darwin arm64+amd64, and a windows-latest msys2 job (mirrors _soak.yml's windows
build + .exe binary-path detection). All legs run the query-leak mode, 320-min
budget.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
timeout-minutes is evaluated at workflow setup, where the inputs context is null
on push events; fromJSON(inputs.duration_minutes || '240') + 60 was a startup
failure (0 jobs), so the soak never ran on qa/soak-** either. Use a fixed 320-min
budget (covers the 240-min default soak + build + analysis).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- _soak.yml: timeout-minutes was 30 (soak-quick) / 45 (asan) while nightly passes
duration_minutes=240 — every 'nightly 4h soak' was silently KILLED at 30 min and
never ran multi-hour. Raise to 300/60 so the soak can actually complete.
- soak-test.sh: add CBM_SOAK_MODE=query-leak (default unchanged). It indexes once
then hammers read-only tools (search_graph/query_graph/trace_path/
get_code_snippet/search_code) with NO reindex/mutation — so index_repository's
cbm_mem_collect never runs to sweep the query-only leak #581 implicates. The
existing RSS ceiling/slope/ratio checks become the #581 detector.
- soak.yml: workflow_dispatch (duration_minutes, mode) + push to qa/soak-** ;
builds the prod binary and runs the soak with timeout = duration + 60, on
ubuntu + macos. Pushing a qa/soak-* branch starts a real multi-hour run.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Stand up a separate, non-gating reproduce-first suite that holds one RED case
per open bug issue (the redness is the deliverable + the regression guard):
- tests/repro/ + repro_main.c -> `make test-repro` (its own runner with its own
main + counters; deliberately EXCLUDED from ALL_TEST_SRCS so the gating
`make test` / ci-ok required check stays green and PRs are not wedged)
- repro_extraction.c: first reproduction, #554 -- a C++ out-of-line method's
inner CALLS edge must attribute to the class-qualified Method QN, not the
Module; ties the call's enclosing_func_qn to the method definition's own
qualified_name so a class-qualifier drop (the live root cause) fails it
- scripts/repro.sh: build+run the board; a build/link failure fails the job,
while expected test redness is reported as the board state (job stays green)
- .github/workflows/bug-repro.yml: workflow_dispatch (platform filter) + qa/**
push; runs the board on linux x2 / macos x2 / windows so many bug vectors can
be reproduced on many platforms at once
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Three native GitHub Actions to automate the mechanical parts of issue
triage:
- stale.yml: actions/stale scoped to the awaiting-reporter label —
warns at 21 days idle, closes at 35, auto-resets when the reporter
replies. Never touches PRs or unlabeled issues.
- label-actions.yml + .github/label-actions.yml: posts a templated
comment when duplicate or awaiting-reporter is applied (duplicates are
linked, not auto-closed).
- issue-labeler.yml + .github/issue-labeler.yml: adds area labels
(windows, stability/performance, parsing/quality, editor/integration,
ux/behavior, cypher, language-request) from title/body keywords.
Additive only; base bug/enhancement labels still come from the forms.
All third-party actions pinned to full commit SHAs with least-privilege
per-job permissions (issues: write only where required).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
- graph-ui: bump vite ^6.4.2 -> ^6.4.3 and add overrides pinning the transitive
form-data >=4.0.6 and @babel/core >=7.29.6 (all dev-scope build/test deps, not
shipped in the binary). Clears the 4 open Dependabot alerts and Scorecard's
VulnerabilitiesID. `npm audit` now reports 0 vulnerabilities.
- codeql.yml: move `security-events: write` from the workflow top level to the
`analyze` job (top level is now `contents: read`), resolving Scorecard's
TokenPermissionsID least-privilege finding. CodeQL still uploads results from the
job-scoped token.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Homebrew only discovers formulae under Formula/ at the tap root. Add a
symlink to the pkg/homebrew/ location so `brew install` works.
Signed-off-by: Michael Hackner <mhackner@gmail.com>
A new push to a PR (or ref) now cancels the in-progress validation for
the previous commit instead of letting obsolete pipelines run to
completion. Drafts deliberately keep the full gate set — multi-platform
feedback matters most during iteration.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
PRs now run security gates, lint, and the full test suite (perf
assertions excluded — they stay in dry runs and releases where a
timing-flaky red cannot block a merge). Builds, smoke and soak remain
maintainer-driven.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
Pull requests run the security island only — static audit, license
gates, and the CodeQL gate plus the ci-ok summary; the full dry-run
chain (lint/test/build/smoke) stays maintainer-driven via
workflow_dispatch. The CodeQL gate now resolves the PR head SHA instead
of the synthetic merge commit so it can find the analysis run.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
Pull requests now execute the dry-run chain (security including the
license gates, lint, full test suite, all build legs, smoke) plus a
single ci-ok summary job that fails unless every stage succeeded.
Branch protection requires dco + ci-ok, so nothing unverified can merge
— including from admins. CodeQL also runs on pull requests so the
security island completes on PR commits.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
Every commit must now carry a Signed-off-by trailer matching its
author, certifying the right to submit the change under the project's
MIT license (DCO 1.1, the Linux kernel mechanism). Enforcement is
strict at three layers: a commit-msg hook rejects unsigned commits
locally (scripts/install-git-hooks.sh), and the new DCO workflow
rejects every push and pull request containing one. Merge commits and
bot authors are exempt, matching standard DCO checks.
Signed-off-by: Martin Vogel <martin.vogel@datadice.io>
The license gate now proves itself before running: a --selftest mode
plants an unlicensed vendored file and asserts the structural layer
detects it, so a silently broken gate can no longer pass. The
byte-identity provenance audit gains exit-code semantics (any verdict
outside the accepted set fails) and runs as a blocking step in the
security workflow for both dry runs and releases.
The server.json sync step read its version from pkg/npm/package.json,
which can lag behind the release being published in the dispatched
commit. Use the workflow's version input instead — it is authoritative
for the run.
With soak_level=none the soak job is skipped; release-draft already
handles that with an explicit condition, but verify and the publish
jobs relied on the default success() check, which treats a skipped
ancestor as non-success and silently skips the whole publish chain.
Give each downstream job an explicit condition that only blocks on
real failures or cancellations.
Release archives now carry THIRD_PARTY_NOTICES.md, generated by
scripts/gen-third-party-notices.sh from THIRD_PARTY.md, the grammar
manifest, and the per-component license texts; the Homebrew formula
and AUR PKGBUILD install it alongside the binary. The SBOM gains
per-component license metadata, corrected versions, and the previously
missing vendored libraries. The security workflow gains a
vendored-license scan with an explicit allow-list policy, and the
release workflow exposes a skip_perf input for releases that do not
touch pipeline logic.
The release tag must point at the commit the artifacts were built from (github.sha). The step tagged whatever the job checkout resolved to - the branch head at job START - so a commit pushed to main mid-run moved the tag target; when that head commit happened to touch .github/workflows/, the GitHub App token's tag push was rejected outright (refusing to create refs at workflow-modifying commits), failing the 0.8.0 draft.
The 0.8.0 release failed at publish-registries because pkg/npm/package.json still carried the previous hand-pinned version and npm refuses to publish over an existing release. The job now rewrites pkg/npm/package.json and pkg/pypi/pyproject.toml from the workflow's version input (with grep verification that the injection took) before publishing, so a forgotten manual bump can never fail the pipeline again. server.json needs no injection - publish-mcp-registry already syncs it from the published npm package.
packaging/glama -> pkg/glama so all packaging lives under pkg/. Update the
_smoke.yml job command and the Dockerfile/comment references to the new path.
verify.sh is path-relative (dirname $0), so its logic is unchanged.
The standard linux release binary dynamically links glibc 2.38+ and
GLIBCXX_3.4.32, so it fails to start on Debian 11, Ubuntu 20.04/22.04,
RHEL/Rocky 8/9, Amazon Linux 2, etc. — yet install.sh, the npm and PyPI
wrappers, and the binary's own self-update all fetched it by default,
contradicting the "single static binary" promise.
Point every linux install + self-update path at the fully-static
"-portable" asset (gcc -static), which has no glibc floor. macOS/Windows
are unaffected and unchanged.
- install.sh, pkg/npm/install.js, pkg/pypi _cli.py: select -portable on linux
- src/cli/cli.c: self-update download URL AND checksum archive name both use
-portable on linux (they must match or the update fails checksum verify)
- scripts/smoke-test.sh: assert linux self-update targets the -portable asset
- scripts/ci/check-glibc-compat.sh: new guard — runs the binary inside
debian:bullseye (glibc 2.31) and asserts it starts
- _smoke.yml: run the guard on the portable binary in smoke-linux-portable
Reproduced: standard binary -> "GLIBC_2.38 not found" on glibc 2.31;
portable binary runs cleanly.
- glama.json claims maintainership on glama.ai.
- packaging/glama/Dockerfile wraps the static Linux binary in a minimal
image so Glama can launch the stdio server and run its MCP introspection
checks (which power the directory score badge). The tool needs no Docker
to run; this image is only for the directory integration. Fetches the
latest release binary, arch-aware via TARGETARCH.
- packaging/glama/verify.sh builds the image and asserts the server answers
initialize + tools/list with no project indexed.
- _smoke.yml runs verify.sh as a non-gating job (continue-on-error) so the
integration is guarded against drift without ever blocking a release.
- Add `mcpName` to the npm package and an `mcp-name:` marker to the PyPI
README so the registry can verify package ownership for both.
- Rewrite server.json to reference the npm + PyPI packages instead of
five per-platform mcpb tarballs. The old form was pinned to a stale
0.6.1 with hardcoded SHA-256s that needed manual updates every release;
the package form only needs a version sync. Also trims the description
to the registry's 100-character limit.
- Add a publish-mcp-registry job: downloads mcp-publisher, authenticates
via GitHub Actions OIDC (no token, no device flow), syncs server.json's
version to the just-published npm package, and publishes. It runs after
npm/PyPI but does not gate the release un-draft, so a registry outage
never blocks shipping and the job can be retried on its own.
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>
The full suite runs the perf/bench/stress/scale suites, which push the slower ubuntu-24.04-arm runner right up against the 30-minute limit (flaky timeout cancellations marked as 'cancelled'). Give the slow legs headroom so the suite completes deterministically.
Add scripts/check-no-test-skips.sh (run from lint) which fails the lint phase on any plain SKIP() or direct tf_skip_count manipulation; only SKIP_PLATFORM() (for genuinely platform-specific tests) is tolerated. Add FAIL() and SKIP_PLATFORM() helpers to the test framework and convert the remaining SKIP()/perf-gated skips across the suite into pass-or-fail assertions, so a suite that cannot meet its preconditions reports a red failure instead of a silent skip.
The macos-15-intel leg had continue-on-error, so when that runner was unavailable the darwin-amd64 artifact silently never built and releases shipped with no Intel macOS binary. Remove continue-on-error so the leg is blocking - the binary ships or the build fails loudly. macos-15-intel is GitHub's supported Intel image through Aug 2027.
GitHub is sunsetting Intel-macOS runners, so the macos-15-intel build leg is
repeatedly cancelled before it can be allocated, turning the aggregate build
result non-success and skipping the smoke + soak jobs even though every other
build/test passed.
Mark only the macos-15-intel matrix leg continue-on-error, and relax the
smoke/soak gate to run unless builds were skipped or a leg genuinely failed
(tolerating a cancelled aggregate caused solely by the non-blocking Intel leg).
All other platforms remain required.
#394 group triaged: #266/#274/#331/#347/#348 verified fixed on v0.7.0 in
Windows CI; #227/#367 (SMB) pending reporter retest. Removing the throwaway
workflow.
Indexes a repo at a C:\ drive-letter path (subdirs + .cbmignore +
.mjs/.js) on windows-latest and dumps diagnostics for #347/#227/#367/
#331/#266/#274/#348. Temporary — delete after the run.
The #336 (Icinga2/Linux) and #385 (KiCad/Windows) reproductions are done —
both index cleanly on v0.7.0 on their reported platforms. Removing the
throwaway workflow.
The reused _build.yml builds all platforms; a cancelled macos-15-intel
runner skipped the icinga2/kicad index jobs. Build inline in each job so
the Linux (#336) and Windows (#385) repros don't depend on unrelated
platforms. Still temporary — delete after the run.
Temporary workflow_dispatch harness: builds current-main binaries and
indexes Icinga2 on Linux (fast) and KiCad on Windows (moderate) to check
whether the reported crashes still reproduce on v0.7.0. To be removed
after the run.