Repository navigation
Calibration: let fitted temperatures and cutoffs reach .confidence - #2
Merged
Merged
Conversation
Two places where calibration and the runtime disagreed about what `<judgment>.confidence` is: - `calibrated()` reshaped a choice's or a score's probabilities but left its `confidence` as the judge first reported it, and the `confidence` node reads exactly that value. So for choice questions a fitted temperature could never change what a `.confidence` rule decides, while docs/rules.md presents calibration as what makes such a threshold meaningful. It now works the confidence out again from the tempered probabilities: for a score with `score_confidence`, for a choice with the `choice_confidence` formula applied to the option it chose (the same number whenever a judge chose its likeliest option; lower when it chose another, down to 0 once the chosen option falls below 1/K). At temperature 1 nothing changes. - `observed()` collected the chosen option's probability (`sureness`) as the values a cutoff on a confidence node could be drawn between, while the runtime compares the reported confidence. On two options p = 0.68 reads 0.36, so for choice questions every candidate line was in the wrong number space. It now collects the reported confidence. Regression tests: a property test that, after tempering, `confidence` is recomputed from the tempered probabilities; an end-to-end `threat.confidence >= 0.7` rule that `calibrated 5` now flattens; and a 41-example cutoff fit where the old code moved the line 0.95 -> 0.555 (33/41 right) and the fixed code finds 0.39 (41/41). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bradAGI
added a commit
that referenced
this pull request
Oct 12, 2026
Ships the calibration fix from #2: a fitted temperature and fitted cutoffs now reach the confidence a rule tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi Brad, here's the PR you asked for on the call, from Mike's team. Our agents prepared it, and separate review passes checked it before we opened it. Reading through rulesmith we found one real bug, in how calibration and
.confidenceinteract. This PR fixes only that.What's wrong
Calibration and the runtime disagree about what
<judgment>.confidenceis, in two places:.confidence.calibrated()(runtime.py) reshapes a choice's or a score'sprobabilitiesbut leavesanswer["confidence"]at the value the judge first reported. Theconfidencenode reads exactly that value. So for choice questions a fitted temperature can't change what a.confidencerule decides, whiledocs/rules.mdpresents calibration as what makes such a threshold meaningful. Example:threatanswers ATTACK at 0.9 vs 0.1, andthreat.confidencereads 0.80 both uncalibrated and atcalibrated 5. Only the reportedprediction.confidencemoves.observed()(calibrate.py) collectssureness(), the probability of the chosen option, as the values a line can sit between. The runtime compares the reported confidence, which for a choice is(p - 1/K) / (1 - 1/K). On two options p = 0.68 reads 0.36. So for a choice question every candidate line is in the wrong space, worst on 2-option questions. Lines on noul and score values were already consistent.The fix
calibrated()now recomputesconfidencefrom the tempered probabilities. A score usesscore_confidence. A choice uses thechoice_confidenceformula on the option it chose. That gives the same number whenever a judge chose its likeliest option. When it chose another option the number is lower, down to 0 once the chosen option falls below 1/K. At temperature 1 nothing changes.observed()now collects the reportedconfidence, the number the rule actually compares. A noul answer has no confidence, so its confidence node is absent, same as at runtime.docs/rules.mdsayingthreat.confidencemoves too.Another option was to make
.confidencereturn the tempered probability of the chosen option. We didn't do that because it would change what every existing.confidence >= xline means. Happy to switch if you prefer it.Two things worth knowing:
chat_judge.pysays the hosted judge's does. If a judge reports something else, the recomputed value replaces it as soon as a temperature other than 1 is set, whichrulesmith calibratedoes for nearly every choice question it fits. For such a judge,calibrated 1.0001reads differently from no temperature at all.confidence, and soprediction.confidence, move with it.calibrateonly fits temperatures for choices, so this only affects hand-written score temperatures.Before / after
A constructed 41-example task: a 2-option judge whose confidence follows the published formula, and a threshold on
route.confidencestarting at 0.95. Accepted requests read 0.42–0.90, reviewed ones 0.10–0.36.main(6a0564b)threat.confidence >= 0.7withcalibrated 5: onmainit reads 0.80 and the rule fires. On this branch it reads 0.22 and the decision falls through toalways.Tests
New tests in
tests/test_calibrate.py:confidenceis recomputed from the tempered probabilities, for choices and scores, and that an answer whose chosen option falls below 1/K reads 0;threat.confidencerule above;mainwith the line at 0.555.No existing test was changed. Full suite, on macOS, Python 3.12, core deps plus the
bench/chessextras (no vizdoom, so the doom/arena modules aren't collected), live and paid markers deselected:mainThe one failure is the same on both:
test_invalid_opponent_is_rejected_before_judge_credentialsimportstest_arena, which pulls inrulesmith.doomand needs vizdoom.ruff checkandruff format --checkare clean.Thanks for building this and for asking us to look. We kept this PR to the one bug. We also have a few small, separate suggestions (docs and defaults) and can send them as their own PRs if you want them.
🤖 Generated with Claude Code