outcome_correct breaches, and regression classification all key off one predicate: score below threshold. The default (0.5) is a starting point, not a measurement. pandaprobe-harness-calibrate is the out-of-band diagnostic that tells you whether a breach actually corresponds to a real failure on your traffic, and what threshold would serve you better.
What to calibrate. Calibration reports on whatever metrics your workspace has scores for —
task_completion, coherence, tool_correctness, and friends. Aim it at the metrics whose threshold actually decides something — the Tier 2 metrics and outcome_correct. Tier-1 metrics never breach on their absolute value (their verdict is the trajectory gate), so their threshold is only an input to gate_target; their score distribution is still worth reading, because that is how you pick a sensible gate_target and gate_drop.Calibration is an operator tool, not a runtime gate — it never enters the healing loop. It reads scores, computes statistics, and prints a report; acting on it (setting a metric’s floor in the
thresholds map, say) is your call.With ground-truth labels
If you can label sessions as failed/ok — even a few dozen — you get the full picture:- JSON dict
- JSON list
- CSV
- Eval-set proxy
- Precision / recall / F1 of the breach predicate at your configured threshold, with the confusion matrix.
- A threshold sweep (
0.05 … 0.95) with the confusion at every point. - Two recommendations: the F1-maximizing threshold, and the lowest threshold reaching a target precision (
--target-precision, default0.9) with non-zero recall.
Without labels
No labels yet? The unsupervised report is still enough to pick a threshold sanely: the score distribution (min/median/mean/max/stdev + a 10-bucket histogram), the breach count at every candidate threshold, and inter-metric agreement — the fraction of fully-scored sessions where two metrics agree on breach vs. no-breach (low agreement usually means one threshold is mis-set with respect to the other).Where the scores come from
Two local sources, merged with precedence and each degrading independently:- The local history store —
state/score_history.json, the per-(session, metric) series the trajectory gate maintains (highest precedence). - The eval set — captured cases’
baseline_scores.
trace_id, not the session_id needed to join session labels. If neither local source yields anything, the CLI exits 1 and names both paths.
Library use
The same machinery is importable for notebooks and pipelines:pandaprobe-harness-calibrate --json emits the identical structure from the command line. A self-contained walkthrough lives at examples/misc/calibration_demo.py in the repository.

