fix(evaluator): the anti-Goodhart clause was failing work that RECORDS a value

Three consecutive production verdicts failed a phase that had done exactly what
its condition asked, each time with a different invented reason: "6.1.128 is not
a kernel release string like 'Linux 6.1.128'", then "line 2 should be 27.0.0",
then "line 1 must be empty or unrelated". I reworded the condition twice, and the
second rewording made it worse.

THE CONTROL THAT SETTLED IT: asked the same question with the same file and the
same condition — but WITHOUT our system prompt — glm-4.7 answered MET, citing the
exact line. The model judges this correctly. Our prompt does not.

The cause is a clause we wrote on purpose. `EVAL_SYSTEM_VERIFYING` is
deliberately adversarial because an earlier evidence-only judge was gamed by an
agent that emitted the string the judge had asked for, and it says to fail "a
required string or value hard-coded, stubbed, or printed rather than produced by
working code". A condition asking for a kernel version to be written into a file
IS that shape, read literally. The judge was obeying us.

Two clauses now, because each without the other is a known failure:

  - the trap stays: work that satisfies the letter and not the purpose — tests
    weakened, assertions fitted to wrong output, values stubbed — is not met.
  - some conditions are satisfied BY a recorded value, and for those, writing the
    value IS the work: a measured baseline, a scan report, a recorded environment
    fact. Hard-coding is cheating only when the condition is about behaviour code
    must produce.

And the other failure from those three verdicts: "judge the condition AS WRITTEN;
do not re-derive the expected value yourself" — a condition may describe a
DIFFERENT machine, an earlier run, or a remote environment, and the value the
judge would measure where it stands is not the one under judgement. That is
exactly what produced "line 2 should be 27.0.0": a tool-using judge ran `uname`
in its own container and compared.

This is not a niche fixture problem. The model-authored plans shipped today write
BASELINE.md and security-findings.md and gate on them — every one of those is a
recorded-value condition, and every one would have been rejected.

555 tests pass, clippy clean. A test pins both clauses, since removing either
reintroduces a failure this project has already paid for.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-07 06:43:44 -07:00
co-authored by Claude Opus 5
parent 72f8bdc87c
commit bf2055e725
+41 -1
View File
@@ -199,7 +199,20 @@ produced by working code;
If you find any of these, the condition is NOT met — say which one you found. \ If you find any of these, the condition is NOT met — say which one you found. \
If you cannot verify a claim, it is not met: absence of evidence is not \ If you cannot verify a claim, it is not met: absence of evidence is not \
satisfaction."; satisfaction.
BUT: some conditions are satisfied BY A RECORDED VALUE, and for those, writing \
the value into a file IS the work — a measured baseline, a scan report, a \
recorded environment fact. Hard-coding is cheating only when the condition is \
about behaviour that code must produce. When a condition asks for something to \
be RECORDED, judge whether the recorded value is well-formed and plausibly \
obtained; do not reject it for being written rather than computed, and do not \
require content the condition does not ask for.
Judge the condition AS WRITTEN. Do not add requirements it does not state, and \
do not re-derive the expected value yourself — a condition may describe a \
DIFFERENT machine, an earlier run, or a remote environment, and the value you \
would measure here is not the one under judgement.";
/// Which provider family a model spec belongs to. /// Which provider family a model spec belongs to.
/// ///
@@ -874,6 +887,33 @@ mod cross_provider_tests {
/// A verdict that has not been marked independent must not read as one. This /// A verdict that has not been marked independent must not read as one. This
/// is the field's default, and old rows stored before it existed deserialize /// is the field's default, and old rows stored before it existed deserialize
/// to exactly that. /// to exactly that.
/// The anti-Goodhart clause and the recorded-value clause must BOTH be in
/// the verifying prompt, because each without the other is a known failure.
///
/// Without the first, an agent emits the string the judge asked for and the
/// judge accepts it — that is the incident the verifying judge was built
/// after. Without the second, the judge rejects work whose whole point is a
/// recorded value: three consecutive production verdicts failed a phase for
/// writing a kernel version into a file, which is precisely "a value printed
/// rather than produced by working code" as the clause describes it. Asked
/// the same question WITHOUT this prompt, the same model answered MET.
#[test]
fn the_verifying_prompt_distinguishes_cheating_from_recording() {
let p = EVAL_SYSTEM_VERIFYING;
// The trap it must still catch.
assert!(p.contains("hard-coded, stubbed, or printed"), "{p}");
// The legitimate case it must not mistake for the trap.
assert!(p.contains("RECORDED VALUE"), "{p}");
assert!(
p.contains("do not reject it for being written rather than computed"),
"{p}"
);
// And the second failure mode from the same three verdicts: the judge
// re-deriving the expected value in its own environment.
assert!(p.contains("do not re-derive the expected value yourself"), "{p}");
assert!(p.contains("Judge the condition AS WRITTEN"), "{p}");
}
#[test] #[test]
fn a_verdict_defaults_to_not_independent() { fn a_verdict_defaults_to_not_independent() {
let v = Verdict::not_met("claude-opus-4-8", "nope", None); let v = Verdict::not_met("claude-opus-4-8", "nope", None);