Repository navigation
fix: semantic drift within min_similarity no longer fails runs; tool evaluators skip tool-free suites - #50
Merged
Conversation
Spec and implementation plan for run r_20261011_triage_e14cda, which failed with every budget passing: semantic drift within min_similarity tested as a critical regression, and a top-level trace_invariants entry was booked as unmeasured on a tool-free suite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ltas semantic pins the source at 1.0, so any rewording is a negative delta. The paired test ran on those raw deltas and called nine consistent 0.90-0.98 rows 'critical', failing a run whose every budget passed. Rows inside min_similarity now enter the test as no change, matching the budgets; delta_avg_score and the aggregates stay raw. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A top-level trace_invariants entry is inherited by tool-free suites, where every pair was booked unmeasured and the blind gate demoted a clean run to conditional_pass with a note about 'tool calls and no prose'. Such pairs now get no cell, like an applies_to miss, and evaluate names the real cause. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ol evaluators Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The templated recommendation read only the overall budget rows, so a run failed by a conclusively breached slice budget said "see the verdict's reason" (there was none) or blamed a slice-only blocking comparison "although no budget was breached" - false twice. failed_budgets now covers every gating budget (overall rows plus the distinct slice rows, via policy.gating_budget_results), naming a slice row for its scope. The severity branch reads a new overall_blocking_evaluators field (slice "all" only); blocking_evaluators keeps its meaning for the prompt and the advisory summary. The generic fail branch no longer points at a reason that can be None. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The forgiven-row test now requires the Wilcoxon path and an effect size computed over the adjusted deltas; a new case reaches the paired t with forgiven rows and pins d and its analytical CI the same way. Both fail when _cohens_d_paired is handed the raw deltas. Also corrects two comments: why the tested and raw means never disagree in sign (only semantic rows are forgiven, and their deltas are never positive), and why SlicedScore.within_tolerance is defaulted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
compare runs the evaluate stage with quiet=True inside its Live grid, so the "checked nothing: no example ... offered the models any tools" warning never reached the user. EvaluateResult now carries the rendered lines (in memory only, like harness_check) and compare prints them above the verdict. Also corrects _score_one_tool's docstring: tool-free pairs never reach it (see _attempts); only a one-sided trace returns [] there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- DOCS.md: a tool-free pair is "not attempted", not "not applicable" (that term is the unmeasured-coverage note in the same paragraph). - llms-full.txt: move the tool-free sentence to the end of item 0 so "The signal" again refers to the "nothing measured:" note. - methodology.md: tool evaluators never see a pair offered no tools; no row, no coverage, no verdict effect; errored pairs keep error rows. - evaluate and compare both warn that a tool evaluator checked nothing. - CHANGELOG: "each comparison"; the recommendation names a breached slice budget; old runs need evaluate re-run to drop stale coverage. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 12, 2026
Merged
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.
What this changes
semanticrow whose similarity stays abovemin_similarity(it carries noSEMANTIC_REGRESSIONflag) now enters the paired significance test as a delta of 0. That is the line the policy budgets already drew, and both now read it from one predicate:analysis/tolerance.py::within_tolerance.delta_avg_score, the slice aggregates and the per-example rows stay raw. A comparison with forgiven rows carries the notek of n rows within the evaluator's own threshold (min_similarity) — tested as no change.decision.reasonnow names each such comparison. The report hero no longer shows "7 of 7 budgets within policy" under FAIL.trace_invariantsthat every suite inherits) are not attempted on a pair where both calls are error-free and neither has a tool trace. Such a pair gets no row and no coverage entry, so it no longer demotes the run toconditional_pass.evaluatewarns once that the evaluator checked nothing because no example offered tools, andcomparenow shows that warning too.docs/methodology.md,docs/configuration.md,docs/agents.md,DOCS.md,llms-full.txt, andCHANGELOG.mdunder[Unreleased].No changes to the config, suite,
state.json,scores.jsonlor bundle schemas.ComparisonResulthas no new field; the new information goes innotes.Why
Run
r_20261011_triage_e14cda(suitetriage,anthropic/claude-sonnet-5→anthropic/claude-sonnet-5-5, CLI 1.4.1) came out FAIL, but nothing in it had regressed. Every budget passed and everysemantic.cosinerow was abovemin_similarity: 0.9(the lowest was 0.904). The semantic evaluator pins the source score at 1.0, so any rewording produces a small negative delta. Nine small drops that all go the same way have almost no variance, so the paired t-test on the raw deltas called themcritical(adjusted p 0.0004, d −2.15), and that failed the run.Fixing that alone would still have left the run at
conditional_pass. The top-levelagent_contractinvariants had been written for another suite's tools, and they booked every pair in this tool-free suite as unmeasured.Before → after for this run: FAIL → PASS. Re-analysing the existing scores already gives
semantic.cosineseveritynone(Δ −0.0524, note "9 of 9 rows within…"). Clearing the staleagent_contractcoverage needs a freshevaluate, which is still pending: it needs an embedding key.Accepted consequence: if only a few rows in a comparison are flagged and many are forgiven, the comparison may not reach significance on its own. The rate budgets (
max_overall_regression_rate,min_equivalence_rate) then decide, which is whatdocs/methodology.mdalready described. To pick up the tool-free fix, an old run needsevaluatere-run;analyzealone is not enough.Follow-ups, deliberately out of scope:
max_critical_regressionsis inert._metricsreads ametadata["severity"]key that nothing writes, and fixing it is a cross-repo change withevalshift-server.failed_budgets.comparestill swallowsevaluate's other warnings that only print outside quiet mode.severity_flooris written but never read.tool_argumentshas the same pinned-source shape assemantic.samples_per_example > 1, a reduced row can carry a semantic flag that disagrees with its mean score.Test plan
pytestpasses: 3035 passed, 94.95% coverage (make ci)ruff check,ruff format --checkcleanmypy --strict src/evalshift_cliclean## [Unreleased]evaluate→analyze→reportonr_20261011_triage_e14cdaand confirmpass, theagent_contract"checked nothing" warning, and that noagent_contractcomparison remainsNew tests pin these cases:
none, and nine flagged rows still test ascritical.kind, and renamed semantic evaluators, are both forgiven.min_similaritystill fails.reason = Noneand names the budget.applies_tomiss keeps its own warning wording.🤖 Generated with Claude Code