Repository navigation
Conversation
… it found formal/repo_batch/RepoBatch.tla models one batch directory of `repo run`: worker threads, resume and --force reruns, crashes between the artifacts and result.json, Ctrl-C, and what `repo report` counts. run.sh checks 13 TLC configs against their expected result (the fixed code passes; the code before the fixes, two mutations, no fairness and four vacuity witnesses each fail as expected). Fixes, each with a test replaying the TLC trace: - run_batch refuses a plan naming one cell twice (a repeated --task or --model, or two --model strings with one slug); with --jobs 2 both runs shared one directory and mixed their files. - load_cells refuses an arm/model whose finished cells ran under two execution ids, left by a --force run that stopped part way; report and compare pooled them in one row. EVALUATOR.md proposes a model-checking grader for agent tasks. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…view fixes Self-review: the refusal in load_cells also blocked hits and a compare of clean arms beside a mixed one; it now runs in summarize and on the rows compare pools. Docs name the harness-binary cause; the Ctrl-C test sets its own SIGINT handler; README lists the gaps the review found. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…in eyes The with-block join can end after the interrupt leaves run_batch, so the second running cell's result.json could land after summarize read the batch (1 of 15 local runs, the 3.14 leg in CI). Wait for both cells. 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 #628 +/- ##
=======================================
Coverage 95.71% 95.72%
=======================================
Files 415 415
Lines 55346 55392 +46
Branches 9514 9522 +8
=======================================
+ Hits 52977 53025 +48
+ Misses 1466 1465 -1
+ Partials 903 902 -1
🚀 New features to boost your workflow:
|
|
OpenCodeReview: Commit 370be67 was not reviewed. This PR has reached its review limit (6 reviews). CI at `370be67`: 10 still running, 3 passing, 1 skipped
To review this commit: re-request review from exai-bot in the Reviewers list. <details><summary>Last review (18e2755): 1 medium</summary> Open threads from the bot: 1 </details> |
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
5 open issues
The secondcompare()call uses treatment"guidance:raw", but this test run only created arms… · New The error only reports the first duplicated cell even if multiple cells are duplicated, which makes… · New This script depends on non-portable tooling/features (grep -P/PCRE,date +%N, andbc) which… · New This script depends on non-portable tooling/features (grep -P/PCRE,date +%N, andbc) which… · New This script depends on non-portable tooling/features (grep -P/PCRE,date +%N, andbc) which… · New
What changed in this PR
Adds a TLA+ model for the dokimi repo run cell lifecycle and uses it to drive fixes that prevent duplicate cell plans and mixed-execution aggregation in reporting/comparison.
Changes:
- Added
formal/repo_batchTLA+ spec + TLC configs + runner script to model-check lifecycle properties. - Prevented duplicate cell directory plans and added tests that replay TLC counterexamples.
- Refused mixed
execution.idrows inrepo reportand in compared rows forrepo compare, with tests and docs updates.
| File | Description |
|---|---|
| dokimi/dokimi/repos/runner.py | Adds _refuse_shared_cells and calls it before any work starts to reject duplicate planned cell directories. |
| dokimi/dokimi/repos/report.py | Adds refuse_mixed_executions() and enforces it in summarize() to prevent pooling multiple executions into a row. |
| dokimi/dokimi/repos/compare.py | Uses refuse_mixed_executions() to refuse only mixed rows that are actually compared. |
| dokimi/tests/test_repo_batch_lifecycle.py | Adds regression tests that replay TLC traces for duplicate plans, Ctrl-C cancel behavior, crash/resume, and mixed-execution rows. |
| dokimi/formal/repo_batch/RepoBatch.tla | Introduces the TLA+ model capturing the cell lifecycle and report/compare counting semantics. |
| dokimi/formal/repo_batch/run.sh | Adds a helper script to run TLC across configs and validate expected outcomes. |
| dokimi/formal/repo_batch/cfg/*.cfg | Adds TLC configurations for “fixed”, pre-fix (“code_main_*”), mutations, fairness, and vacuity witnesses. |
| dokimi/formal/repo_batch/README.md | Documents mapping from model actions to code and summarizes TLC results and findings. |
| dokimi/formal/repo_batch/EVALUATOR.md | Proposal doc for a model-checking grader architecture. |
| dokimi/formal/repo_batch/.gitignore | Ignores TLC output directories/artifacts. |
| dokimi/docs/repo-evaluation-contract.md | Documents new refusal behaviors for repo run duplicate plans and report/compare mixed executions. |
| dokimi/changelog.d/20261111_180000_napetrov_repo-batch-lifecycle.md | Changelog entry describing the fixes and the new formal model. |
| .github/.licenserc.yaml | Exempts .tla/.cfg headers from the license header checker. |
Review effort: Lite
_refuse_shared_cells now lists every repeated cell with its count, and refuses names that differ only in case, since they share one directory on a case-insensitive volume. Thread: PRRT_kwDOVBg7g8624FgM Thread: PRRT_kwDOVBg7g8624Y6I Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
run.sh resolves its jar, output and config paths from the caller's directory before it changes directory, and uses only POSIX tools (sed, tail, no grep -P, date +%N or bc), so it runs on macOS and minimal Linux. Thread: PRRT_kwDOVBg7g8624FgR Thread: PRRT_kwDOVBg7g8624Y65 Thread: PRRT_kwDOVBg7g8624Y7X Thread: PRRT_kwDOVBg7g8624Y7x Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
run.sh runs java after it changes into its own directory, so a JAVA given with a slash must be made absolute first; a bare command name keeps its PATH lookup. Thread: PRRT_kwDOVBg7g86274J4 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
vchamarthi
left a comment
There was a problem hiding this comment.
Review at 0fa65be. 3 new findings: 3 must-fix. In the line comments. Adds a TLA+ model of the repo run cell lifecycle, a refusal for a plan that names one cell twice, and repo report / repo compare refusals for rows whose cells ran under two executions. The plan refusal, the case-only refusal and the run.sh path fixes for TLA2TOOLS_JAR, OUT and the configs hold; the new mixed-execution refusal names a remedy that the batch check refuses; run.sh still fails when JAVA is a relative path; and the Ctrl-C test has no Windows skip.
Verified
- Changed tests: The 12 cases of
dokimi/tests/test_repo_batch_lifecycle.pyanddokimi/tests/test_repo_batch_run_script.pypass on0fa65be. - Merge:
0fa65bemerges onto38d07f9with no conflict. - Duplicate-plan refusal: Changing the planned-twice check in
dokimi/dokimi/repos/runner.pyfrom> 1to> 2makes the refusal tests fail, so they pin the check. - Relative paths in run.sh: A relative config, jar and
OUTresolve from the caller's directory, astest_relative_paths_resolve_from_the_callers_directorypins;JAVAis the exception (see earlier). - Missing config:
run.shgiven a config path that does not exist exits non-zero, names the file on stderr, and writes no<name>.log, before TLC starts. - Execution id type: An execution id that is not a string is counted as
unrecordedbyrefuse_mixed_executions, so it is refused beside a real id. - Ctrl-c: A Ctrl-C during a batch starts no queued cell: the queued cells are reported as missing, not failed.
Still open from earlier rounds
- #628 (comment):
JAVA=./bin/java bash <run.sh> <fixed.cfg>, run from the directory that holds./bin/java, now giveserror rc=127forfixed(summaryNO) and exits 1;run.shchanges into its own directory before it runs"$java"(line 28).
Pre-existing, not blocking
dokimi/dokimi/repos/batch_ledger.py:59:cell_namemaps two different models to one directory, and a laterrepo runon the same batch reuses the first one's finished cell. Input:repo run --batch b1 --model org/m-1 --task fix_add, thenrepo run --batch b1 --model org:m-1 --task fix_add. The second run reports0 ran, 1 already doneand returns without error;fix_add__guidance-raw__org_m-1__r1/result.jsonkeeps"model": "org/m-1", soorg:m-1is never run.reused_result(dokimi/dokimi/repos/batch_ledger.py:140) returns the finished cell because the execution id, built inexecution_record, does not include the model. The PR's_refuse_shared_cellsrefuses this collision only inside one plan; across invocations the same bug class is left open. The same two invocations behave the same way on main.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
Review at 27a835a. Both fixes are right: a plan that names one cell directory twice (or two names that differ only in case) is refused before any agent starts, and repo report / repo compare refuse a row whose cells hold two executions. What is left is the wording of the remedy, a Windows skip, a run.sh exit-status check and where the evaluator proposal lives.
Agreed with @vchamarthi: the remedy the mixed-execution message names is refused while another arm holds the older execution; test_ctrl_c_starts_no_queued_cell needs a Windows skip. The relative JAVA path is fixed in 4d51619.
Agreed with @exai-bot: run.sh reports pass without checking TLC's exit status.
Disagreed with @Copilot on the guidance:raw arm in the compare test: replied in its thread.
What I ran, at 27a835a:
make ci-local --offline: 0 failed, 21 passed, 19 skipped (network-only jobs).tests/test_repo_batch_lifecycle.pyon the merge-base fc9fbf5: 8 of 10 fail. The two that pass there (Ctrl-C, crash then resume) pin behaviour that already held, as the README says.test_ctrl_c_starts_no_queued_cell40 times in parallel on a loaded host: 40 passed, so the 0.3 s wait is not flaky here.- TLC itself was not run (no
tla2tools.jaror Java here);run.shwas checked through its fake-java tests only.
Checked, no issues
- Call sites:
repo comparerefuses only the compared rows, after_in_models, and batch names are distinct, so two batches with one name cannot be grouped together.repo report --hitslists cells one by one and pools nothing. - Legacy cells: a cell with no
executioncounts asunrecorded, ascheck_batch_executionsdoes on resume, so the two checks agree. - Merge: clean into current
main(5 commits behind); no open PR conflicts with it. - License headers: every new file has the SPDX header;
.tlaand.cfgcarry it in TLA+ comments.
| SPDX-License-Identifier: Apache-2.0 | ||
| --> | ||
|
|
||
| # Proposal: a model-checking grader for agent tasks |
There was a problem hiding this comment.
EVALUATOR.md and the "(proposal)" section of README.md are a plan for a feature that does not exist, and AGENTS.md keeps those out of the tree.
AGENTS.md, "What goes in the tree": "Design rationale, status and plans go in the PR description or an issue; the tree has no ADRs or status reports", with dokimi/research/records/ as the one exception. This file starts with "Status: proposal, not implemented", and README.md:140 repeats it.
Suggestion: move the proposal to an issue or a record in dokimi/research/records/, and keep in formal/repo_batch/ only what describes the model and how to run it.
There was a problem hiding this comment.
Waiting on a decision before I change this: where the evaluator proposal lives. Options: (A) one record under dokimi/research/records/, the exception AGENTS.md allows; (B) an issue, and delete EVALUATOR.md and the README section. My recommendation: A, so the design reasoning sits next to the model it describes. The thread stays open until then.
…erun, check TLC exit status in run.sh - refuse_mixed_executions raised TypeError on a result.json with `batch` null, a list or a number; _check_cell now raises CellFileError - the mixed-execution message named an arm-only --force rerun that check_batch_executions refuses; it names a run over every arm and model - run.sh took a verdict line from the log whatever TLC's exit status; a pass needs 0 and a violation 12 or 13 - test_ctrl_c_starts_no_queued_cell is skipped on Windows, where os.kill with SIGINT ends the process Signed-off-by: vchamarthi <123653452+vchamarthi@users.noreply.github.com>
|
Pushed 1 commit to
Threads: 1 fixed, 0 answered as not a defect. Questions for @napetrov1. Move
|
783e2ad to
452f5cd
Compare
|
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 452f5cd. The fixes are right and the tests pin them: the duplicate-cell and case-only refusals, the mixed-execution refusal in report and on compared rows in compare, and the --force remedy now named in the message. I found no wrong behaviour. Two risks are test gaps that let a real regression through, and there are two nits inline, plus one more in the body. The merge also waits for a decision on where the evaluator proposal lives.
What I ran, on a Linux review host at 452f5cd:
make ci-local: 29 passed, 3 failed, 13 skipped. The failures are the gitleaks download (HTTP 504) and its scan, and 3 pytest 60 s timeouts in test_import_architecture.py and test_audit_test_quality.py, which are files the PR does not touch and pass when run alone; 19,018 tests passed; changed-line coverage 100%- Reproductions of the refusals on the
runner.py,report.pyandcompare.pypaths, with the LLM transport mocked: duplicate and case-only plans, legacy and unrecorded cells, two batches in onecompare, the--modelfilter, the--forcererun over every arm. - A mutation run: 21 mutants of the new Python guards, 18 killed, 3 survive (the
comparemodel-filter order and two message strings). 25 mutants ofrun.sh, 9 survive (see the inline comment). - The lifecycle tests against the PR's base: they do not collect (
refuse_mixed_executionsis missing), and the 6 non-stringbatchcases intest_repo_report_unreadable_cell.pyfail there. - Every non-merge commit has a
Signed-off-by.
Open from other reviewers: @vlad-perevezentsev's point that EVALUATOR.md and the README's proposal section keep a plan in the tree, against AGENTS.md, waits for a decision from the author: a record under dokimi/research/records/, or an issue. @xaleryb made the same point.
One more nit. README.md:43 and RepoBatch.tla:133 say _write_json is "mkstemp + os.replace". artifacts.write_json writes a <name>.<pid>.<thread>.tmp sibling and calls os.replace; it does not call mkstemp. Suggestion: write "temp file + os.replace" in both places.
Checked, no issues
- Import order:
batch_ledger,reportandcompareimport cleanly in a fresh interpreter, so the newreporttobatch_ledgerimport makes no cycle. - Legacy cells: a batch with no
executionrecords still reports and compares. One recorded cell next to unrecorded ones is refused asunrecorded, ascheck_batch_executionsdoes. - Remedy: a
--forcerun over the whole plan clears the mixed state;comparerefuses the compared rows only. - Docs and rules: the changelog fragment format, the
mkdocs.ymlnav entry for the contract page, the growth baseline and docs code references pass; no generated artifacts are committed.
Bot points I checked that no longer apply at this head (2)
- Copilot's point on the
guidance:rawarm in the compare test does not apply:_runalways passes--arm guidance:raw, so the batch has that arm and the compared pair exists. - exai-bot's point on
run.shreportingpasswithout the exit status does not apply at this head:run.sh:37takes a pass only with exit 0 and the violation branches only with 12 or 13.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A mixed arm of one model must not refuse a compare that selects another model, and the refusal names the --task and --reps of the remedy. Thread: PRRT_kwDOVBg7g863ez0W Thread: PRRT_kwDOVBg7g863ez0a Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Covers the mutants the fake-java tests let through: a pass line with status 12, exit 0 without the completion line, the last violation named, invariant status 13, a failing cfg before a passing one, a cfg with no EXPECT line, and a relative OUT for TLC's -metadir. Thread: PRRT_kwDOVBg7g863ez0b Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g863ez0d Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The findings these reviews and comments name are fixed on this branch; the one open question, where the evaluator proposal lives, is held. Thread: PRR_kwDOVBg7g88AAAABXs_Lvw: already fixed in 452f5cd Thread: PRR_kwDOVBg7g88AAAABXxLu2A: already fixed in 452f5cd Thread: IC_kwDOVBg7g88AAAABmY9fAw: already fixed in 452f5cd Thread: PRR_kwDOVBg7g88AAAABX_VGsw: fixed in the commits that name its threads Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Names that differ only in case are refused on every system, since they share one directory on a case-insensitive volume. Thread: PRRT_kwDOVBg7g863ez0d Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Re: review by vlad-perevezentsev
|
|
|
|
| for cfg in "${cfgs[@]}"; do | ||
| name=$(basename "$cfg" .cfg) | ||
| expect=$(sed -n 's/^\\\* EXPECT: //p' "$cfg") | ||
| log="$out/$name.log" |
There was a problem hiding this comment.
Must-fix. Impact: the runner silently discards a config's counterexample log when another selected config has the same basename, and the summary cannot distinguish the two inputs. Trigger: invoke run.sh /tmp/before/fixed.cfg /tmp/after/fixed.cfg. Evidence: both inputs set name=fixed, so the second > "$log" truncates the first log; they also share -metadir "$out/states/$name" and the same summary label. Please assign distinct output identifiers to distinct config paths, or reject duplicate basenames before running any checks, and add a two-config regression test. Workaround: use unique basenames or separate invocations with different OUT directories. Severity: medium because this loses verification evidence within the supported explicit-config interface, but does not affect repository-evaluation artifacts.
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 2130ad5. The re-review finds no bug or risk in the changes since my last review: the new tests pin the points I raised, and the guards in runner.py, report.py and compare.py hold. One nit inline on run.sh. The merge still waits for the author's decision on where the evaluator proposal lives.
What I ran, on a Linux review host at 2130ad5:
make ci-local: 33 passed, 0 failed, 14 skipped; the changed-line coverage step passes.- Mutation run of the new Python guards on a copy: 15 of 16 mutants killed. The one that survives changes only the
batch or wherelabel in the refusal message. - A reproduction of
run.shwith a fakejava:run.sh before/fixed.cfg after/fixed.cfgwrites onefixed.log, passes-metadir out/states/fixedto both runs, and prints two rows labelledfixed. - Every non-merge commit (16) has a
Signed-off-by.
Waits for the author: @vlad-perevezentsev's and @vchamarthi's question on EVALUATOR.md and the README proposal section (a record under dokimi/research/records/, or an issue) waits for a decision, since the author handed the choice back with a recommendation and no change.
Checked, no issues
- Threads from my earlier review: the
--modelnarrowing case, the--task and --repspin, therun.shverdict cases and the case-only wording in the changelog and the contract are all present at this head. - Docs: the message wording,
_refuse_shared_cells,has no usable batchand the case-only refusal in the contract, changelog and README match the code. - Bad
batchvalues: null, list and number exit 2 for bothreportandcompare, with no traceback.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
An interrupt raised while pool.map was still submitting cells let the queued cells start, because the pool exit waited without cancelling them. The pool now cancels its pending futures on KeyboardInterrupt. Signed-off-by: vchamarthi <123653452+vchamarthi@users.noreply.github.com>
antonwolfy
left a comment
There was a problem hiding this comment.
I pushed a commit per nit, up to 18e2755, which fix the nit from my review:
18e2755:dokimi/formal/repo_batch/run.sh:24: a repeated cfg basename is refused with exit 2 before any TLC run, and a test covers it.
make ci-local at 18e2755: 33 passed, 0 failed, 14 skipped.
Not approving yet. Still open: @vlad-perevezentsev on EVALUATOR.md and README proposal section: record under dokimi/research/records/ or an issue; author handed the choice back.
|
Pushed 1 commit to
Open threads this head already settles:
The PR description needs these corrections:
|


feat(formal): TLA+ model of the repo run cell lifecycle; fix duplicate-cell plans and mixed-execution rows
Summary
This PR adds a TLA+ model of the
dokimi repo runcell lifecycle. The model covers worker threads, resume,--forcereruns, crashes between the artifacts andresult.json, Ctrl-C, and whatrepo report/comparecount. TLC is run on it, and the two bugs it found are fixed, each with a test that replays the trace TLC printed.result.jsonper cell dir; a retry reuses the cell namerun_cellrmtree before an attempt,result.jsonwritten last;_refuse_shared_cells(new)batch_lock; threads die with the process;_refuse_shared_cellscheck_batch_executions;refuse_mixed_executions(new) insummarizeand on compared rowsrecord_plan+load_cellsmissing listpool.mapcancels pending futures (already true; the test pins it)Bugs fixed
--task/--model, or with--model org/m --model org:m, which give the same slug. With--jobs 2, two threads shared one dir: one rmtree'd the other's dir while its agent ran, andresult.jsonended up beside the other attempt'smeta.jsonand answer. Such a plan is now refused before any agent runs.--forcerun with another execution that stopped part way left cells of two execution ids, andreport/comparepooled them in one row. A harness binary replaced mid-batch does the same.reportnow refuses the batch, andcomparerefuses the rows it compares, naming both ids.A first probe suggested Ctrl-C starts queued cells. That probe used
_thread.interrupt_main(), which sends no signal. A real SIGINT wakes the main thread andpool.mapcancels the queued cells, so it is not a bug.Evidence
formal/repo_batch/run.shchecks each of the 13 configs against itsEXPECTline, and all 13 matched:fixedcode_main_*(code before the fixes)no_fairnessfixed: 10.3M states generated, 5.75M distinct, depth 28, 432 s. Tools: tla2tools v1.8.0 (sha256 in the README) and Temurin 21.0.5+11. The full table is informal/repo_batch/README.md.tests/test_repo_batch_lifecycle.pyhas 11 test cases (9 functions, one parametrized three ways). On intel/main 9 of them fail: the duplicate plan ×3, the stopped--force, naming every repeated cell, the case-only refusal, the per-batch mixed-execution refusal and the twocomparerefusals. The Ctrl-C and crash-then-resume tests characterise existing behaviour and pass on both trees.tests/test_repo_batch_run_script.py(16 test cases, 8 functions, one parametrized nine ways) runsrun.shwith relative paths, with only POSIX tools on PATH, and with the verdict cases; all 16 fail on main's script.make ci-local: 0 failed, 32 passed, 13 skipped, on the pre-round head. On the round head, the repo, batch, runner, formal and tla tests passed (6430 passed, 3 skipped) before the lastrun.shedit; after it, the lifecycle, run-script and unreadable-cell report tests pass on the final tree (190 passed: 11, 16 and 163).Local self-review (pr-review, L3, 3 passes, fresh-context reviewers): pass 1 nits and pass 2's stale counts in this description were fixed; pass 3 on the final head has no blocking findings. The passes led to the refusal moving out of
load_cells, so hits and clean compared arms are no longer refused, and to the test and doc fixes in commits 2 and 3.Limits
--forcetogether with--max-batch-usd;result.json;execution.id;run.shnot in CI.formal/repo_batch/EVALUATOR.mdproposes a model-checking grader for agent tasks. The spec and checker are pinned outside the agent's reach. Its outputs are pass, counterexample, timeout or tool error; a timeout is never counted as a pass. A pass is reported as its own measure, not as a quality score.Merge danger
Medium.
repo report/comparenow refuse batches they used to pool: an arm/model with cells of two execution ids. The message names both ids and the--forcererun that clears it.repo runnow refuses a duplicate cell plan, and two--modelnames that differ only in case (refused on every file system, since they share one directory on a case-insensitive volume).🤖 Generated with Claude Code