Repository navigation
feat(repo): compare records the start context, filters tasks, and flags inconclusive rows - #296
Conversation
An evaluation of AGENTS.md guidance files (26 review_replay tasks, two arms, three reps) showed three gaps in reading a batch with repo compare. Start context: a batch did not say what each cell started with, so whether an arm's guidance was delivered had to be read by hand from every agent.stdout. The Transcript now carries first_prompt_tokens, the input + cache_creation + cache_read tokens of the first main-thread call, read with dokimi.context.trace (one call per message id, subagent calls apart). repo run writes it as result.json agent.first_prompt_tokens (null until set, and for a trace without such a call), and repo compare compares it as a descriptive measure next to cost_usd and turns. Like a process count, it is compared only when both arms report it, so a batch written before the field has no row rather than a row of zeros. Task selection: repo compare --task ID (repeatable) compares only those task ids. An id no finished cell has is refused through CompareError; the tasks left out are not listed as unpaired nor their cells as missing; the summary records the selection as task_filter and the text header prints "tasks: N of M (--task)". Reading notes, with no number changed: a gate or score row of 2+ tasks whose interval includes 0 gets an "inconclusive" note with mde, the smallest effect 80% power detects at that spread, (1.959964 + 0.841621) * sd / sqrt(n) (mde is in every row of --json); a row whose interval and sign-flip p disagree gets interval_p_disagree and a note naming the two methods, saying for gate and score that p is the one to read; descriptive rows with p < 0.05 are named on one line as uncorrected and outside Holm's family. Contract sections 4 and 9, docs/context-window.md and the compare module docstring describe the field, the flag and the notes. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
CLI interface change detectedThis PR changes dokimi's user-facing CLI surface (commands and/or options): ~ `dokimi repo compare` parameters changed:
+ added option `--task`Updated on every push; the |
|
@exai-bot /review |
exai-bot
left a comment
There was a problem hiding this comment.
Note
AI-generated review of 30030ed1d020, requested by @antonwolfy, using the default review skill. It can be wrong: verify a finding before acting on it. React with a thumbs up or down on a finding to tell the bot whether it helped.
CI on this commit, at review start and at posting: 13 passed, 3 running, 2 skipped.
Model us.anthropic.claude-sonnet-5-5, effort default, 12 of 162 turns, 1.3 of 120 minutes, 528k in / 6k out tokens.
The change records first_prompt_tokens per cell and compares it as a descriptive measure, adds repo compare --task, and prints reading notes (mde, interval/p disagreement, uncorrected descriptive p) without changing any number. I read the diff against context/trace.py, compare.py and the test helpers and found no defects. The parse_trace reuse, the one-arm-reports skip rule, the task-filter handling of missing and unpaired, and the interval_p_disagree condition all behave as the description says. I could not run the tests, because pytest, numpy, ruff and mypy are not installed in the container and there is no network.
No issues found.
For a human reviewer
- Design call: the notes label a gate or score row inconclusive and give an
mdecomputed from the observed spread of the per-task deltas. Confirm that framing is what you want users to read. - Whether the interval/p disagreement note should fire on descriptive rows, since the t interval and the sign-flip p will often differ at small task counts and could add noise.
Repository checks
Each ran as the head of this pull request defines it.
| Check | From | Result | Note |
|---|---|---|---|
python scripts/check_growth.py |
AGENTS.md | not run | no shell call ran this command |
pytest tests/test_repo_compare_reading.py tests/test_repo_compare.py tests/test_import_architecture.py tests/test_repo_reach.py |
AGENTS.md | not run | pytest is not installed in the container and there is no network |
ruff check / mypy dokimi |
.github/workflows/ci.yml | not run | tools are not installed in the container and there is no network |
Not verified
I did not run the new tests (test_repo_compare_reading.py, test_repo_reach.py, test_repo_run.py) or the CI checks. I checked the bootstrap-interval expectations (lo > 0, hi = +0.250) against the code by reading only. python scripts/check_growth.py ran and printed only a note that origin/main has no growth baseline to compare against. I did not confirm compare.py stays under the complexity cap for _reading_notes.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #296 +/- ##
==========================================
+ Coverage 93.52% 93.58% +0.05%
==========================================
Files 274 277 +3
Lines 35625 35800 +175
Branches 6052 6070 +18
==========================================
+ Hits 33320 33504 +184
+ Misses 1489 1483 -6
+ Partials 816 813 -3
🚀 New features to boost your workflow:
|
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 11 selected item(s). Review details
|
Resolve the conflicts with intel#238: cmd_repo_compare passes --task and catches CellFileError too; _task_values reads first_prompt_tokens inside the new reading_cells block; compare.py keeps `import math` for min_detectable. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Review comment 4415626666: the one-arm skip of first_prompt_tokens was tested only with the baseline lacking it. The test now also compares a batch whose treatment lacks it and asserts the same warning and no unpaired entries, so a skip that checks only the baseline arm fails. Review comment 4416413441: the main call's message id now has a third, sparser usage line after the full one, so the first prompt is 19_380 only when the lines merge by maximum; "last line wins" gives 16_319 and fails the test, as "first line wins" already did. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
Review round in 00f9b40, tests only. Both OpenCodeReview points held: a mutant survived each at cad52dd, and both are caught now.
On the two design points exai-bot left for a human reviewer:
|
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
Review at 00f9b40. The first_prompt_tokens record and --task are right. The reading notes can still mislead in two cases: mde at task counts where the sign-flip test cannot reach 0.05, and gate and score rows read against the raw p instead of p_holm. Both are in the line comments.
What I ran, at 00f9b40:
make ci-local --offline: 17 checks passed, 0 failed, 13 skipped (network, docker and the other Pythons).paired_summaryon small gate sets:[1, 0]givesmde1.401 at p 1.0, and[1, 1, 0]gives 0.934 at p 0.5.comparewith two treatments and 6 of 7 gate tasks up: p 0.031, p_holm 0.062, interval [+0.571, +1.000], and no note.compare --task fixon a batch with tasksfixandfix__a: a missingfix__acell is still listed.
Checked, no issues
- Start context:
_claude_stream_jsontakes the first main-thread call fromparse_trace, so a message id's usage lines are merged field by field, ascontext tracedoes. A broken line goes intoerrorsthere and does not lose the cell's record. A killed cell writesnull. - Older batches:
first_prompt_tokensgoes through the same one-arm rule as the process counts. The tests now cover both directions and the sparse-later-line merge, as the resolved threads asked. - Numbers: no existing value changes;
mdeandinterval_p_disagreeare new keys, and nothing in the schemas covers the compare JSON. - Docs and changelog: the contract,
context-window.mdand the fragment describe the new fields and--task.
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 15 selected item(s). Review details
|
The inconclusive note gave mde with normal quantiles (1.96 + 0.84), but the interval beside it is a t interval at n-1 degrees of freedom. At 4 tasks the note said +-0.639 where the t quantiles give +-0.950; at 26 the gap is small. The contract's formula is updated to match. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…refix A task id may contain '__', so a missing cell of fix__a starts with fix__ and was counted as a cell of fix. The task is now split off the cell name from the right, as the arm, model and rep are the last three parts. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
With --task the arm and metric checks run on the selected cells, so an arm that ran only other tasks read as a wrong name. Those errors now add 'among the --task selection' when a selection is set. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The sentence bolded an orphan 'first'. It now names the first prompt. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
of is the tasks with a finished cell, the only ids --task can name, not the task count of the batches: a task whose cells all crashed before result.json is not in it. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
There was a problem hiding this comment.
🟡 Changes recommended
The detection-limit estimate misrepresents the exact sign-flip test’s small-sample capabilities, and ambiguous cell-name parsing silently omits unfinished cells when filtering by task.
2 open issues
What changed in this PR
2 new must-fix findings.
This PR improves dokimi’s repository A/B comparisons by recording initial prompt size, supporting task subsets, and adding guidance for interpreting uncertain results.
Changes:
- Records and compares first-main-thread prompt tokens, while preserving missing values for older batches.
- Adds repeatable
repo compare --taskfiltering with selection details in text and JSON. - Adds detection-limit estimates and notes for inconclusive results, interval/p-value disagreements, and uncorrected descriptive comparisons.
| File | Description |
|---|---|
dokimi/tests/test_repo_run.py |
Checks recorded prompt tokens and null defaults. |
dokimi/tests/test_repo_reach.py |
Tests first-call selection and merged usage records. |
dokimi/tests/test_repo_compare_reading.py |
Tests task filtering, token comparisons, and statistical notes. |
dokimi/tests/repo_eval_fixture.py |
Adds assistant-call usage to the fake harness. |
dokimi/dokimi/repos/runner.py |
Persists first-prompt token counts. |
dokimi/dokimi/repos/events.py |
Extracts initial prompt size through the shared trace parser. |
dokimi/dokimi/repos/compare.py |
Implements task filtering, token comparisons, and reading notes. |
dokimi/dokimi/commands/repo_run.py |
Exposes and forwards the repeatable --task option. |
dokimi/docs/repo-evaluation-contract.md |
Documents the new fields, filtering, and statistical guidance. |
dokimi/docs/context-window.md |
Documents first-prompt measurement in repository evaluations. |
dokimi/changelog.d/20261029_150000_antonwolfy_compare-reading.md |
Announces the new recording and comparison features. |
Review effort: Balanced
|
Head at review: 00f9b40 (unchanged on fetch, 10-29). Pushed sha: 525e9d2. Anton, the small fixes for Vlad's threads are on top of 00f9b40 and pushed as 525e9d2 (6 commits, signed off). Each fix has a test that fails on 00f9b40. Two of the threads are design questions, listed at the end, and are not changed. Fixed, with the commits (pushed shas):
Not changed, for you to decide:
Test note for you: |
repos/compare.py was 787 lines, and the review answers that follow (4418945747, 4420755924, 4418945753, 4420755898, 4418945763) take it past the 800-line limit of the growth ratchet. The --task selection (_select_tasks, _cell_task) moves unchanged into repos/compare_tasks.py, where the missing-cell fix of 4420755898 adds its code. An unknown id now raises TaskSelectionError there, which compare() re-raises as the same CompareError with the same message. No behaviour changes. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Answers review comments 4418945747 and 4420755924. The row's p is an exact sign-flip test, whose smallest p over k changed (non-tied) tasks is 2 / 2**k: at 5 or fewer changed tasks no effect of any size reaches p < 0.05, yet the inconclusive note named an effect that 80% power would detect (gate deltas [1, 1, 0]: p 0.5, mde 1.788; the four-task score row: mde 0.950, p floor 0.125). min_sign_flip_p gives that floor (1 / (N + 1) for the Monte Carlo test above 16 changed tasks). Where it is 0.05 or more, mde is null in every row, and the gate or score note says instead "with 2 changed tasks the sign-flip p cannot go below 0.5: no effect can reach p < 0.05 here". From 6 changed tasks on, mde is computed as before. The module docstring, the contract and the changelog fragment say so, and no longer claim the notes change no number: they change no delta, interval or p. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Answers review comment 4418945753. interval_p_disagree was set in paired_summary, before _holm ran, so a gate or score row compared its interval with the raw p while p_holm is what decides it. With two treatments and gate deltas six +1 and one tie, each row printed +0.857 [+0.571, +1.000], p 0.031, p_holm 0.062 and no note. _holm now sets interval_p_disagree on the gate and score rows from Holm's verdict (correct_family, unrounded against 0.05), and their note quotes p_holm and says to read p_holm. Descriptive rows keep the flag on their raw p. The module docstring, the contract and the changelog fragment say so. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Answers review comment 4420755898 (and finishes 4418945759). 349d544 took a missing cell's task from its name, split from the right, which assumed the arm and model slugs carry no "__". They can: runner.cell_name keeps the "__" of a model (cell_name("fix", "guidance:raw", "m__v1", 2) is fix__guidance-raw__m__v1__r2, read as task fix__guidance-raw) and arms.slug keeps that of an arm such as skill:lint__v2, so --task fix silently left such an unfinished cell out of the missing list. The task now comes from the cell's meta.json, which repo run writes with the task id when the cell starts, read with the same guarded read as the module's other cell files. A cell only plan.json names (no files) gets the longest task id of its batch's finished cells that its name starts with followed by "__". A cell neither gives a task for is missing only without --task. The name-splitting _cell_task and its comment about slugs are gone. The contract and the changelog fragment say how the task is found. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Answers the second half of review comment 4418945763. Without --arm,
compare takes every other arm that has a finished cell among the
--task selection, so an arm that ran none of the selected tasks dropped
out of the comparison with no word.
task_filter now carries dropped_arms: without --arm, the arms of the
batches with no finished cell among the selection ([] with --arm, where
such an arm is refused). The text header prints them under the tasks
line ("not compared, no finished cell among the --task selection:
guidance:raw"). The default arm list is one helper, _other_arms, for
both. The module docstring, the contract and the changelog fragment say
so; the fragment also says what M of "tasks: N of M" counts (the tasks
with a finished cell), which e00edbc said in the contract only
(4418945768).
To keep repos/compare.py under the 800-line limit, CompareError moves
into compare_tasks.py, which now raises it for an unknown --task id
directly; compare.py imports it, so dokimi.repos.compare.CompareError
is unchanged for its callers.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
|
Review round in eb5fbce..68b4c86, on top of the fixes in 0c7d61f..525e9d2. It settles the questions those left open:
Every thread is answered and resolved; the Testing section has the mutants and |
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
The merge of main brought intel#254's flag registry and its snapshot check: docs/reference/cli-interface.json lacked the --task option this branch adds to `repo compare`, so test_the_cli_interface_snapshot_is_current failed. Regenerated with scripts/cli_surface.py dump (`make cli-snapshot`). --task needs no allowlist row: it is already a canonical spelling of the registry's `inputs` concept ("a filter on --tasks", text, repeatable), which is what the compare option is (an append of task ids). intel#298's check_comment_blocks.py --base origin/main --head HEAD finds no comment block over two lines among the lines this branch adds or edits, so no comment changes. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Review comment 4422703568: mde and the inconclusive note read the raw sign-flip floor, but gate and score rows are decided by p_holm. Two score treatments with six changed tasks each both got an mde and an "80% power detects" note, though Holm over the two rows cannot take either p_holm below 2 * 2/64 = 0.0625. After Holm, each gate or score row gets p_holm_floor: its p_holm with its own p put at its floor (min_sign_flip_p over its changed tasks) and the family's other p as observed, from correct_family. Where that is not below 0.05, mde is null and the inconclusive note says "with Holm over N rows, p_holm cannot go below X here: no effect can reach p_holm < 0.05". A family of one row is the raw rule, as before; descriptive rows keep it. min_detectable, ALPHA and POWER move to the new repos/compare_reach.py with the floor helper, holm_floor, so compare.py stays under 800 lines; compare re-imports them. Tests pin the two-treatment case (both rows null, the Holm note) and one where a row of eight changed tasks keeps its mde beside a six-task row that loses it. The module docstring, contract §9, architecture.md and the changelog fragment follow. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Review comments 4422911586 and 4422911592: `mde` is computed as (t(0.975, n-1) + t(0.80, n-1)) * sd / sqrt(n), the effect a one-sample t test at the uncorrected 5% level detects with 80% power. It is not the power of the sign-flip test nor of the Holm-corrected decision: with two rows and the other row's p at 1, Holm needs this row's raw p below 0.025, and the figure is still the one-row value. `p_holm_floor` only decides whether `mde` is given. The inconclusive note now reads "an uncorrected t-test estimate puts the effect 80% power detects at this spread at about ±X (N tasks), before Holm and not for the sign-flip test". The `compare.py` module docstring, `compare_reach.min_detectable`, contract section 9, architecture.md and the changelog fragment say the same; the contract says why no Holm-calibrated figure is given (it would depend on the other rows' observed p, which a planning figure cannot know). How `mde` is computed and every JSON key and value are unchanged; the two tests that assert the note text take the new wording. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
LGTM
I checked fda7c07. Everything from my earlier rounds still holds. The Holm gap from the last round is fixed: a gate or score row whose p_holm cannot drop below 0.05, even with its own p at its floor, now has no mde and a note that names Holm. mde is labelled an uncorrected t-based planning figure in the note, the contract and the changelog.
What I ran, at fda7c07:
make ci-local --offline: 19 checks passed, 0 failed, 13 skipped (network, docker and the other Pythons). This includes the CLI snapshot and the file length report (compare.pyat 800 lines).- The two-treatment score case from the exai-bot thread: both rows now print
with Holm over 2 rows, p_holm cannot go below 0.0625 here, andmdeis null. - My earlier repros again: small gate sets give
mdenull, thep_holmdisagreement note still fires, and--taskstill keeps the right missing cells. - The new
test_repo_compare_reading.pyon 68b4c86: the three Holm-floor and wording tests fail there and pass here.
Checked, no issues
- Holm floor:
holm_floorrunscorrect_familywith only this row's p moved to its floor. So a row that would be ranked later in Holm's order can still reach 0.05 next to a row with a very small p, and keeps itsmde. A family of one row falls back to the raw rule. - Merge from main: nothing on main touched
compare.py, and the CLI snapshot now has--task. - Docs and changelog: the contract, the module docstring,
architecture.md(the newcompare_reach.py) and the fragment match the code. The new file has the SPDX header.

Summary
Found while evaluating IntelPython/dpnp#3099 (AGENTS.md guidance) with dokimi: 26
review_replaytasks, two arms (guidance:noneandguidance:rev:<sha>), 3 reps, Claude Code, 156 cells. Reading that batch withrepo compareran into three gaps.maingivesagent.stdout: median 19,380 tokens against 16,315 forguidance:none(each ±20 over 78 cells), +3,065. That matches the rootAGENTS.md(7,015 chars) and none of the 7 nested files (21,199 chars), which load only lazily.result.jsonagenthas whole-run token totals only, andrepos/events.pyreads the assistant messages but not theirusage.repo runhas--task;repo comparehas none, so every statistic of the split was recomputed by hand.recall +0.022 [-0.066, +0.100], p 0.62, 26 tasks(1.96 + 0.84) * 0.222 / sqrt(26): the result is inconclusive.metric:n_findings+0.89, t interval [-0.091, +1.860], sign-flip p 0.0497: the report quoted "p = 0.049" for a descriptive row outside Holm's family, one of about a dozen comparisons. The score row over the 6 post-cut tasks no guidance commit named: +0.139, bootstrap [+0.028, +0.250], which excludes 0, while the exact sign-flip p is 0.25 (three non-zero deltas, all positive, so p cannot go below 0.25).Related: #292 asks for an interval that agrees with the sign-flip p by construction; the disagreement note here is the stopgap until then. #290 asks
repo compareto flag tasks a guidance revision may have seen;--taskis how the rest get compared on their own.Changes
repos/events.py:Transcript.first_prompt_tokens, theinput + cache_creation + cache_readtokens of the first main-thread call (parent_tool_use_idnull) of a Claude Codestream-jsontrace. It is read withdokimi.context.trace.parse_trace, so a call is counted once by message id and its blocks merge ascontext tracedoes; there is no second implementation.nullwhen the trace has no such call, and for thetextformat.repos/runner.py:result.jsonagent.first_prompt_tokens(nulluntil set, like the other token fields).repos/compare.py:first_prompt_tokensis a descriptive measure next tocost_usdandturns(per-task mean over the cells that have it). Like aprocesscount it is compared only when both arms report it for some shared task: a batch written before the field has no row, not a row of zeros, and when only one arm has it a warning names it.repo compare --task ID, repeatable (commands/repo_run.py,compare_tasks.select_tasks): compares only those task ids. An id no finished cell of the batches has is refused (exit 1,✗ no finished cell of the compared batches has --task t9). The tasks left out are not listed as unpaired and their cells not as missing. A missing cell's task is the one itsmeta.jsonrecords; a cell onlyplan.jsonnames gets the longest task id of the batch's finished cells that its name starts with followed by__, and a cell neither gives a task for is missing only without--task.--jsonrecords the selection astask_filter({"tasks": [...], "of": <tasks with a finished cell>, "dropped_arms": [...]},nullwithout--task): without--arm,dropped_armsnames the arms with no finished cell among the selection, which drop out of the comparison. The text header printstasks: N of M (--task)and, when an arm dropped out,not compared, no finished cell among the --task selection: guidance:raw.compare._reading_notes), after thechanged:and headroom notes. No delta, interval, p or table column changes:score inconclusive: the interval includes 0; an uncorrected t-test estimate puts the effect 80% power detects at this spread at about ±0.676 (6 tasks), before Holm and not for the sign-flip test.mde = (t(0.975, n-1) + t(0.80, n-1)) * sd / sqrt(n)over the per-task deltas (sample SD), a planning figure (an approximate, uncorrected t-based estimate, not the power of the sign-flip test nor of the Holm-corrected decision), is in every--jsonrow,nullfor one task or deltas with no spread. It isnulltoo where the sign-flip p cannot reach 0.05: over k changed tasks it cannot go below 2 / 2**k, so at 5 or fewer, and the note says so instead:gate inconclusive: the interval includes 0; with 2 changed tasks the sign-flip p cannot go below 0.5: no effect can reach p < 0.05 here. A gate or score row is decided byp_holm, so its reach is read after Holm's correction (compare_reach.holm_floor, withcorrect_family):p_holm_flooris the row'sp_holmwith its own p put at that floor and the family's other p as observed, and where it is not below 0.05,mdeisnulland the note names Holm: two score treatments of six changed tasks each printscore inconclusive: the interval includes 0; with Holm over 2 rows, p_holm cannot go below 0.0625 here: no effect can reach p_holm < 0.05.p_holm_floordecides only whethermdeis given, not its size. A family of one row is the raw rule; descriptive rows keep it;interval_p_disagree: trueand a note. For a gate or score row the p isp_holm, the one that decides it, set once Holm's correction has run. On the 6-delta case above it printsscore: the interval [+0.028, +0.250] excludes 0 but p_holm 0.25 is ≥ 0.05; they come from two methods, a percentile bootstrap, too narrow over few tasks, and a sign-flip test; read p_holm. A descriptive row compares its raw p, names the t interval instead and does not say "read p_holm". On a gate or score row that has this note, the inconclusive note is not printed too;p < 0.05 on turns: descriptive rows, their p uncorrected and outside Holm's family, which covers gate and score only.agent.first_prompt_tokens) and §9 (the measure,--task, the three notes,mdeandp_holm_floor), a line indocs/context-window.md,repos/compare_tasks.pyandrepos/compare_reach.pyindocs/architecture.md, and thecompare.pymodule docstring.docs/reference/cli-interface.json, the CLI snapshotmain's test(cli): flag registry, ratchet contract test, CLI snapshot (#94 F1a) #254 checks, regenerated withmake cli-snapshotforrepo compare --task. The flag needs no allowlist row:--taskis already a canonical spelling of theinputsconcept indokimi/cli_flags.py(ID, text, repeatable, "a filter on --tasks").tests/test_repo_compare_reading.py(test_repo_compare.pyis 955 lines, near the 1000-line limit for a test file), one trace test intests/test_repo_reach.py, and the agent-record tests intests/test_repo_run.py. The fake harness intests/repo_eval_fixture.pyputsusageon itsFAKE_USAGEassistant message.Risk
result.jsonagentgains one key; readers that take the dict as it is see one more field. No schema covers this file.repo compare --jsonrows gainmde,p_holm_floorandinterval_p_disagree, and the summary gainstask_filter(tasks,of,dropped_arms). Every existing key and value is unchanged.first_prompt_tokensrow for batches run from this change on. Older batches print as before apart from the notes.repo runparses each Claude Code trace a second time (incontext.trace) to get the first call. That is linear in the trace size and runs once per cell.repos/compare.pygrows from 696 to 800 lines (limit 800); the--taskselection andCompareErrorare in the newrepos/compare_tasks.py(52 lines),min_detectable,ALPHA,POWERand the Holm floor in the newrepos/compare_reach.py(32 lines), anddokimi.repos.compare.CompareErroranddokimi.repos.compare.min_detectablestill import. Open PR fix(model): one same-model rule for baseline, self-judge and repo compare #273 also editscompare.py. This change edits only the parts above and reformats nothing around them; merged together, the file will need a further split to stay under the limit.Testing
On a Linux review host, at
30030ed(onmaine966422):make ci-local: 27 passed, 1 failed, 7 skipped. Skipped: external-catalog-smoke (needs docker), test-latest, docs deploy and pip-audit report (job conditions false on a PR), cli-surface packaging (unknown expression locally), conda-forge-deps (no micromamba), semgrep secrets (not installed). The failure ispip-audit:Found 10 known vulnerabilitiesinfilelock 3.32.7(fixes in 4.0.9 / 4.2.1). That version is pinned inmain's lock, which this branch does not touch; ci(deps): document and ignore ten more filelock 3.32.7 advisories #288 documents and ignores those advisories. 10196 tests passed and 4 were skipped; changed-line coverage 100% (42 lines). The cli-surface step reports the one interface change:dokimi repo compare, added option--task.test_repo_compare_reading.py,test_repo_compare.py,test_repo_compare_paths.py,test_repo_reach.py,test_repo_run.py,test_repo_process_counts.py,test_repo_uptake.py,test_repo_skill_arms.py,test_context_trace.py): 456 passed. Guard tests (test_docs_code_refs.py,test_growth_ratchet.py,test_audit_test_quality.py,test_import_architecture.py): 290 passed.ruff checkon the changed files andmypyon the whole package (278 files) are clean.main, the new tests fail:test_repo_compare_reading.pystops at import (min_detectabledoes not exist), andtest_first_prompt_tokens_is_the_first_main_thread_call_counted_onceand threetest_repo_run.pyagent-record tests fail. All of them pass here.context.trace's block merge dropped;null;first_prompt_tokensdropped from_task_values, or from the both-arms rule;--task: the unknown-id check off, every cell kept, missing cells not filtered, the header line dropped, the CLI not passing the flag;Z_POWERset to 0, orstdevswapped forpstdev;repo runagainst a model, since this host makes no model calls. The dpnp numbers above come from that evaluation's batch, which is not on this host.mainmerged in after fix(cli): run, compare, arms, tasks, explore and repo exit with a message on bad input or output #238 (cad52dd). The conflicts were incmd_repo_compare, which now passes--taskand also catches fix(cli): run, compare, arms, tasks, explore and repo exit with a message on bad input or output #238'sCellFileError, and in_task_values, where thefirst_prompt_tokenslist moved into fix(cli): run, compare, arms, tasks, explore and repo exit with a message on bad input or output #238'sreading_cellsblock.compare.pykeepsimport mathformin_detectableand is 770 lines. On that commit, the compare, run, reach and context-trace tests with the guard tests: 644 passed;ruffandmypyare clean on the merged files.mainf0f95a2with the other PRs from this evaluation (fix(repo): scan says why it is incomplete #295 at4fa7783, feat(repo): compare records the start context, filters tasks, and flags inconclusive rows #296 atcad52dd, feat(repo): replay grades record each credited hit; repo report --hits lists them #297 at0a7539a, fix(novelty): a capped run says how far the cap reached #299 atfa453d8), on a workstation: the full suite has 10482 passed and 57 skipped, and the same 5 failures and 1 collection error asmainalone there (10456 passed). All six come from that machine's setup: nodocker, nohypothesis, and two tests that depend on its git and shell settings.00f9b40(test only):test_first_prompt_tokens_of_an_older_batch_is_no_row_not_zeronow also checks a batch where only the baseline reportsfirst_prompt_tokens, with the same warning and no unpaired entries.test_first_prompt_tokens_is_the_first_main_thread_call_counted_oncegives the main call a third, sparser usage line after the full one. Before the change, two mutants passed these tests: the one-arm skip checking only the baseline arm (if not has[0]), and "last line wins" incontext.trace's merge. Each now fails, and "first line wins" fails as well: 3 of 3 caught. An unmutated copy passes both files (100 tests).make ci-local: 25 passed, 3 failed, 7 skipped. 10528 tests passed and 4 were skipped; changed-line coverage 100% (43 lines). Two of the failures are gitleaks: its download got HTTP 504 four times, so Scan commits had no binary (exit 127). The third ispip-audit, the samefilelock 3.32.7finding as before; that version comes frommain's lock. The skips are the same 7 as before.eb5fbce,5eff69d,aeea5f1,d986407,68b4c86, on a Linux review host, at68b4c86(onmainf0f95a2).eb5fbcemoves the--taskselection, without behaviour change, into the newrepos/compare_tasks.pysocompare.pystays under 800 lines (799 now).5eff69d:mdeisnullwhere the sign-flip p cannot reach 0.05 (2 / 2**k ≥ 0.05, at 5 or fewer changed tasks), and the note says how low p can go.aeea5f1: a gate or score row'sinterval_p_disagreeand its note readp_holm.d986407: a missing cell's task comes from itsmeta.json, else the longest known task id its name starts with.68b4c86:task_filter.dropped_armsand a header line name the arms--taskleaves without a cell. New tests intest_repo_compare_reading.py:test_no_mde_where_the_sign_flip_p_cannot_reach_005(the four-task score row, and five changed of six),test_a_gate_row_of_two_changed_tasks_has_no_mde([1, 1, 0]),test_a_gate_interval_against_p_holm_is_flagged,test_an_interval_across_zero_against_a_raw_p_below_005_is_not_flagged_when_p_holm_is_not(bootstrap_cireplaced by one spanning 0: in 7,975 random score rows with a sign-flip p in [0.025, 0.05), no bootstrap interval included 0),test_task_reads_a_missing_cell_s_task_from_its_meta_or_the_known_ids(modelm__v1, armskill:lint__v2, tasksfix,fix__a,fix__guidance-raw),test_task_names_an_arm_that_ran_none_of_the_selected_tasks.test_an_interval_across_zero_is_inconclusive_with_its_mdenow uses six changed tasks. At525e9d2the new and changed tests fail (11 of 19 in the file) and here they pass. Mutants, each on a copy of68b4c86against the tests that pin it: 13 of 13 failed (the floor check dropped, counted over all tasks, doubled; the floor note dropped; the Holm flag not set, or set from the raw p; the note quoting the raw p;meta.jsonignored, the plan fallback dropped, the shortest id taken;dropped_armsalways empty, filled under--arm, not printed). An unmutated copy passes (145 tests). The fixes from the colleague's commits hold: with the prefix match put back (349d544) or the--taskscope suffix removed (d5f2d7c), their tests fail.make ci-local: 27 passed, 1 failed, 7 skipped. 10538 tests passed and 4 were skipped; changed-line coverage 100% (100 lines). The failure ispip-audit, the samefilelock 3.32.7finding as before (10 known vulnerabilities, 3 ignored);mainignores those advisories since ci(deps): document and ignore ten more filelock 3.32.7 advisories #288, which this branch predates, and CI tests the merge withmain. The skips are the same 7 as before.c19f9d2,63c112e, on a Linux review host, at63c112e(onmain83f18fb, merged into the branch at8fa6756).c19f9d2regeneratesdocs/reference/cli-interface.jsonforrepo compare --task: at8fa6756,test_the_cli_interface_snapshot_is_current(test(cli): flag registry, ratchet contract test, CLI snapshot (#94 F1a) #254) failed on the stale snapshot and passes now.--taskneeded no allowlist row, being a canonical spelling of the registry'sinputsconcept, and ci: fail comment blocks over two lines that a PR adds or edits #298'scheck_comment_blocks.py --base origin/main --head HEADfinds no comment block over two lines in the branch.63c112e: a gate or score row'smdeisnullwhere Holm keeps itsp_holmfrom 0.05 even with its p at the sign-flip floor (p_holm_floor,compare_reach.holm_floor), and the note names Holm. New tests intest_repo_compare_reading.py:test_no_mde_where_holm_over_the_family_keeps_p_holm_from_005(two score treatments of six changed tasks each: bothmdenull,p_holm_floor0.0625, the Holm note twice) andtest_a_row_holm_still_lets_reach_005_keeps_its_mde(eight changed tasks,p_holm_floor0.0156, keeps itsmdebeside a six-task row that loses it). At8fa6756both fail (anmdeof 0.676 wherenullis due; nop_holm_floor) and here they pass. Mutants, each on a copy of63c112eagainst that file: 5 of 5 failed (themdereset dropped; the floor taken without the family; the floor not put in, the observed p used; the Holm note dropped; the family counted over every row).make ci-local: 28 passed, 2 failed, 7 skipped. 10661 tests passed and 4 were skipped; changed-line coverage 100% (112 lines); the CLI snapshot, comment-block andpip-auditsteps pass. The two failures are gitleaks: its download got HTTP 504 four times, so Scan commits had no binary (exit 127). A gitleaks already on the host scanned the branch's commits instead: no leaks found. The skips: external-catalog-smoke (needs docker), test-latest, docs deploy and pip-audit report (job conditions false on a PR), cli-surface packaging (unknown expression locally), conda-forge-deps (no micromamba), semgrep secrets (not installed). Not yet run merged together with the other follow-up branches.fda7c07, on a Linux review host, atfda7c07(onmain83f18fb).fda7c07(docs and note text only; howmdeis computed and every JSON key and value are unchanged) namesmdefor what it is: an approximate, uncorrected t-based planning estimate, the effect a one-sample t test at the uncorrected 5% level detects with 80% power at the row's spread, not the power of the sign-flip test nor of the Holm-corrected decision. The note now readsscore inconclusive: the interval includes 0; an uncorrected t-test estimate puts the effect 80% power detects at this spread at about ±0.487 (8 tasks), before Holm and not for the sign-flip test. Thecompare.pymodule docstring,compare_reach.min_detectable, contract §9,docs/architecture.mdand the changelog fragment say the same, and §9 says why no Holm-calibrated figure is given. Checked first at63c112e: in the eight-task case oftest_a_row_holm_still_lets_reach_005_keeps_its_mde, both raw p are 1.0 and the row'smdeis 0.487, the one-row value, while Holm over the two rows passes a raw p of 0.024 (p_holm0.048) and not 0.026 (0.052). Changed tests:test_an_interval_across_zero_is_inconclusive_with_its_mdeandtest_a_row_holm_still_lets_reach_005_keeps_its_mdeassert the new wording. At63c112e(the old wording) both fail and here they pass. Mutant, on a copy offda7c07: "uncorrected" dropped from the note, both fail; 2 of 2 caught.compare.pyis 800 lines (limit 800).make ci-local: 28 passed, 2 failed, 7 skipped. 10661 tests passed and 4 were skipped; changed-line coverage 100% (112 lines). The two failures are gitleaks: its download got HTTP 504, so Scan commits had no binary (exit 127). The installed gitleaks scanned the branch's commits instead: no leaks found. The skips: external-catalog-smoke (needs docker), test-latest, docs deploy and pip-audit report (job conditions false on a PR), cli-surface packaging (unknown expression locally), conda-forge-deps (no micromamba), semgrep secrets (not installed). Not yet run merged together with the other follow-up branches.Revert
git revertof the commit. Cells written in the meantime keepagent.first_prompt_tokensinresult.json; readers before this change ignore the key.Checklist
pytestpassesruff,mypy, and schema checks passAGENTS.md.github/.licenserc.yaml)