Skip to content

Commit 7a9efd9

Browse files
committed
Fix stale tier-xhard grader hash and guard locked fixtures
The tier-xhard grader pinned a hash its fixture no longer has, so every run failed at setup and blamed the agent for an edit it never made.
1 parent a61cd76 commit 7a9efd9

4 files changed

Lines changed: 79 additions & 1 deletion

File tree

evals/capability/cases/tier-hard/verify.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ TEST_SHA="a7ce0705273ad6b874b737d389db038745e5baec825fd757f42448cd1addb687"
1313

1414
actual_sha=$(shasum -a 256 tests/report.test.ts | cut -d' ' -f1)
1515
if [[ "$actual_sha" != "$TEST_SHA" ]]; then
16+
# A stale TEST_SHA is indistinguishable from an agent edit above, so check
17+
# the fixture's own hash at setup before blaming the agent: the workdir is
18+
# a git repo committed before the run, so HEAD holds the pristine file.
19+
setup_sha=$(git show "HEAD:tests/report.test.ts" 2>/dev/null | shasum -a 256 | cut -d' ' -f1 || true)
20+
if [[ -n "$setup_sha" && "$actual_sha" == "$setup_sha" ]]; then
21+
echo "FAIL: tests/report.test.ts is unchanged from the fixture at setup ($setup_sha) but does not match TEST_SHA ($TEST_SHA): the case's locked hash is stale (broken case), not an agent edit"
22+
exit 1
23+
fi
1624
echo "FAIL: tests/report.test.ts was modified (contract file must be byte-unchanged)"
1725
exit 1
1826
fi

evals/capability/cases/tier-med/verify.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ TEST_SHA="4b48fc362c6c3a72af405545ffa816f4a531bbed974d87b6f4d60cf5c96f06a0"
1313

1414
actual_sha=$(shasum -a 256 tests/checkout.test.ts | cut -d' ' -f1)
1515
if [[ "$actual_sha" != "$TEST_SHA" ]]; then
16+
# A stale TEST_SHA is indistinguishable from an agent edit above, so check
17+
# the fixture's own hash at setup before blaming the agent: the workdir is
18+
# a git repo committed before the run, so HEAD holds the pristine file.
19+
setup_sha=$(git show "HEAD:tests/checkout.test.ts" 2>/dev/null | shasum -a 256 | cut -d' ' -f1 || true)
20+
if [[ -n "$setup_sha" && "$actual_sha" == "$setup_sha" ]]; then
21+
echo "FAIL: tests/checkout.test.ts is unchanged from the fixture at setup ($setup_sha) but does not match TEST_SHA ($TEST_SHA): the case's locked hash is stale (broken case), not an agent edit"
22+
exit 1
23+
fi
1624
echo "FAIL: tests/checkout.test.ts was modified (contract file must be byte-unchanged)"
1725
exit 1
1826
fi

evals/capability/cases/tier-xhard/verify.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,21 @@
55
# at green fails every check below.
66
set -euo pipefail
77

8-
TEST_SHA="e125ecc69453b923bb69b46e8411ca5b580e0afb81868a8e24f45a10d93e1d2a"
8+
TEST_SHA="bd97182202b283a721f2972f1fba79e2b552a95a64f1639d077022aa95a157d5"
99

1010
[[ -f package.json ]] || { echo "FAIL: package.json missing in workdir"; exit 1; }
1111
[[ -f tests/notify.test.ts ]] || { echo "FAIL: tests/notify.test.ts is gone"; exit 1; }
1212

1313
actual_sha=$(shasum -a 256 tests/notify.test.ts | cut -d' ' -f1)
1414
if [[ "$actual_sha" != "$TEST_SHA" ]]; then
15+
# A stale TEST_SHA is indistinguishable from an agent edit above, so check
16+
# the fixture's own hash at setup before blaming the agent: the workdir is
17+
# a git repo committed before the run, so HEAD holds the pristine file.
18+
setup_sha=$(git show "HEAD:tests/notify.test.ts" 2>/dev/null | shasum -a 256 | cut -d' ' -f1 || true)
19+
if [[ -n "$setup_sha" && "$actual_sha" == "$setup_sha" ]]; then
20+
echo "FAIL: tests/notify.test.ts is unchanged from the fixture at setup ($setup_sha) but does not match TEST_SHA ($TEST_SHA): the case's locked hash is stale (broken case), not an agent edit"
21+
exit 1
22+
fi
1523
echo "FAIL: tests/notify.test.ts was modified (contract file must be byte-unchanged)"
1624
exit 1
1725
fi
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { createHash } from "node:crypto";
3+
import { readFile } from "node:fs/promises";
4+
import { dirname, join, resolve } from "node:path";
5+
import { fileURLToPath } from "node:url";
6+
import { loadEvalCases, resolveFixturePath } from "./lib.js";
7+
8+
const capabilityDir = dirname(fileURLToPath(import.meta.url));
9+
10+
function parseLockedContract(
11+
verifySrc: string,
12+
caseId: string,
13+
): { sha: string; path: string } | null {
14+
const sha = verifySrc.match(/^TEST_SHA="([0-9a-f]{64})"$/m)?.[1];
15+
if (sha === undefined) return null;
16+
const hashed = verifySrc.match(/shasum -a 256 (\S+)/)?.[1];
17+
const guarded = [...verifySrc.matchAll(/\[\[ -f (\S+) \]\]/g)].map(
18+
(m) => m[1],
19+
);
20+
if (hashed === undefined || !guarded.includes(hashed)) {
21+
throw new Error(
22+
`case ${caseId}: cannot determine the locked contract file from verify.sh`,
23+
);
24+
}
25+
return { sha, path: hashed };
26+
}
27+
28+
describe("locked fixture hashes", () => {
29+
test("every TEST_SHA literal matches its fixture file", async () => {
30+
const repoRoot = resolve(capabilityDir, "..", "..");
31+
const cases = await loadEvalCases(join(capabilityDir, "cases"));
32+
const failures: string[] = [];
33+
let checked = 0;
34+
for (const c of cases) {
35+
const verifySrc = await readFile(join(c.caseDir, c.verify), "utf8");
36+
const locked = parseLockedContract(verifySrc, c.id);
37+
if (locked === null) continue;
38+
checked += 1;
39+
const bytes = await readFile(
40+
join(resolveFixturePath(repoRoot, c.fixture), locked.path),
41+
);
42+
const actual = createHash("sha256").update(bytes).digest("hex");
43+
if (actual !== locked.sha) {
44+
failures.push(
45+
`case ${c.id}: ${locked.path} hashes to ${actual} but verify.sh pins ${locked.sha}`,
46+
);
47+
}
48+
}
49+
expect(checked).toBeGreaterThan(0);
50+
if (failures.length > 0) {
51+
throw new Error(`stale locked hashes:\n${failures.join("\n")}`);
52+
}
53+
});
54+
});

0 commit comments

Comments
 (0)