From 599a889b0d085eb677429f1bcb5b984156a393c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 21 Jun 2026 14:20:10 +0000 Subject: [PATCH] fix(ci): soundness gate must not assume bare `dune` on PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Property 5 ran `dune build test/xfail/test_xfail_pins.exe`, which fails in CI where dune is only reachable via `opam exec --` (not bare) — greening locally but red in CI ("ERROR (property 5): cannot build ..."), which is how it slipped past a local run. The harness exe is already built by the `dune build` / `dune runtest` steps that run before this gate, so prefer the pre-built binary and only build (with whatever dune is available) if it is missing; fail closed otherwise. Verified by running the gate with the exe pre-built and `dune`/`opam` removed from PATH (the CI condition) — it passes. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01BbxKhXQwTvVgkYDgBMLJoa --- tools/check-soundness-ledger.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tools/check-soundness-ledger.sh b/tools/check-soundness-ledger.sh index 13f3e4f9..04dbe173 100755 --- a/tools/check-soundness-ledger.sh +++ b/tools/check-soundness-ledger.sh @@ -253,8 +253,21 @@ check_pins() { local pins; pins="$(pinned_row_pins)" [ -n "$pins" ] || { note "ERROR (property 5): no 'residual (pinned)'/'open (tracked)' rows found (fail closed)"; return 1; } printf '%s\n' "$pins" | grep -q '^NOPIN$' && { note "ERROR (property 5): a pinned/open row names no test_* pin (fail closed)"; return 1; } - dune build "$XFAIL_EXE" >/dev/null 2>&1 || { note "ERROR (property 5): cannot build ${XFAIL_EXE}"; return 1; } - local report; report="$(AFFINE_FIXTURES="$PWD/test/e2e/fixtures" "_build/default/${XFAIL_EXE}" 2>&1 || true)" + # Run the pin harness. CI builds it in the `dune build` / `dune runtest` steps + # before this gate, so prefer the pre-built binary and do NOT assume a bare + # `dune` is on PATH (in CI dune is reached via `opam exec --`, not bare). Only + # build if the exe is missing (e.g. a standalone local run), with whatever dune + # is available. Fail closed if it still isn't there. + local exe="_build/default/${XFAIL_EXE}" + if [ ! -x "$exe" ]; then + if command -v dune >/dev/null 2>&1; then + dune build "$XFAIL_EXE" >/dev/null 2>&1 || true + elif command -v opam >/dev/null 2>&1; then + opam exec -- dune build "$XFAIL_EXE" >/dev/null 2>&1 || true + fi + fi + [ -x "$exe" ] || { note "ERROR (property 5): pin harness ${exe} not built (run dune build first)"; return 1; } + local report; report="$(AFFINE_FIXTURES="$PWD/test/e2e/fixtures" "$exe" 2>&1 || true)" local pin r=0 while IFS= read -r pin; do [ -z "$pin" ] && continue