Repository navigation
Conversation
A zero delta on a task set where both arms pass (or fail) every paired task reads as "no effect" but means the tasks had no room to move. Gate and score comparisons now carry headroom: ceiling, floor, near_ceiling (baseline at the top on all but at most one of 2+ tasks) or null, with a note in the text report. p values and Holm are unchanged. result.json records the task's score_max so score rows know their top. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #114 +/- ##
==========================================
+ Coverage 90.24% 90.31% +0.06%
==========================================
Files 206 206
Lines 24289 24450 +161
Branches 3906 3948 +42
==========================================
+ Hits 21920 22082 +162
+ Misses 1703 1700 -3
- Partials 666 668 +2
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟡 Changes recommended
Pooling legacy and current score cells can incorrectly report a ceiling despite unknown legacy maxima.
1 open issue
What changed in this PR
Adds headroom notes to repository comparisons to distinguish saturated task results from evidence of no effect.
Changes:
- Classifies gate and score comparisons as ceiling, floor, or near-ceiling.
- Records task score maxima for score comparisons.
- Adds tests, contract documentation, and a changelog entry.
| File | Description |
|---|---|
| dokimi/tests/test_repo_run.py | Checks persisted score maxima. |
| dokimi/tests/test_repo_compare.py | Tests headroom classifications and text output. |
| dokimi/dokimi/repos/runner.py | Records score_max in results. |
| dokimi/dokimi/repos/compare.py | Computes and displays headroom flags. |
| dokimi/docs/repo-evaluation-contract.md | Documents headroom behavior. |
| dokimi/changelog.d/20261018_120000_napetrov_feat-repo-compare-ceiling.md | Announces comparison notes and metadata. |
Review effort: Balanced
…ore_max
When legacy cells (score_max=None) and current cells (score_max=1.0) appear
for the same task, do not report ceiling if legacy maxima are unknown.
Previously, _finite() would drop None values, leaving only {1.0}, which
incorrectly triggered the ceiling headroom flag. Now check if any cell has
an unknown max before setting top.
Fixes the case where both arms average 1.0 but some cells have unknown
score_max values.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
antonwolfy
left a comment
There was a problem hiding this comment.
The rule is sound and the legacy-batch handling after c0c29af holds up, but the note misreads a regression, and the tests leave much of the new logic unpinned. I'd fix the inline risks before merge.
What I ran, on a Linux review host at c0c29af:
make ci-local: 19 passed, 0 failed, 4 skipped; changed-line coverage gate passes.- Reproductions for the code findings, with the LLM transport mocked (cells built with the
_cellhelper oftest_repo_compare.py). - A mutation run over
headroom(),_task_values,_headroom_note, the_compare_modelwiring and therunner.pyscore_max: 36 mutants, 16 of which passtest_repo_compare.pyandtest_repo_run.py(99 tests).
Test gaps (the mutants that survive):
- The tolerance and the floor condition are unpinned.
value >= topwithoutEPS,value >= top - 0.5, and a floor that checks only the baseline, only the treatment, or usesorall pass. Every fixture uses exact 0, 1 orscore_maxvalues. Add a case with a mean of reps just under the top (for example 0.6 of 1) that must stayNone, and a floor case where only one arm is at 0. len(pairs) >= 2is unpinned. Changing it to>= 1or>= 3passes. Add a one-task comparison with the baseline at the top and the treatment below: it must beNone.headroomon non-primary rows is unpinned.if True else Noneatcompare.py:257passes, because the only check is cost 0.1, whereheadroom()returnsNoneanyway. Use a cost of 1.0 on every task.- Pooling cells with different
score_max, and the treatment's own top, are unpinned.max(maxes)instead ofNone, and using the baseline's top for both arms, both pass. The new regression test covers only the unknown-max case.
Checked, no issues
- Holm and p:
headroomis added afterpaired_summary;pandp_holmare unchanged on a ceiling row. - JSON:
headroomisnullon cost, turns and metric rows; gate and score are flagged separately in a mixed task set. - Legacy batches: cells with and without
score_maxpooled for one task giveNone, as do cells with different maxima. - Repo rules: the changelog fragment follows
changelog.d/README.md; no versioned schema orwith_docs/without_docskey is touched.
…lure or mixed score.max as no signal near_ceiling needs the treatment nowhere below the baseline, floor ignores pairs with a grader failure, and pairs whose tops differ have no headroom. Pin score_max in result.json to the task's score.max with a max-4 task, and rewrap and complete the contract bullet. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
3 open issues
Review effort: Lite
| "harness": opts.profile.name, "harness_version": meta["harness_version"], | ||
| "base_date": meta["base_date"], "score_task": bool(task.score), "agent": agent, "grade": grade} | ||
| "base_date": meta["base_date"], "score_task": bool(task.score), | ||
| "score_max": task.score["max"] if task.score else None, "agent": agent, "grade": grade} |
There was a problem hiding this comment.
No change. Tasks are validated before they run: manifest.py _check_task (repo validate) requires score to be {metric, max} with a numeric max, and grading.py already indexes task.score["metric"] on the same path. A malformed task is rejected at validate time rather than silently treated as an unknown top here.
antonwolfy
left a comment
There was a problem hiding this comment.
Review at fce566b. The five points from my earlier review are fixed: a collapse no longer reads as near_ceiling, grader-failed pairs stay out of floor, differing tops give null, and the runner and contract are pinned. Two things are left: the near_ceiling note still misreads a grader failure, and the new grader-failure and tops logic is only partly pinned by the tests.
What I ran, on a Linux review host at fce566b:
make ci-local: 19 passed, 0 failed, 4 skipped.- Reproductions for the code findings, with the LLM transport mocked (cells built with the
_cellhelper oftest_repo_compare.py). - A mutation run over the new code in
headroom(),_compare_modeland_task_values: 20 mutants, 9 of which passtest_repo_compare.pyandtest_repo_run.py(102 tests).
Checked, no issues
- Holm and p: unchanged;
headroomis still only a note. - Mixed sets: a gate
ceilingand a scorefloorin one batch are flagged separately, and the note wording matches the data. - Legacy cells: no
score_maxis never counted as the top; two legacy arms at 0 still givefloor. - Runner:
task.score["max"]cannot raise, because the manifest loader rejects a score withoutmax.
… near-ceiling signal Headroom now returns None when the one non-top baseline pair is a grader failure, and tops and failures are computed only for gate and score rows. Adds tests for failures on either arm, on one rep, and for mixed or unknown tops; fixes the changelog and docstring to match the rules. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A grader failure scores 0, which reads as the top when score.max <= 0: every cell failing on a max-0 score task was reported as ceiling. Floor and near_ceiling already skipped such rows; ceiling now does too, and the three per-branch checks collapse into one. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
score.max is what the oracle must reach, not a bound. A treatment scoring 90 against a baseline of 80 on max 80 (p 0.008, 8 up) was flagged ceiling, and one baseline at 70 made it near_ceiling. A pair counts as at the top only when both arms are there and tied. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
EPS on float means, the treatment side of floor, the two-task minimum of near_ceiling, a small loss, one known top per task, and no flag on cost or turns rows: each of these could be dropped with the suite still green. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
|
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 9b0c90e. The three fixes since fce566b are right: any grader failure gives null, a pair counts as at the top only when both arms tie, and the new tests pin EPS, the treatment side of floor, and the two-task minimum. Two points inline: a test gap in the tops wiring, which I'd fix before merge, and a nit on the contract text.
What I ran, on a Linux review host at 9b0c90e:
make ci-local: 19 passed, 0 failed, 4 skipped; changed-line coverage 100% (35 lines).- A mutation run over
headroom()and the_compare_modelwiring: 30 mutants, 7 of which passtest_repo_compare.pyandtest_repo_run.py(121 tests). Most are equivalent or sub-EPS; thetopswiring below is the one that matters.
Checked, no issues
- Earlier points:
any(failed)vsall(failed), failure on either side,>= 2,base_top.count(False) <= 1, the treatment-not-below check and theat_topEPS are all killed by the suite. - Past
score.max: 90 vs 80 on max 80 givesNone; 90 vs 90 givesceiling. NaN scores and a NaN or infscore_maxgive no flag. - Runner:
score_maxis written fromtask.score["max"], which the manifest check requires to be numeric. - Repo rules: the changelog fragment matches the code; no versioned schema covers
result.jsonor the compare output.
…ntract headroom text The mixed-tops test only covered the ceiling and near_ceiling paths, so reading the baseline's top for both arms passed the suite. Add a floor case where both arms score 0 and only the treatment's top differs. Also fix the garbled headroom sentence in the repo evaluation contract and say that non-gate rows carry null. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 6ffb878. Both threads from my last review are fixed: the tops wiring is pinned by the new floor test, and the contract sentence reads correctly. One test gap is left, and one nit on the changelog.
What I ran, on a Linux review host at 6ffb878:
make ci-local: 19 passed, 0 failed, 4 skipped; 5,210 tests; changed-line coverage gate passes.- A mutation run over
headroom(), the_compare_modelwiring,_task_valuesand therunner.pyscore_max: 53 mutants, 10 of which passtest_repo_compare.pyandtest_repo_run.py(122 tests). Six are equivalent or differ only at float noise; the four that matter are inline. - Offline reproductions of
headroom()on unknown tops, with the LLM transport mocked.
Checked, no issues
- Earlier points: the treatment's top in
floor,any(failed), the two-task minimum,count(False) <= 1, the tops-mismatch guard and therunner.pyscore_maxlink are all killed by the suite. - Holm and p: unchanged;
headroomis only appended to the row, and isnullon cost, turns and metric rows. - Repo rules: the changelog fragment is in the right place and format; no versioned schema covers
result.jsonor the compare output.
| return None # its 0 is not a measurement, at either end (a score.max of 0 would read it as the top) | ||
|
|
||
| def at_top(value: float, top: float | None) -> bool: | ||
| return top is not None and value >= top - EPS |
There was a problem hiding this comment.
The tolerances in headroom() are only pinned at exact values, so a 0.1 slack passes the full suite.
Mutation check: each of these passes all 122 tests in test_repo_compare.py and test_repo_run.py.
value >= top - EPS->value >= top - 0.1(compare.py:279): a gate task averaged over reps at 0.95 in both arms would read as at the top, so a comparison that still has room is flaggedceiling.p["treatment"] >= p["baseline"] - EPS->... - 0.1(compare.py:289): a treatment 0.05 below the baseline on a task no longer blocksnear_ceiling, which hides a small regression behind "too few can improve".abs(treatment - baseline) <= EPS->< 0.5(compare.py:282): a treatment 0.3 pastscore.maxis stillceiling.- gate top
1.0->0.5(compare.py:465): a gate task with a mean of 0.5 in both arms reads asceiling.
The existing cases use 0.6 of 1 and large drops, so only a coarse slack is caught.
Suggestion: add a parametrized headroom() case per line with values 0.05 to 0.1 from the boundary: both arms at 0.95 of 1 (None), baseline 1.0 with treatment 0.95 on two tasks (None), baseline 1.0 with treatment 1.3 on max 1 (None), and a gate with both arms at a rep mean of 0.5 on every task (None).
| @@ -0,0 +1,3 @@ | |||
| ### Added | |||
|
|
|||
| - **`repo compare` flags gate and score comparisons with no headroom** — each comparison gains `headroom`: `"ceiling"` when both arms are at the top (gate 1.0, the task's `score.max`) on every paired task, `"floor"` when both are at 0 on every one, `"near_ceiling"` when the baseline is at the top on all but at most one of 2+ paired tasks and the treatment is nowhere below the baseline, else `null`; arms whose tops differ (or are unknown), and a grader failure in either arm, get `null`. The text report prints a note under the comparison. A zero delta on such a task set is no headroom, not no effect. p values and Holm's correction are unchanged. `result.json` now records the task's `score_max`; score rows of batches run before this have no known top and are never flagged `ceiling`. | |||
There was a problem hiding this comment.
Nit: "arms whose tops differ (or are unknown) ... get null" overstates the rule. The code returns None only when the baseline's and treatment's tops differ. Two unknown tops are equal, so headroom([{0, 0}, {0, 0}], [(None, None)] * 2) still gives floor and only ceiling is withheld. The contract bullet is right ("never ceiling"). Suggestion: say "arms whose tops differ get null; a batch without a recorded top is never ceiling".


Summary
A zero delta with no p reads as "no effect". On a task set where both arms pass every paired task, it means the tasks had no room to move. On oneDAL a compile-check tool cut new build breaks from 5.9 to 0.4 per run with Haiku 4.5. With Haiku 5.5 and Sonnet 5.5, both arms passed 9/9, and
repo comparewould have shown that as no effect (#41, #65).Real output for 3 tasks × 3 reps, where both arms pass every cell:
guidance:raw vs guidance:none, model m-1 measure tasks baseline treatment delta [95% CI] p p_holm up/down/tied gate 3 1.000 1.000 +0.000 [+0.000, +0.000] - - 0/0/3 cost_usd 3 0.100 0.100 +0.000 [+0.000, +0.000] - - 0/0/3 turns 3 10.000 10.000 +0.000 [+0.000, +0.000] - - 0/0/3 + gate ceiling: both arms pass all 3 paired tasks; no headroom to show an effectresult.jsonnow recordsscore_max, so score rows know their top. Batches recorded before this change never getceilingon score rows; they can still getfloor.compare.pydocstring and the contract'srepo comparebullet. A changelog fragment is added.Evidence
Before: two arms that both pass every task show a zero delta, no p, and nothing else.
After:
tests/test_repo_compare.pycovers the cases:score.max, an unknown max (cells disagree), and floor that includes a grader failure.64 passed.
tests/test_repo_run.pyasserts thatresult.jsoncarriesscore_max.make ci-local: 0 failed, 19 passed, 4 skipped. The skipped jobs need docker or network, run only on PRs, or are semgrep.Merge Danger
Door: two-way
The change adds one JSON field to the
repo compareoutput and one toresult.json; nothing is removed or renamed.Blast Radius: small
Readers of
repo compare --jsonsee a newheadroomkey. Text output gains a line only under flagged comparisons.🤖 Generated with Claude Code