mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-31 04:07:52 +00:00
Five red-team criticals, all closed and test-pinned: (1) two-PR gate poisoning: a same-hash committed-baseline edit must now be receipts-backed (byte-match THIS run) or the gate is inconclusive — and a receipts-backed same-hash regression still needs a justification; (2) corpus hollowing: bless-mode gold_total shrink (deleting failing fixtures / flipping holdout) now requires a justification like any adverse move; (3) continuity cross-source detection was structurally blind to writer-seeded slugs (the zero-gate was vacuous) — writer+reader slug→source maps now merge; (4) --update-baseline writes BEFORE compare (the one-shot bless flow no longer trains users to ignore exit 2) and REFUSES to write when seed failures invalidate the run; (5) 15 continuity writers' write-back gold was computed and dropped — now accumulated, so the write-back cell is flag-independent (gold 46 → 61). Plus: baselines bind their run config (holdout/llm/harness/suite — fixtures_hash covers files only), the CI gate script refuses a working-tree baseline deletion, and renderInlineTime no longer straddles UTC midnight for non-Z offsets.
50 lines
2.5 KiB
Bash
Executable File
50 lines
2.5 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
|
|
# Deletion defense (red-team finding): if main carries a baseline but the
|
|
# working tree deleted it, every FUTURE PR would take the ungated
|
|
# first-landing path once this one merges. Refuse.
|
|
if [ ! -f "$BASELINE_PATH" ]; then
|
|
echo "[brainbench-gate] ERROR: ${BASELINE_PATH} exists on ${MAIN_REF} but is deleted in this tree — restore it or re-run --update-baseline" >&2
|
|
exit 2
|
|
fi
|
|
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
|