Archives >3MB may not be unpacked by VirusTotal, meaning only the
container gets scanned (meaningless). Now extracts the actual
executables from tar.gz/zip before uploading to VirusTotal.
Users still download archives (preserves Unix permissions).
Also fixes analysis ID parsing (base64 format, not SHA256).
Also fixes SBOM format (SPDX 2.3 instead of CycloneDX).
- Wait until at least 60 engines have actually scanned (not just
reported type-unsupported/failure/timeout)
- 20 minute timeout per binary (was 5 minutes)
- If scan doesn't complete in time → FAIL (incomplete scan is not clean)
- Engines that can't scan (type-unsupported, failure, timeout) are
excluded from the completion count
freshclam requires DatabaseMirror set in config. Without it, ClamAV
scans with empty signatures (catches nothing). Now properly configures
freshclam.conf on both Linux (apt) and macOS (Homebrew) before updating.
Every platform now has a real antivirus gate that fails the build:
- Windows: Windows Defender with ML heuristics (MpCmdRun.exe)
- Linux: ClamAV with freshclam signature update
- macOS: ClamAV with freshclam signature update
Any detection on any platform blocks the release-draft from being
created. This is on top of the VirusTotal zero-tolerance gate in
the verify job.
SECURITY.md updated with complete security architecture.
- Output is comma-separated, not newline — use tr ',' '\n' to split
- URLs are /gui/file/<sha256> permalinks — extract SHA256 hash and
query /api/v3/files/<sha256> for last_analysis_stats
- Release notes table builder uses same comma-split fix
- Gate uses /tmp flag file to propagate failure from subshell
The verify job polls VirusTotal API for completed scan results.
If ANY engine flags ANY binary as malicious or suspicious, the
release stays as draft and the pipeline fails. Zero tolerance —
investigate and fix before retrying.
- VirusTotal: scans all release binaries (tar.gz + zip) via
crazy-max/ghaction-virustotal after publishing. Scan result
links are appended to the release notes automatically.
- CHANGELOG.md: local copy of all release notes so history
is preserved in the repo, not just on GitHub.
The cp command fails when SUFFIX is empty because source and destination
are the same file. Only copy when SUFFIX is non-empty (ui variant).
Also add fail-fast: false to smoke-windows so both variants always run.
The UI zip contains codebase-memory-mcp-ui.exe but the smoke test
always runs ./codebase-memory-mcp.exe. Copy the extracted binary
to the expected name so both standard and ui variants work.
1. smoke-windows: Use CLANG64 (not UCRT64) so runtime matches the
build environment. Binary built with CLANG64 needs CLANG64 DLLs.
2. smoke-unix: Add fail-fast: false so one platform failure doesn't
cancel all 7 other smoke jobs.
3. smoke-test.sh: Capture and display stderr on indexing failure to
help debug smoke-unix errors.
4. release.yml: Same CLANG64 smoke fix.
1. Add zip to MSYS2 build packages (both dry-run and release).
2. embed-frontend.sh: replace xxd with od+sed for hex dump — xxd
requires vim which isn't in MSYS2 CLANG64 by default.
3. embed-frontend.sh: flip IS_MACOS→IS_LINUX — Linux gets ld -r -b
binary (ELF-only), everything else (macOS, Windows) gets the
portable C-array approach via od.
4. release.yml: same Windows archive fix (msys2 shell + .exe detect).
1. dry-run.yml: Add skip_tests and skip_builds workflow_dispatch inputs
for faster iteration. Build jobs use always() to run even when tests
are skipped.
2. Windows archive: Switch from PowerShell Copy-Item to MSYS2 shell
with .exe extension detection. MSYS2 Clang produces .exe suffix
that Copy-Item didn't account for.
3. Linux embed: Use absolute path for ld output when cd'd into dist
directory. Relative path build/c/embedded/*.o was invalid from
inside graph-ui/dist/.
4. release.yml: Same Windows archive fix as dry-run.
- Use env: block instead of shell quoting for WIN32_LIBS in CI
- Forward WIN32_LIBS from environment in test.sh and build.sh
- Add skip_lint input to dry-run workflow for faster iteration
- Replace mingw-w64 with llvm-mingw Docker image (mstorsjo/llvm-mingw)
for local cross-compilation — includes Wine for future test execution
- Fix WIN32_LIBS quoting: use single quotes so -Wl,--allow-multiple-
definition actually reaches the linker
- Install MSYS2 zlib into llvm-mingw sysroot for cross-compilation
MinGW GCC does not ship ASan runtime on Windows. MSYS2 CLANG64
provides full sanitizer support via compiler-rt. All platforms
now run tests with ASan + UBSan — no exceptions.
- test-windows job runs scripts/test.sh under MSYS2 (same as build)
- Remove libsqlite3-dev from all CI steps (sqlite3 is vendored now)
- Both dry-run.yml and release.yml updated
- Add scripts/clean.sh, test.sh, build.sh, lint.sh as single source
of truth for local and CI builds (always clean, always parallel)
- Fix -Wmissing-braces: replace {{0}} with memset for struct arrays
- Fix -Wrestrict: use intermediate buffer for same-struct snprintf
- Update dry-run.yml and release.yml to use the new scripts