Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 124 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ name: CI

# Guardrails as automated gates (CLAUDE.md "The guardrails as automated gates"):
# - release job: configure (portable default, no RIPWIRE_NATIVE — CI hardware is not "this dev machine")
# + build + the full gate suite (170+ gates incl. determinism/G4/quality) per
# OS x build-flavour (Release / plain — see the job comment), run in parallel via
# + build + the full gate suite (170+ gates incl. determinism/G4/quality) on SIX legs:
# build-flavour (Release / plain) x front end (macOS AppleClang, Linux gcc, Linux
# clang) — see the job comment for why each axis exists — run in parallel via
# test/pargates.py -j 3 (same authoritative list as regression.sh).
# ubuntu-24.04 here is also the FIRST real proof of L1's portability fix: real x86-64
# Linux hardware, not the local RIPWIRE_PRETEND_LINUX proxy test/portablebuildcheck.sh
# uses on the (Apple Silicon) dev machines this repo is normally built on.
# - rhel job: the platform this project ships a Linux prebuilt for, checked at PR time instead of only
# at release. RHEL 9 userland (ubi9) via `container:` on a hosted Ubuntu runner, built with
# Red Hat's own gcc-toolset (RHEL 9's default gcc 11 cannot do C++23). Build + fixture
# self-run + the two headline guarantees, NOT the full suite — see the job comment for why.
# - asan job: configure -DRIPWIRE_ASAN=ON (G1: address,undefined,integer,float-divide-by-zero,
# float-cast-overflow, fail-fast) + build + the fixture self-run + the 5 heaviest verbs
# (--pack-task/--from-trace/--quality-delta/--merge-scout/--edit-check)
Expand Down Expand Up @@ -114,12 +119,29 @@ jobs:
# already-gated degrade path + --version's build-type token) and SKIP honestly on the
# Release leg, pointing at the plain leg as where the alert is proven; a missing alert
# on the plain flavour still FAILS. Neither flavour alone is enough.
name: release (${{ matrix.os }}, ${{ matrix.flavor }})
#
# And BOTH FRONT ENDS on Linux, not just the platform default. gcc is the one this job used to have,
# and it is a feature — a second front end over the same tree, which found three real portability bugs
# on its first run. But it is not the whole story either: RIPWIRE_OPT_REMARKS and RIPWIRE_PGO are
# Clang-only by construction (-Rpass=/-fsave-optimization-record, .profdata/llvm-profdata) and
# CMakeLists FATAL_ERRORs on anything else, so on a gcc-only Linux leg optremarkscheck's configure arms
# cannot be expressed and can only skip. macOS covers Clang but not Linux's libc, headers or linker.
# Six legs = {macos-14 AppleClang, ubuntu gcc, ubuntu clang} x {Release, plain}: every guardrail is
# observed by at least one leg on the OS it matters for, and no leg has to assert the impossible.
name: release (${{ matrix.os }}, ${{ matrix.flavor }}, ${{ matrix.cc }})
strategy:
fail-fast: false
# Explicit include list rather than three crossed axes: there is no gcc on the macOS runners (`g++`
# there is an AppleClang shim, so a `cc: gcc` macOS leg would silently be a second AppleClang leg
# claiming to be gcc), and a leg that lies about its toolchain is worse than a leg that is missing.
matrix:
os: [macos-14, ubuntu-24.04]
flavor: [Release, plain]
include:
- { os: macos-14, flavor: Release, cc: appleclang }
- { os: macos-14, flavor: plain, cc: appleclang }
- { os: ubuntu-24.04, flavor: Release, cc: gcc }
- { os: ubuntu-24.04, flavor: plain, cc: gcc }
- { os: ubuntu-24.04, flavor: Release, cc: clang }
- { os: ubuntu-24.04, flavor: plain, cc: clang }
Comment on lines +139 to +144

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

: "${MATRIX_CC:?set MATRIX_CC to gcc, clang, or appleclang}"
compiler_file="$(find build/CMakeFiles -type f -name CMakeCXXCompiler.cmake -print -quit)"
actual_id="$(awk -F'"' '/CMAKE_CXX_COMPILER_ID/ { print $2; exit }' "$compiler_file")"

case "$MATRIX_CC" in
  gcc) expected_id=GNU ;;
  clang) expected_id=Clang ;;
  appleclang) expected_id=AppleClang ;;
  *) exit 2 ;;
esac

test "$actual_id" = "$expected_id"

Repository: redhat-et/ripwire

Length of output: 223


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow file exists:"
test -f .github/workflows/ci.yml && echo yes || echo no

echo
echo "Relevant workflow lines 100-190:"
sed -n '100,190p' .github/workflows/ci.yml | nl -ba -v100

echo
echo "Search for MATRIX_CC, CC, CXX, CMAKE_CXX_COMPILER_ID:"
rg -n "MATRIX_CC|matrix\.cc|CC=|CXX=|CMAKE_CXX_COMPILER_ID|CMakeCXXCompiler|cmake --find" .github/workflows/ci.yml

Repository: redhat-et/ripwire

Length of output: 255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow file exists:"
test -f .github/workflows/ci.yml && echo yes || echo no

echo
echo "Relevant workflow lines 100-190:"
awk 'NR>=100 && NR<=190 { printf "%6d  %s\n", NR, $0 }' .github/workflows/ci.yml

echo
echo "Search for MATRIX_CC, cc, CC, CXX, compiler ID:"
rg -n "MATRIX_CC|matrix\.cc|CC=|CXX=|CMAKE_CXX_COMPILER_ID|CMakeCXXCompiler|cmake --find|cmake -S|cmake -B" .github/workflows/ci.yml

Repository: redhat-et/ripwire

Length of output: 7584


Validate the configured compiler against matrix.cc.

CC/CXX are only set for the clang matrix value. The GCC and AppleClang rows use the runner default, and the confirmation step only prints the cached CMAKE_CXX_COMPILER_ID and version without comparing it to the expected matrix compiler. A changed default compiler can make a matrix.cc row pass. Make the configure step fail unless the CMake compiler ID matches the row: gcc → GNU, clang → Clang, appleclang → AppleClang.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 135 - 140, Update the compiler
confirmation/configuration flow in the CI matrix so it validates
CMAKE_CXX_COMPILER_ID against matrix.cc, mapping gcc to GNU, clang to Clang, and
appleclang to AppleClang. Make the configure step fail on any mismatch,
including rows using the runner default compiler, rather than only printing the
cached compiler ID and version.

runs-on: ${{ matrix.os }}
steps:
# L4 (Linux probe): checkout@v4 defaults to a --depth 1 clone, which leaves ONE commit of history
Expand All @@ -130,20 +152,35 @@ jobs:
with:
fetch-depth: 0 # churn/co-change gates read real git history — a shallow clone reddens them

# clang is installed on BOTH Linux legs, not only the clang one. optremarkscheck's Clang-only
# configure arms pin `clang++` when the default front end is not Clang, so the gcc leg keeps that
# coverage instead of skipping it — the same remedy the asan job applies for G1's Clang-only
# sanitizer members. It is a package install, not a change of the leg's own compiler: the gcc leg
# still BUILDS ripwire with gcc, which is the point of having it.
- name: Install tooling (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends libxml2-utils ripgrep bc
sudo apt-get install -y --no-install-recommends libxml2-utils ripgrep bc clang

- name: Install tooling (macOS)
if: runner.os == 'macOS'
run: brew install ripgrep


# CC/CXX are set for the clang leg ONLY. Leaving them unset elsewhere is deliberate: the gcc leg and
# the macOS leg must each get their platform's own default, which is what a stranger's clone gets.
- name: Configure (portable — no RIPWIRE_NATIVE; empty build type = the plain flavour)
env:
CC: ${{ matrix.cc == 'clang' && 'clang' || '' }}
CXX: ${{ matrix.cc == 'clang' && 'clang++' || '' }}
run: cmake -S . -B build ${{ matrix.flavor == 'Release' && '-DCMAKE_BUILD_TYPE=Release' || '' }}

# The front end is recorded per leg so a log reader never has to infer it from the job name alone —
# a `cc: clang` leg whose CMake cache says GNU is a broken matrix, not a passing run.
- name: Confirm the front end this leg actually configured with
run: |
grep -h 'CMAKE_CXX_COMPILER_ID\|CMAKE_CXX_COMPILER_VERSION' build/CMakeFiles/*/CMakeCXXCompiler.cmake
Comment on lines +180 to +182

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
grep -hE 'CMAKE_CXX_COMPILER_ID|CMAKE_CXX_COMPILER_VERSION' build/CMakeFiles/*/CMakeCXXCompiler.cmake

Repository: redhat-et/ripwire

Length of output: 230


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== workflow context =="
sed -n '1,230p' .github/workflows/ci.yml 2>/dev/null || true

echo
echo "== grep availability =="
command -v grep || true
grep --version | sed -n '1,3p' 2>/dev/null || true
if command -v ggrep >/dev/null 2>&1; then
  ggrep --version | sed -n '1,3p' 2>/dev/null || true
fi

echo
echo "== regex behavior in current sandbox =="
printf '%s\n' 'CMAKE_CXX_COMPILER_ID="GNU"' 'CMAKE_CXX_COMPILER_VERSION="14"' > /tmp/test_compile.cmake
echo
echo "gnu-brace glob expanded grep command:"
grep -h 'CMAKE_CXX_COMPILER_ID\|CMAKE_CXX_COMPILER_VERSION' /tmp/test_compile.cmake || echo exit=$?
echo
echo "globs:"
find build -name CMakeCXXCompiler.cmake 2>/dev/null || true

Repository: redhat-et/ripwire

Length of output: 15420


🌐 Web query:

BSD grep does not support \| basic regular expression escape documentation

💡 Result:

In BSD grep (and other POSIX-compliant implementations), basic regular expressions (BREs) do not support the unescaped pipe (|) character as an alternation operator [1][2][3]. Instead, the pipe character is treated as a literal character when used in a basic regular expression context [1][2][3]. To perform alternation in BSD grep, you must either: 1. Use the escaped version of the pipe character (|) in a basic regular expression [1][2][3]. 2. Use the -E flag to interpret the pattern as an Extended Regular Expression (ERE), which supports the unescaped pipe (|) as an alternation operator [4][5][6]. This distinction arises because BSD grep adheres to the traditional definition of BREs, where metacharacters like?, +, {, |, (, and) are stripped of their special meaning and require a backslash to be interpreted as operators [1][2][3]. In contrast, some modern implementations (like GNU grep) may behave differently depending on the configuration, but for standard BSD grep, the backslash is required for BREs [1][2][3]. If you are writing portable scripts, using grep -E is generally recommended over relying on BRE-specific escape sequences [4][5][6].

Citations:


Use grep -E for the compiler-name check.

This step runs on macOS and still builds build/, so it can fail if the glob expansion produces no files. Make the find command explicit and keep the alternation -E so the release job prints both compiler values.

Portability-style replacement
-          grep -h 'CMAKE_CXX_COMPILER_ID\|CMAKE_CXX_COMPILER_VERSION' build/CMakeFiles/*/CMakeCXXCompiler.cmake
+          grep -E 'CMAKE_CXX_COMPILER_ID|CMAKE_CXX_COMPILER_VERSION' build/CMakeFiles/*/CMakeCXXCompiler.cmake
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 176 - 178, Update the “Confirm the
front end this leg actually configured with” step to use grep’s explicit
extended-regex mode (-E) while matching both CMAKE_CXX_COMPILER_ID and
CMAKE_CXX_COMPILER_VERSION. Make the input-file discovery explicit so the
command does not rely on an unmatched build/CMakeFiles glob on macOS, while
preserving output of both compiler values.


- name: Build
run: cmake --build build -j

Expand All @@ -165,6 +202,86 @@ jobs:
- name: G4 — xmllint --noout
run: ./build/ripwire test/fixture --no-cache | xmllint --noout -

# ─── rhel: the platform this project actually ships a prebuilt for, checked at PR time ───────────────
#
# GitHub hosts no RHEL runner image, but a job can run inside RHEL userland ON a hosted Ubuntu runner via
# `container:`. UBI (Universal Base Image) pulls from registry.access.redhat.com with no subscription,
# which is what makes this work on public CI — release.yml's `smoke-rhel` job already relies on it.
#
# WHY IT IS WORTH A LEG. release.yml builds the Linux artifact in a manylinux_2_28 container and smoke-runs
# it on ubi9 precisely because a binary built on stock ubuntu-24.04 needs GLIBCXX_3.4.31 and DIES on RHEL 9
# (verified, and recorded in that workflow's own comment). But that check only runs when a release is cut:
# every ordinary PR was tested on glibc 2.39 Ubuntu and macOS only, so a RHEL-incompatible change stayed
# invisible until publish time. This leg moves that signal to the pull request.
#
# SCOPE, STATED HONESTLY. It builds and runs the two headline product guarantees (determinism, well-formed
# XML) plus the fixture self-run — NOT the 361-gate suite. That is deliberate, not laziness: the suite needs
# ripgrep, which is not in the RHEL repositories (it lives in EPEL), and the RHEL-specific signal here is
# "does this toolchain compile it and does this userland run it", which the ubuntu legs cannot give and
# which these steps do give. The full suite stays on the four ubuntu legs and the two macOS ones.
rhel:
name: rhel (ubi9, ${{ matrix.flavor }})
runs-on: ubuntu-24.04
container: registry.access.redhat.com/ubi9/ubi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin the UBI container by digest.

registry.access.redhat.com/ubi9/ubi is a mutable tag. A changed image can alter CI behavior or introduce an unreviewed base image. Pin the approved image digest and update it deliberately.

🧰 Tools
🪛 zizmor (1.29.0)

[error] 225-225: unpinned image references (unpinned-images): container image is unpinned

(unpinned-images)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 225, Update the workflow’s container image
reference from the mutable registry.access.redhat.com/ubi9/ubi tag to the
approved immutable digest, preserving the existing UBI container configuration
and changing the digest only through deliberate image updates.

Source: Linters/SAST tools

strategy:
fail-fast: false
matrix:
flavor: [Release, plain]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
Comment on lines +231 to +233

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict and avoid persisting the checkout token.

This job builds and executes repository code after checkout. Set job-level permissions: { contents: read } and persist-credentials: false to prevent a token with repository-default permissions from being available to that code.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 231-240: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 231 - 233, Add job-level permissions
with contents: read for the workflow job containing actions/checkout@v4, and set
persist-credentials: false in that checkout step. Preserve the existing
fetch-depth setting while ensuring checked-out code cannot access a persisted
token or broader repository permissions.

Source: Linters/SAST tools


# RHEL 9's default gcc is 11, which does not implement C++23 — CMakeLists sets CXX_STANDARD 23 with
# STANDARD_REQUIRED ON, so the configure would fail outright. gcc-toolset-N is Red Hat's own supported
# answer (a parallel-installable newer GCC in /opt/rh). Probe newest-first rather than pinning one
# version: which toolsets a given UBI9 AppStream carries changes over the RHEL 9.x lifecycle, and a
# hard pin turns a routine repo refresh into a red leg. Whichever is found is ECHOED, so the log always
# records which compiler actually built this leg rather than leaving it to be inferred.
- name: Install the RHEL build toolchain
run: |
set -eu
dnf install -y cmake git libxml2 python3 make diffutils findutils
found=""
for v in 14 13 12; do
if dnf install -y "gcc-toolset-$v-gcc-c++"; then
found="$v"
break
fi
done
[ -n "$found" ] || { echo "no gcc-toolset-{14,13,12} in this UBI9 AppStream — C++23 is not buildable here"; exit 1; }
echo "TOOLSET=$found" >> "$GITHUB_ENV"

- name: Record the toolchain this leg is using
run: |
echo "gcc-toolset-$TOOLSET"
"/opt/rh/gcc-toolset-$TOOLSET/root/usr/bin/g++" --version
cmake --version | head -1
cat /etc/redhat-release

# CC/CXX point straight at the toolset's binaries rather than `source .../enable`, because every `run:`
# step is its own shell — a sourced environment would not survive to the next step.
- name: Configure (portable — no RIPWIRE_NATIVE; empty build type = the plain flavour)
env:
CC: /opt/rh/gcc-toolset-${{ env.TOOLSET }}/root/usr/bin/gcc
CXX: /opt/rh/gcc-toolset-${{ env.TOOLSET }}/root/usr/bin/g++
run: cmake -S . -B build ${{ matrix.flavor == 'Release' && '-DCMAKE_BUILD_TYPE=Release' || '' }}

- name: Build
run: cmake --build build -j

- name: Self-run on the fixture (it parses and ranks under RHEL userland)
run: ./build/ripwire test/fixture --no-cache | head -c 400; echo
Comment on lines +273 to +274

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Propagate ripwire failures from the RHEL smoke checks.

The head pipeline returns success when ripwire fails after head starts. The XML pipeline can also hide a nonzero producer exit. Write the self-run output to a file before truncating it, and enable pipefail for the XML pipeline.

Proposed fix
-        run: ./build/ripwire test/fixture --no-cache | head -c 400; echo
+        run: |
+          ./build/ripwire test/fixture --no-cache >/tmp/rhel-self-run.xml
+          head -c 400 /tmp/rhel-self-run.xml; echo
...
-        run: ./build/ripwire test/fixture --no-cache | xmllint --noout -
+        run: |
+          set -o pipefail
+          ./build/ripwire test/fixture --no-cache | xmllint --noout -

Also applies to: 282-283

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 273 - 274, Update the RHEL smoke-check
steps around the self-run command and the XML pipeline: capture the self-run
output in a temporary file before truncating it for display, so the ripwire exit
status propagates, and enable pipefail for the XML pipeline so producer failures
are not masked. Preserve the existing output truncation and XML processing
behavior.


- name: det-gate — 2-run byte-identical diff
run: |
./build/ripwire test/fixture --no-cache >/tmp/run_a.xml
./build/ripwire test/fixture --no-cache >/tmp/run_b.xml
diff -q /tmp/run_a.xml /tmp/run_b.xml

- name: G4 — xmllint --noout
run: ./build/ripwire test/fixture --no-cache | xmllint --noout -

asan:
name: asan (${{ matrix.os }})
strategy:
Expand Down
36 changes: 36 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,42 @@ which compiles `DEGRADED_PATH_ALERT` out; any gate that asserts a degrade path t
CI builds *both* flavours on purpose — Release catches optimizer-only bugs, the plain build catches
degrade paths. If you add a degrade path, the plain run is what proves it.

**Never edit the tree while a build is running, and never background a build you then edit around.**
This is not a style preference — it silently produces a binary that cannot exist from any single
commit, and it costs hours to diagnose because every symptom points somewhere else.

Make decides what to recompile by comparing mtimes. Header tracking is correct and complete (the
compiler writes real depfiles: `build/CMakeFiles/ripwire.dir/src/ingest.cpp.o.d` lists `src/model.h`
among ~1100 headers, and `pagerank.cpp.o.d` correctly lists none). But mtime ordering is only
meaningful if the sources hold still. If you edit `src/model.h` — or `git checkout` a branch that
does — while a build is in flight, that build writes `.o` files whose **mtime is newer than the
header** but whose **content predates it**. Make then correctly concludes "up to date" and never
recompiles them again. `cmake --build build -j` reports success, exit 0, no warnings, forever.

Two ways this was hit in one session, both from backgrounding a build:

- A `cmake --build asan` started on one branch and finished after a `git checkout` to another that
changed `Symbol`. Half the objects had `sizeof(Symbol)==96`, half `104`. ASan reported a
heap-buffer-overflow in `ingest` — a real report, of a fake bug. The tell: the overflowing buffer
was **1344 bytes = 14 × 96**, an exact multiple of the *previous* struct size. If a sanitizer
report's region size divides evenly by an old `sizeof`, stop debugging and rebuild.
- `src/quality.h`'s `kIngestParserVerMirror` edited while a clean rebuild ran. The binary kept
emitting the old value through repeated successful rebuilds; `qextractionkeycheck` failed with
`parserVer=41, expected 12/42` and looked like a missed mirror update. `touch`ing the header fixed
it, which is the diagnosis: the object was newer than the source it disagreed with.

If sources may have moved under a build — after any branch switch, or if you are unsure — do not
trust an incremental rebuild:

```bash
cmake --build build --clean-first -j # and the same for asan/ if that tree is in play
```

`test/g1freshcheck.sh` catches the ordinary stale binary (binary older than source) and is worth
believing when it fires — it is not noise. It cannot catch this variant, because here the binary is
*newer* than the source and only its contents are stale. Nothing in CMake can repair a source that
changed mid-compile; the discipline is the fix.

## Verify

```bash
Expand Down
12 changes: 12 additions & 0 deletions bench/headtohead/r4-2026-08-06/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ is not atomic, and a torn line is a silently lost instance.
stay in output order and simply never match the universe. Filtering one tool's noise and not
another's is how a comparison becomes an advertisement.

## One recorded field was rewritten after the fact

`results/r4_cbm.jsonl`'s `project` key. cbm mints its project id from the **absolute** path of the clone
it indexes, so all 60 recorded values began with the run machine's home directory — which
`test/ripwirepubliccheck.sh` arm 5 rejects, correctly, and which turned that gate red on all four CI legs.
The host-specific prefix is now the literal `workdir-`; the `repos-` / `repos_c-` pool marker and the repo
slug after it are untouched, because those are real provenance (which clone tree an instance ran in).

Nothing derived from the numbers moved: `r4_score.py` never reads `project` — only `r4_worker.py` does,
and only to hand cbm's own indexer's id back to its search call within a single run. A re-run regenerates
the field from whatever path it runs at, so it was never reproducible across machines to begin with.
Comment on lines +82 to +90

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Make path sanitization durable for reruns.

This change sanitizes only the checked-in JSONL. bench/headtohead/r4-2026-08-06/r4_worker.py still receives the absolute CBM project from cbm_index and passes that live value to cbm_search at Lines 180-202. The README also states that reruns regenerate project from the execution path. A future rerun can reintroduce a home-directory path, fail test/ripwirepubliccheck.sh, and disclose the runner path. Normalize the serialized value at the write boundary while retaining the raw ID for search.

As per path instructions: focus on major issues impacting performance, readability, maintainability and security; avoid nitpicks and avoid verbosity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bench/headtohead/r4-2026-08-06/README.md` around lines 82 - 90, Update
r4_worker.py at the result serialization boundary to sanitize the CBM project
value before writing JSONL, replacing the execution-specific absolute prefix
with the stable workdir- form. Preserve the raw project ID separately and
continue passing that raw value to cbm_search so search behavior is unchanged;
ensure reruns cannot serialize the runner’s home-directory path.

Source: Path instructions


## Two harness bugs worth remembering

Both were found because a run *failed loudly* rather than scoring a zero.
Expand Down
Loading
Loading