From 49efac728edfe4ac8e2d7575d79d526f88c9d70d Mon Sep 17 00:00:00 2001 From: HomericIntelligence Agent <4211002+mvillmow@users.noreply.github.com> Date: Sun, 10 May 2026 21:35:20 -0700 Subject: [PATCH 1/4] chore: forbid ::warning:: advisory pattern; pip-audit + benchmark fail-fast Ports the Bucket F regression guard from HomericIntelligence/Odysseus#282 and refactors 2 advisory-annotation site(s) to fail-fast: - .github/workflows/_required.yml:693 (pip-audit --strict) - .github/workflows/extras.yml:73 (make benchmark.native) Local pip-audit --strict --skip-editable: no findings. make benchmark.native exits non-zero only on real benchmark errors; non-blocking timing regressions are reported but do not gate. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/_required.yml | 40 ++++++++++++++++++++++++++++----- .github/workflows/extras.yml | 8 +------ .pre-commit-config.yaml | 19 ++++++++++++++++ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/.github/workflows/_required.yml b/.github/workflows/_required.yml index 4c7f50e..b4e8dc2 100644 --- a/.github/workflows/_required.yml +++ b/.github/workflows/_required.yml @@ -82,6 +82,35 @@ jobs: fi echo 'OK: no "continue-on-error: true" found' + - name: "Reject advisory annotation pattern" + run: | + set -euo pipefail + mapfile -t files < <(git ls-files -- '.github/workflows/*.yml' '.github/workflows/*.yaml') + # The advisory-annotation pattern in workflow run blocks is morally + # equivalent to continue-on-error: true: it lets a tool fail without + # failing the CI step. The runbook (Bucket F) requires every tool + # to be fail-fast. The only legitimate annotation is informational + # text not tied to a tool's exit code — those should use + # echo "WARN:" to stdout instead. See docs/runbooks/no-silent-failures.md. + declare -a scan_files=() + for f in "${files[@]}"; do + # Exempt this workflow file (heredoc would otherwise self-match). + case "$f" in + .github/workflows/_required.yml) continue ;; + esac + scan_files+=("$f") + done + if [ "${#scan_files[@]}" -eq 0 ]; then + echo "No files to scan" + exit 0 + fi + if grep -nF '::warning::' "${scan_files[@]}"; then + echo "" + echo '::error::Found advisory annotations above. Make the underlying tool fail-fast or use echo "WARN:" to stdout. See docs/runbooks/no-silent-failures.md Bucket F.' + exit 1 + fi + echo 'OK: no advisory annotations found' + # ============================================================ # 1. lint # clang-format, ruff, pre-commit, CMake cycle check, @@ -687,12 +716,11 @@ jobs: run: | set -euo pipefail pip install -e ".[dev]" --quiet - # pip-audit --strict exits non-zero on any finding. We want to surface - # the report as a warning (Trivy below is the gating scan) without - # silently masking the rc. - if ! pip-audit --strict; then - echo "::warning::pip-audit found Python dependency advisories — see step log above" - fi + # pip-audit --strict exits non-zero on any finding — fail-fast per + # docs/runbooks/no-silent-failures.md (Bucket F). If a finding is + # genuinely unfixable, add it to a `--ignore-vuln` allowlist with an + # inline comment naming the tracking issue and planned fix date. + pip-audit --strict - name: Run Trivy filesystem scan (SARIF) uses: aquasecurity/trivy-action@v0.36.0 diff --git a/.github/workflows/extras.yml b/.github/workflows/extras.yml index da6b588..db8cd2a 100644 --- a/.github/workflows/extras.yml +++ b/.github/workflows/extras.yml @@ -66,13 +66,7 @@ jobs: run: sccache --show-stats - name: Run benchmarks - run: | - set -euo pipefail - # Benchmarks can legitimately regress without blocking the workflow — - # report-only. Surface the rc as a warning instead of silently masking. - if ! make benchmark.native; then - echo "::warning::benchmark.native exited non-zero — see step log above" - fi + run: make benchmark.native - name: Upload benchmark results uses: actions/upload-artifact@v7 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f8d79b4..78791f6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -138,6 +138,25 @@ repos: files: ^\.github/workflows/.*\.ya?ml$ pass_filenames: true + - id: forbid-advisory-warnings + name: "forbid advisory ::warning:: pattern in workflow run blocks" + description: > + The advisory-warning workflow annotation is morally equivalent to + `continue-on-error: true` when it replaces a tool's failure exit + with a benign-looking warning. The CI step still passes, the + underlying problem still goes unfixed. Make the step fail-fast + instead. The only legitimate use is annotating a step that runs + BEFORE the failing tool (e.g., advising on a deprecated config), + not wrapping the tool's own exit. See docs/runbooks/no-silent-failures.md. + language: pygrep + # Match the GitHub Actions advisory-annotation prefix. We exempt + # this workflow file (the lint rule itself contains the literal + # for self-documentation) via the `exclude` regex below. + entry: '::warning::' + files: ^\.github/workflows/.*\.ya?ml$ + exclude: ^\.github/workflows/_required\.yml$ + pass_filenames: true + # ============================================================================ # Git Commit Message Linting (Optional) # ============================================================================ From d10c8eb7579977428c221c83baa3feee09e85165 Mon Sep 17 00:00:00 2001 From: Micah Villmow <4211002+mvillmow@users.noreply.github.com> Date: Sun, 10 May 2026 22:40:30 -0700 Subject: [PATCH 2/4] fix(ci): real-fix pip-audit and benchmark failures surfaced by fail-fast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing the ::warning:: suppressions in #550 surfaced two pre-existing issues that the advisory annotations had been hiding. ## pip-audit --strict CI failed with: ERROR:pip_audit._cli:projectkeystone: Dependency not found on PyPI and could not be audited: projectkeystone (0.1.0) No CVEs — the failure is that `pip install -e ".[dev]"` registers the local project in the venv, and pip-audit then tries to look it up on PyPI. Keystone is intentionally not published to PyPI. The third-party dependency tree itself is clean (matches the PR author's local `pip-audit --strict --skip-editable` run). Fix: add `--skip-editable` to the CI invocation with a comment explaining why. This is not an `--ignore-vuln` allowlist (no vulnerability is being suppressed); it's scoping pip-audit to the package set it can actually audit. ## make benchmark.native CI failed with: Error: Build directory not found make[1]: *** [Makefile:171: benchmark] Error 1 Real bug. `scripts/run_benchmarks.sh` defaulted `BUILD_DIR` to `$PROJECT_ROOT/build/release/bin`, but the Makefile actually produces binaries at `$(BUILD_DIR)/$(BUILD_SUBDIR)` -> `build/x86.release/`. The path the script looked at never existed; the prior `if ! make benchmark.native; then echo "::warning::..."; fi` had been swallowing this and benchmarks were never actually exercised in CI. Fix: point the script's default at `build/x86.release` and let callers override `BUILD_DIR` (full path) or `BUILD_SUBDIR` (suffix only). Follow-up tracked in HomericIntelligence/ProjectKeystone#551 (single source of truth between Makefile and script; per-binary missing detection; doc reconciliation). Refs: HomericIntelligence/ProjectKeystone#550, HomericIntelligence/ProjectKeystone#551 Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/_required.yml | 9 ++++++++- scripts/run_benchmarks.sh | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_required.yml b/.github/workflows/_required.yml index b4e8dc2..01cdf71 100644 --- a/.github/workflows/_required.yml +++ b/.github/workflows/_required.yml @@ -720,7 +720,14 @@ jobs: # docs/runbooks/no-silent-failures.md (Bucket F). If a finding is # genuinely unfixable, add it to a `--ignore-vuln` allowlist with an # inline comment naming the tracking issue and planned fix date. - pip-audit --strict + # + # --skip-editable: the project itself (projectkeystone) is installed + # in editable mode above so pip-audit can resolve its [dev] extras. + # It is intentionally not published to PyPI, so auditing it as a + # PyPI package errors with "Dependency not found on PyPI". We only + # care about auditing the *third-party* dependency tree, not the + # local project package. + pip-audit --strict --skip-editable - name: Run Trivy filesystem scan (SARIF) uses: aquasecurity/trivy-action@v0.36.0 diff --git a/scripts/run_benchmarks.sh b/scripts/run_benchmarks.sh index ee4f08d..0bd0ceb 100755 --- a/scripts/run_benchmarks.sh +++ b/scripts/run_benchmarks.sh @@ -26,7 +26,12 @@ NC='\033[0m' # No Color # Configuration PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" -BUILD_DIR="${BUILD_DIR:-$PROJECT_ROOT/build/release/bin}" +# BUILD_DIR must match the Makefile output path: $(BUILD_DIR)/$(BUILD_SUBDIR) +# which defaults to build/x86.release (BUILD_DIR=build, BUILD_SUBDIR=x86, +# CMAKE_BUILD_TYPE=Release -> suffix .release). Callers may override either +# BUILD_DIR (full path) or BUILD_SUBDIR (suffix only). +: "${BUILD_SUBDIR:=x86.release}" +BUILD_DIR="${BUILD_DIR:-$PROJECT_ROOT/build/$BUILD_SUBDIR}" BENCHMARK_DIR="${BENCHMARK_OUTPUT_DIR:-$PROJECT_ROOT/build/reports/benchmarks}" RESULTS_DIR="$BENCHMARK_DIR/results" mkdir -p "$RESULTS_DIR" From 3145bc95ab75a23fca6e29fa0810e177106cfd12 Mon Sep 17 00:00:00 2001 From: Micah Villmow <4211002+mvillmow@users.noreply.github.com> Date: Mon, 11 May 2026 06:46:55 -0700 Subject: [PATCH 3/4] fix(ci): audit pyproject directly; point bench script at bin/ output dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two pre-existing issues surfaced by removing the ::warning:: suppressions in #550 were only partially fixed by d10c8eb. Both checks were still red on run 25652460546 / 25652460547. Real root causes and fixes below. ## security/dependency-scan (Required) The previous fix tried `pip install -e ".[dev]" && pip-audit --strict --skip-editable`. In pip-audit 2.10.0 (newer than the pinned version), `--strict` treats a `--skip-editable` skip as a dependency-collection failure and errors out: ERROR:pip_audit._cli:projectkeystone: distribution marked as editable Process completed with exit code 1. `projectkeystone` is intentionally absent from PyPI — pyproject.toml exists only so Python dev/test tools (mypy, conan, pytest, ...) can be declared. There is no "install the project" requirement for an audit. Fix: drop the editable install entirely and pass the project path as a positional argument. `pip-audit --strict .` resolves pyproject.toml's production dependency closure (pydantic, nats-py, pydantic_core, annotated-types, typing_extensions, typing-inspection) directly from PyPI metadata without installing anything. Locally: $ pip-audit --strict . No known vulnerabilities found No `--ignore-vuln` allowlist needed; the closure is clean. No suppression patterns introduced — still fail-fast on findings. ## benchmarks (Extras / non-blocking) The previous fix pointed `BUILD_DIR` at `build/x86.release`, but CMakeLists.txt:88 sets `CMAKE_RUNTIME_OUTPUT_DIRECTORY` to `${CMAKE_BINARY_DIR}/bin`. CI log confirmed the linker produced `bin/message_pool_benchmarks`, `bin/hierarchy_benchmarks`, `bin/distributed_benchmarks` — but the script kept looking at `build/x86.release/hierarchy_benchmarks` (no bin/), so every binary was reported missing and `make benchmark` exited non-zero. Fix: introduce `BENCH_BIN_DIR` (default `$BUILD_DIR/bin`) and use it everywhere the script previously read `$BUILD_DIR/$bench`. Callers can still override `BUILD_DIR` (full path to the CMake binary dir), `BUILD_SUBDIR` (suffix only), or `BENCH_BIN_DIR` (full path to binaries) to point at non-standard layouts. Refs: HomericIntelligence/ProjectKeystone#550, HomericIntelligence/ProjectKeystone#551 Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/_required.yml | 20 ++++++++++++-------- scripts/run_benchmarks.sh | 18 +++++++++++------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.github/workflows/_required.yml b/.github/workflows/_required.yml index 01cdf71..426a6e2 100644 --- a/.github/workflows/_required.yml +++ b/.github/workflows/_required.yml @@ -715,19 +715,23 @@ jobs: - name: Python dependency audit (pip-audit) run: | set -euo pipefail - pip install -e ".[dev]" --quiet # pip-audit --strict exits non-zero on any finding — fail-fast per # docs/runbooks/no-silent-failures.md (Bucket F). If a finding is # genuinely unfixable, add it to a `--ignore-vuln` allowlist with an # inline comment naming the tracking issue and planned fix date. # - # --skip-editable: the project itself (projectkeystone) is installed - # in editable mode above so pip-audit can resolve its [dev] extras. - # It is intentionally not published to PyPI, so auditing it as a - # PyPI package errors with "Dependency not found on PyPI". We only - # care about auditing the *third-party* dependency tree, not the - # local project package. - pip-audit --strict --skip-editable + # Auditing the project path directly resolves pyproject.toml's + # production dependency closure without installing the project + # itself. This avoids the editable-distribution edge case in + # pip-audit 2.10+ where `--strict --skip-editable` errors with + # "distribution marked as editable" because projectkeystone is + # installed via `pip install -e` but is intentionally absent from + # PyPI (Keystone is a C++20 library; pyproject.toml exists only + # for dev/test tooling). + # + # Dev extras (mypy, conan, pytest, ...) are tools, not shipped + # code, and are intentionally out of scope here. + pip-audit --strict . - name: Run Trivy filesystem scan (SARIF) uses: aquasecurity/trivy-action@v0.36.0 diff --git a/scripts/run_benchmarks.sh b/scripts/run_benchmarks.sh index 0bd0ceb..0173826 100755 --- a/scripts/run_benchmarks.sh +++ b/scripts/run_benchmarks.sh @@ -26,12 +26,16 @@ NC='\033[0m' # No Color # Configuration PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" -# BUILD_DIR must match the Makefile output path: $(BUILD_DIR)/$(BUILD_SUBDIR) -# which defaults to build/x86.release (BUILD_DIR=build, BUILD_SUBDIR=x86, -# CMAKE_BUILD_TYPE=Release -> suffix .release). Callers may override either -# BUILD_DIR (full path) or BUILD_SUBDIR (suffix only). +# BUILD_DIR is the CMake build directory; binaries land in $BUILD_DIR/bin +# because CMakeLists.txt sets CMAKE_RUNTIME_OUTPUT_DIRECTORY to +# "${CMAKE_BINARY_DIR}/bin". The Makefile uses $(BUILD_DIR)/$(BUILD_SUBDIR) +# as its CMake binary dir, which defaults to build/x86.release +# (BUILD_DIR=build, BUILD_SUBDIR=x86, CMAKE_BUILD_TYPE=Release -> .release). +# Callers may override BUILD_DIR (full path to CMake binary dir), +# BUILD_SUBDIR (suffix only), or BENCH_BIN_DIR (full path to binaries). : "${BUILD_SUBDIR:=x86.release}" BUILD_DIR="${BUILD_DIR:-$PROJECT_ROOT/build/$BUILD_SUBDIR}" +BENCH_BIN_DIR="${BENCH_BIN_DIR:-$BUILD_DIR/bin}" BENCHMARK_DIR="${BENCHMARK_OUTPUT_DIR:-$PROJECT_ROOT/build/reports/benchmarks}" RESULTS_DIR="$BENCHMARK_DIR/results" mkdir -p "$RESULTS_DIR" @@ -94,7 +98,7 @@ BENCHMARKS=( # Check that benchmarks exist missing=0 for bench in "${BENCHMARKS[@]}"; do - if [[ ! -f "$BUILD_DIR/$bench" ]]; then + if [[ ! -f "$BENCH_BIN_DIR/$bench" ]]; then echo -e "${YELLOW}Warning: $bench not found, skipping${NC}" missing=$((missing + 1)) fi @@ -115,14 +119,14 @@ echo "" # Run each benchmark suite for bench in "${BENCHMARKS[@]}"; do - if [[ ! -f "$BUILD_DIR/$bench" ]]; then + if [[ ! -f "$BENCH_BIN_DIR/$bench" ]]; then continue fi echo -e "${BLUE}Running $bench...${NC}" # Build benchmark command - BENCH_CMD="$BUILD_DIR/$bench" + BENCH_CMD="$BENCH_BIN_DIR/$bench" BENCH_ARGS="--benchmark_out_format=json --benchmark_out=$RESULTS_DIR/${bench}_$TIMESTAMP.json" # Add filter if specified From 8a3cda6079fcf422f66eb5c17647c34590ea7388 Mon Sep 17 00:00:00 2001 From: Micah Villmow <4211002+mvillmow@users.noreply.github.com> Date: Mon, 11 May 2026 06:52:55 -0700 Subject: [PATCH 4/4] fix(bench): export RESULTS_DIR/TIMESTAMP so embedded python heredoc sees them After fixing the benchmark binary lookup path in 3145bc9, CI got further: all three benchmark executables (hierarchy, message_pool, distributed) ran to completion and wrote their per-suite JSON files. The post-run "Merging results..." heredoc then failed with: No result files found matching None/*_None.json make[1]: *** [Makefile:171: benchmark] Error 1 `python3 << EOF` runs as a subprocess of bash and only inherits *exported* environment variables. `RESULTS_DIR`, `TIMESTAMP`, `RESULTS_FILE`, and `COMPARE_BASELINE` were plain shell locals, so `os.environ.get('RESULTS_DIR')` returned None on both the merge heredoc and the regression-compare heredoc. Fix: export the four shell variables the heredocs reference. Refs: HomericIntelligence/ProjectKeystone#550 Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/run_benchmarks.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/run_benchmarks.sh b/scripts/run_benchmarks.sh index 0173826..c51664c 100755 --- a/scripts/run_benchmarks.sh +++ b/scripts/run_benchmarks.sh @@ -110,9 +110,17 @@ if [[ $missing -eq ${#BENCHMARKS[@]} ]]; then exit 1 fi -# Timestamp for results +# Timestamp for results. +# Exported so the embedded `python3 << EOF` heredocs below can read them via +# os.environ.get(). Heredocs run as a subprocess of bash and only inherit +# *exported* variables; without `export` the python sees None and emits +# "No result files found matching None/*_None.json". +export TIMESTAMP TIMESTAMP=$(date +%Y%m%d_%H%M%S) +export RESULTS_DIR +export RESULTS_FILE RESULTS_FILE="$RESULTS_DIR/results_$TIMESTAMP.json" +export COMPARE_BASELINE echo -e "${YELLOW}Running benchmarks...${NC}" echo ""