Repository navigation
fix(repos): an unreadable result.json is that cell's error in repo run - #304
Conversation
Settles the open part of review point B3 item 2 on intel#238: `repo run --batch X` on a batch whose finished cell holds a truncated result.json ended the whole batch with a bare JSONDecodeError, because run_cell read the file with json.loads and one() does not catch ValueError. intel#238 narrowed the changelog; this changes the behaviour. run_cell now reads a finished cell through _finished_result, which raises UnreadableResult (a PrepError, so one() already reports it per cell) when the file is not UTF-8, not JSON or not a JSON object. The message names the file and says how to recover: remove it to run the cell again, or rerun with --force. The cell is logged as `✗ <cell>: <path>: ...` and counted in the summary's crashed cells, the other cells still run or are reused, and the command exits 1 as for any other crashed cell. The file is neither changed nor removed, and the cell is not run again on its own. check_batch_executions already skipped an unparsable result.json; it now also skips one that is not an object, which it used to count as an `unrecorded` execution, refusing the whole batch up front. A readable result is reused as before. The contract's paragraph on rerunning a batch gains a sentence on this case, and a Fixed changelog fragment describes it. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
There was a problem hiding this comment.
🟡 Changes recommended
Corrupt retained results can be counted as completed on hosts that lack required CPUs, bypassing validation and per-cell error reporting even with --force.
1 open issue
What changed in this PR
1 new finding.
This follow-up to #238 makes malformed finished-cell results report per-cell errors instead of stopping an entire repo run batch.
Changes:
- Adds shared result parsing for UTF-8, JSON syntax, and object-shape validation.
- Adds regression tests covering error reporting, continued execution, and preservation of corrupt files.
- Updates the rerun contract and changelog.
| File | Description |
|---|---|
dokimi/tests/test_repo_run_unreadable_result.py |
Tests malformed results alongside reused and unfinished cells. |
dokimi/dokimi/repos/runner.py |
Adds result validation and integrates it with batch checks and cell reuse. |
dokimi/docs/repo-evaluation-contract.md |
Documents per-cell errors and recovery options. |
dokimi/changelog.d/20261030_120000_antonwolfy_repo-run-unreadable-result.md |
Records the fix and file-preservation behavior. |
Review effort: Balanced
| except (OSError, UnreadableResult): # no execution to compare; a cell of this run reports it in run_cell | ||
| continue |
There was a problem hiding this comment.
Fixed in 84c92bb, as a new commit on top. The unsupported-task branch of run_batch.one moves into a helper, _unsupported_cell. When the cell has a result.json, the helper reads it through _finished_result. On UnreadableResult (or an OSError reading it), one() records the cell in state["errors"] and logs ✗ <cell>: <path>: ... the same way the PrepError handler does. The file stays as it is, and the cell is neither rerun nor marked unsupported. A readable kept result is still counted as already done.
On this path --force keeps the file, so the message's recovery hint differs from run_cell's: "this host cannot run the task, so --force keeps the file; remove it, and the cell runs on a host that meets the task's requirements". The comment on the check_batch_executions catch now says the cell is reported in run_cell, or in _unsupported_cell for a task this host cannot run.
New test: test_an_unreadable_kept_result_of_a_task_this_host_cannot_run_is_that_cells_error, with and without --force. Both fix_race cells finish on a capable host. Then r1's result.json is replaced with [] and r2's is cut in half, and the batch is rerun on a 2-CPU AMD host. Both cells are reported by path, the run exits 1, both files are unchanged, and no unsupported.json is written. The test fails if the branch goes back to counting any existing file as done.
The contract paragraph and the changelog fragment now cover this case.
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 4 selected item(s). Review details
|
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #304 +/- ##
=======================================
Coverage 93.54% 93.55%
=======================================
Files 275 275
Lines 35701 35726 +25
Branches 6053 6055 +2
=======================================
+ Hits 33398 33423 +25
Misses 1486 1486
Partials 817 817
🚀 New features to boost your workflow:
|
Answers the review on intel#304: run_batch counted a finished cell of a task this host cannot run as already done as soon as its result.json existed, without reading it. check_batch_executions skips an unreadable file, so a result.json replaced with [] or cut in half was never reported there, even with --force, which keeps those cells. The unsupported-task branch of run_batch moves into _unsupported_cell. A kept result.json is now read through _finished_result; when it is not UTF-8, not JSON or not a JSON object (or cannot be read at all), one() records it in the run's errors and logs `✗ <cell>: <path>: ...` like any other per-cell error, and the command exits 1. The file is left as it is, and the cell is neither rerun nor marked unsupported. The message says that --force keeps the file on this host and that removing it lets the cell run on a host that meets the task's requirements. A readable kept result is counted as already done, as before. The comment on the check_batch_executions catch now names both places that report the cell, and the contract and changelog fragment cover the unsupported-task case. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>

Summary
Follow-up to the review of #238, where
repo run --batch Xon a batch whose finished cell holds a truncatedresult.jsondies with a bareJSONDecodeErrorinstead of reporting that cell (review, item 2 of B3). #238 narrowed its changelog; this changes the behaviour.On
main,run_cellreads a finished cell'sresult.jsonwith a barejson.loads, andrun_batch's per-cell handler does not catchValueError, so the error ends the whole command. Rerunning a two-rep batch (--arm guidance:raw --task fix_add --reps 2) after writing a badresult.jsoninto the r1 cell:result.jsonmain{not json✗ Expecting property name enclosed in double quotes: line 1 column 2 (char 1), no summary line, exit 1✗ fix_add__guidance-raw__m-1__r1: runs/b1/fix_add__guidance-raw__m-1__r1/result.json: not valid JSON (Expecting property name …); remove it to run the cell again, or rerun with --force, which replaces every cell of this run's plan, then0 ran, 1 already done, 1 crashed, …, exit 1\xff{}(not UTF-8)✗ 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte, exit 1✗ fix_add__guidance-raw__m-1__r1: …/result.json: not valid UTF-8 text; remove it …, the other cell reused, exit 1[]✗ runs/b1 holds cells of another execution (unrecorded) than this run (…); use another --batch, or --force to replace them, exit 1✗ fix_add__guidance-raw__m-1__r1: …/result.json: not a JSON object; remove it …, the other cell reused, exit 1A finished cell of a task this host cannot run (its
hostrequirements unmet) takes another path.run_batchkeeps that cell, even with--force, and onmaincounts it as already done because itsresult.jsonexists, without reading it. Here bothfix_racecells finished on a capable host, then the batch was rerun with--task fix_add --task fix_race --reps 2on a 2-CPU AMD host:fix_racecellsmain0 ran, 4 already done, 0 crashed, …, exit 0 (with--force:2 ran, 2 already done, …, exit 0)✗ fix_race__guidance-raw__m-1__r2: …/result.json: not valid JSON (Unterminated string …); this host cannot run the task, so --force keeps the file; …, then0 ran, 3 already done, 1 crashed, …, exit 1[], r2 cut in half,--force✗ runs/b1 holds cells of another execution (unrecorded) than this run (…), exit 1✗ fix_race__guidance-raw__m-1__r1: …/result.json: not a JSON object; this host cannot run the task, so --force keeps the file; remove it, and the cell runs on a host that meets the task's requirements,✗ fix_race__guidance-raw__m-1__r2: …/result.json: not valid JSON (Unterminated string …); …, then2 ran, 0 already done, 2 crashed, …, exit 1With this PR's first commit alone,
check_batch_executionsskipped the[]file as well, so the second row printed2 ran, 2 already done, …and exited 0 (review comment); the second commit reports both cells.Changes
dokimi/dokimi/repos/runner.py:run_cellreads a finished cell through a new_finished_result, which raisesUnreadableResult(aPrepError, whichrun_batchalready reports per cell) when the file is not UTF-8, not JSON or not a JSON object. The message starts with the file's path and names both ways back: remove the file, or--force. The cell is logged as✗ <cell>: <path>: ..., counted among the crashed cells, and the command exits 1 as for any other crashed cell; the other cells still run or are reused. The file is neither changed nor removed, and the cell is not rerun on its own.run_batch's branch for a task this host cannot run moves into_unsupported_cell, which reads a keptresult.jsonthrough the same helper. When that file cannot be read,run_batchrecords the cell's error and logs it the same way, with or without--force. The message says--forcekeeps the file on this host and that removing it lets the cell run on a host that meets the task's requirements. The file stays as it is, and the cell is neither rerun nor marked unsupported. A readable kept result still counts as already done.check_batch_executionsreads through the same helper: as before, it skips aresult.jsonit cannot parse, and now it also skips one that is not an object. It used to count that as anunrecordedexecution and refuse the whole batch. Its comment names where such a cell of this run is reported:run_cell, or_unsupported_cell.dokimi/tests/test_repo_run_unreadable_result.py(new;tests/test_repo_run.pyis at the 1000-line cap):result.jsonis cut in half, a JSON list or not UTF-8, r2 is finished and r3 is missing. The rerun logs r1 by path, reuses r2, runs r3 (one agent started), prints1 ran, 1 already done, 1 crashed, exits 1 and leaves r1's file byte for byte.--force. One finished cell'sresult.jsonis[]and the other is cut in half. Both cells are logged by path, the run exits 1, both files are unchanged and nounsupported.jsonis written. The test reusestest_repo_host's fake host probe.dokimi/docs/repo-evaluation-contract.md: the paragraph on rerunning a batch gains sentences on both cases.Risk
Only a batch with a
result.jsonthat is not a readable JSON object behaves differently. Its other cells now run instead of the command stopping. A non-objectresult.jsonoutside this run's plan no longer refuses the batch asunrecorded; it is skipped, as an unparsable one already was. A kept cell of a task this host cannot run, whoseresult.jsoncannot be read, now fails the run (exit 1) wheremaincounted it as already done and could exit 0. Every case in the tables above exits 1 on this branch. A readable result is reused or refused exactly as before.Testing
On a Linux review host, at
84c92bb(onmaine97ff7a):make ci-local: 30 passed, 0 failed, 7 skipped (external-catalog-smoke, which needs Docker;test-latest,docs.yml:deployandpip-audit.yml:report, whose job conditions are false on a PR;cli-surface / Package the result;conda-forge-deps, no micromamba; semgrep secrets, offline). 10656 tests passed and 4 were skipped; changed-line coverage was 100% (32 lines measured). The matrix legs on other Pythons (3.11, 3.12, 3.14) were not run.mainthe batch ends with the bare error, or counts the unsupported task's cells as done; here each cell is reported and the batch continues.main's code: all 5 cases fail. The unsupported-task test also fails, in both cases, against the first commit alone (02a2640).check_batch_executionsback to a barejson.loads(a list counts asunrecorded): the list case fails._finished_resultreturns a non-object: the list case fails.UnreadableResultaValueErrorinstead of aPrepError(not reported per cell): all 3 cases fail._unsupported_cellback to counting any existingresult.jsonas done, without reading it: both unsupported-task cases fail (no exit 1)._unsupported_cellreads the file withrun_cell's hint (--forcereplaces it): both unsupported-task cases fail.run_batch's handler catches onlyOSError, soUnreadableResultends the batch: both unsupported-task cases fail.test/leftovers-2026-10-30) onmaine97ff7a, at84c92bb:pytest -q -n auto10658 passed, 4 skipped;mypyandruff check .clean.Revert
A plain revert of both commits. Nothing is stored or versioned by this change.
Checklist
pytestpassesruff,mypy, and schema checks passAGENTS.md.github/.licenserc.yaml)