Files
gbrain/scripts/ci-brainbench-gate.sh
T
Garry Tan c2938f62fb fix: pre-landing review fixes — specialist round (4 reviewers, 22 findings)
The catch that mattered (testing + maintainability, independently): --llm
extraction metrics were computed per fixture but never reached any scoreboard
cell — dead matched_any_gold/stored_rows aggregation fields proved the
unfinished wiring. Now aggregated Σ-style into the write-back cell when llm
is on, pinned by a harness-level stubbed-transport test.

Also: RUN-scoped --llm BudgetTracker (a per-invocation cap multiplied by
fixture count — ~$550 worst case — now one tracker, exhaustion aborts the
run loudly); continuity loop collapsed to one prep per pair + read-only
reader per harness (the writer replay was provably observable-effect-free
and orderings rebuilt byte-identical brains — 90 preps → 15, runtime ~12s →
~7s, identical scores; baseline counts updated 24 → 12 honestly);
factKeywordProbe escapes ILIKE metacharacters; validator rejects unpassable
slug-less retrieve-turns; ci-gate fails HARD on a broken ref instead of
silently running ungated; privacy year-scan covers the gold dir; e2e tests
self-sufficient (shared artifacts in beforeAll) with a minimal-env --llm
gate; round4/cellKey single-sourced; explicit return in eval.ts dispatch;
stale comment + type anchor fixes.
2026-06-12 12:38:34 -07:00

43 lines
2.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# BrainBench CI gate (Cathedral 2, decision 4) — local parity with the
# .github/workflows/test.yml `brainbench` job.
#
# Governance: the gate compares HEAD's run against MAIN's copy of the
# committed baseline (git show origin/master:...), NEVER the working tree's —
# a PR cannot rewrite the thing it is compared against. Two modes resolve
# automatically inside `eval brainbench --compare`:
# same fixtures_hash → count-aware gate (any newly-failed gold item fails)
# different hash → corpus-bless (the PR's committed baseline must
# exactly match HEAD's run; regressions vs main need
# a `justification` in the committed baseline)
#
# Exit codes pass through: 0 pass · 1 regression · 2 error/inconclusive.
set -euo pipefail
BASELINE_PATH="evals/brainbench/baselines/main.json"
MAIN_REF="${BRAINBENCH_MAIN_REF:-origin/master}"
# mktemp default (review finding): a fixed world-writable /tmp path is a
# symlink-planting target on shared hosts. CI overrides via BRAINBENCH_OUT.
OUT="${BRAINBENCH_OUT:-$(mktemp /tmp/brainbench-result-XXXXXX.json)}"
MAIN_BASELINE="$(mktemp /tmp/brainbench-main-baseline-XXXXXX.json)"
trap 'rm -f "$MAIN_BASELINE"' EXIT
# Fail HARD when the ref itself is broken — only a genuinely-absent baseline
# may take the ungated first-landing path (review finding: an unfetched ref
# or typo'd BRAINBENCH_MAIN_REF must not silently disable the gate).
if ! git rev-parse --verify --quiet "${MAIN_REF}^{commit}" > /dev/null; then
echo "[brainbench-gate] ERROR: ref ${MAIN_REF} does not resolve — fetch it or fix BRAINBENCH_MAIN_REF" >&2
exit 2
fi
if git show "${MAIN_REF}:${BASELINE_PATH}" > "$MAIN_BASELINE" 2>/dev/null; then
echo "[brainbench-gate] comparing against ${MAIN_REF}:${BASELINE_PATH}"
bun src/cli.ts eval brainbench --compare "$MAIN_BASELINE" --out "$OUT"
else
# First landing: the ref exists but carries no baseline yet. Run without a
# gate so the PR that introduces BrainBench can commit the initial baseline.
echo "[brainbench-gate] no baseline on ${MAIN_REF} yet — running ungated (initial-landing path)"
bun src/cli.ts eval brainbench --out "$OUT"
fi