Repository navigation
feat(repo): process metrics in result.json, manifest process_metrics, grade optional for dokimi: graders - #157
Conversation
… grade optional for dokimi: graders result.json gains `process`, counted by the framework from events.jsonl: tool_calls, commands, failed_commands, denied_commands and guidance_read (read paths the guidance globs match). The manifest's process_metrics maps a name to a regex over command text, output or both; repo validate refuses a regex that does not compile. entry_points.grade is required only when some task's grader is not a built-in dokimi: one. Part of intel#41 (step D4). Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #157 +/- ##
==========================================
+ Coverage 92.43% 92.98% +0.55%
==========================================
Files 240 243 +3
Lines 30415 30835 +420
Branches 5029 5081 +52
==========================================
+ Hits 28114 28673 +559
+ Misses 1615 1460 -155
- Partials 686 702 +16
🚀 New features to boost your workflow:
|
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 343f7ac. The process-metrics code is clean and the counting rules match the contract. Two things are worth fixing before merge: the optional grade entry point has no runtime behind it at this head, and the one runner test cannot tell a working guidance_read / counters wiring from a broken one.
What I ran, on a Linux review host at 343f7ac:
make ci-local: 25 passed, 1 failed, 6 skipped. 6,731 tests, changed-line coverage passes. The failure is pip-audit on virtualenv 21.14.7 (CVE-2026-113722) from the existing locks, fixed onmainby #152.- A reproduction of the first finding through
repo runwith the container runner faked. - A mutation run: 48 mutants of
process_metrics.py,manifest.pyandrunner.pyagainst the three test files that cover them; 18 survive, 17 of them real gaps (the second finding).
Checked, no issues
- Schema: dropping
required: ["grade"]only widensrepo_manifest.v1;process_metricsis validated by both the schema andparse_counter. - Counting rules:
failed_commandsignoresrcnull, denied commands still reach the counters, bad regex / badmatch/ bad name are refused with the metric named. - Offline: the new tests need no network or Docker.
… cell without a grade entry point The runner fixture now has process_metrics and a guidance glob, with a trace that reads a guidance file and runs a failing command; a null process for an unreadable trace is covered. Process-metric unit tests gain output-only 'any' matches, command-only counting and non-1 rc values. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…re any agent runs grade_problems ran only in repo validate. repo run and repo tasks check loaded such a manifest, ran and paid for every cell, and graded each one grader_failure. _context now checks the selected tasks before the batch is pinned or the image built. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
The schema name pattern skips non-string keys, so a bare 404:, on: or yes: passed repo validate, then made the sorted result.json write raise TypeError after the agent had run, aborting the batch. The manifest now names the key. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
A command the harness permissions refused comes back as an is_error result (rc 1), one a hook refused has rc null; neither ran. failed_commands counted the first, so it depended on the denial mechanism. Denied commands are now counted only in denied_commands. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
…ance_read Guidance globs are root-relative, but every read path was matched, so a home-dir or ../ AGENTS.md and a denied read counted as guidance read, also in a guidance:none cell. The new test also fails a matcher that accepts any path. Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
|
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 56fe503. Both threads from my earlier review are addressed: the merge of main brings the built-in grader dispatch, and the new runner and unit tests now pin guidance_read, counters and process: null. Three things are worth fixing before merge, plus three nits.
What I ran, on a Linux review host at 56fe503:
make ci-local: 27 passed, 1 failed, 7 skipped. 7,173 tests. The failure ispip-auditon the lock files, which this PR does not touch; the changelog, schema-compat, docs and gitleaks jobs pass.- Reproductions of the two
guidance_readfindings throughevents.convertandprocess_metrics, with no model involved. - A mutation run: 62 mutants of
process_metrics.py,manifest.py,runner.pyandrepo_run.pyagainst the four test files that cover them; 11 survive, 6 of them real gaps (finding 1).
Findings that cannot be anchored inline:
-
The tests still let four regressions through. Each mutant below passes
test_repo_process_metrics.py,test_repo_run.py,test_repo_builtin_graders.pyandtest_repo_manifest.py(260 tests):BUILTIN_GRADER_PREFIX = "dokimi", andBUILTIN_GRADER_PREFIX not in t.graderingrade_problems(manifest.py:377). The tests only usedokimi:x,dokimi:review_replayandtests, so a repository grader nameddokimi_testsorx-dokimi:ywould skip the "grade required" error and every cell would then fail grading after the agent is paid for. Addgrade_problems(m, [summary("a", "dokimi_tests"), summary("b", "x-dokimi:y"), summary("c", "dokimi")])and expect one problem naming a, b and c.e.get("kind") == "read"replaced bye.get("path")(process_metrics.py:70): aneditofAGENTS.mdwould count as read. The onlyeditevent in the tests issrc/lib.c. Add{"kind": "edit", "path": "AGENTS.md"}and expect it absent._in_workspaceasnot str(p).startswith("..")(process_metrics.py:60): only a leading../is tested. Addsrc/../AGENTS.mdanda/../../x/AGENTS.md.additionalProperties: falseremoved from the object form of a counter inrepo_manifest.v1.json:{regex: x, mach: output}is then accepted and silently counts againsttext. Add it to the refusal cases intest_repo_manifest.py.
-
Nit: the contract still says the opposite in §4.5.
repo-evaluation-contract.md:534(outside this diff) reads "entry_points.gradeis still required (repo_manifest.v1), even when every task uses a built-in grader." The schema,grade_problems, §2 and the changelog fragment now say it is not. Suggestion: reword the sentence to sayentry_points.gradeis optional when every task's grader starts withdokimi:.
Waiting for the author: @xaleryb's question on process for an agent that exits before writing any trace line waits for the author. At this head such a run gets process with every count at zero, the same as a short run with no tool call.
Checked, no issues
- Schema versioning: dropping
required: ["grade"]and addingprocess_metricsonly widensrepo_manifest.v1;schema_compatagainst the base reports OK. - Counting rules:
failed_commandsskipsrcnull and denied commands;denied_commandsand the counters still see denied commands; a bad regex,matchor name is refused with the metric named. The schema andparse_counteragree on every shape I tried. - Grade-optional rule:
_require_gradehonours--task, runs before the pin and the image build, and a cell with onlydokimi:graders and noentry_points.gradegrades. - Offline: the new tests need no network or Docker.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
antonwolfy
left a comment
There was a problem hiding this comment.
I pushed a commit per nit, up to 0fbf76c, which fix the three nits from my review:
406d986:dokimi/dokimi/repos/manifest.py:57: removedBUILTIN_GRADER_PREFIXand madegrade_problemsuse the importedBUILTIN_PREFIX.81e80b4:dokimi/docs/repo-evaluation-contract.md:210: the name rule now says a name cannot start with a digit.0fbf76c:dokimi/docs/repo-evaluation-contract.md:534: §4.5 now saysentry_points.gradeis optional when every task's grader starts withdokimi:.
make ci-local at 0fbf76c: 27 passed, 1 failed, 7 skipped.
GitHub CI has not run on these commits: the branch conflicts with main, and Actions runs no workflow on a conflicting PR. CI will check them once main is merged in.
Not approving yet. Still open: the three risks from my review.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…n review mutants Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Review items at 56fe503 and the merge of main, mapped to commits:
|
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…wth ratchet) Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
antonwolfy
left a comment
There was a problem hiding this comment.
LGTM. Both threads from my last review are fixed and pinned: a failed read no longer counts in guidance_read, and paths are normalized before the workspace test. One nit inline.
What I ran, on a Linux review host at 1b9bda8:
make ci-local: 26 passed, 3 failed, 7 skipped. 7,782 tests. The schema-compat, changelog, docs and CLI-surface jobs pass.pip-auditfails on the lock files, which this PR does not touch. The two gitleaks jobs fail on the tool install and the missing binary, not on a finding;gitleaks detectover the PR's 12 commits reports no leaks.- A mutation run on
process_metrics.py: 19 mutants (rc filter,deniedfilter,normpath, the~,..and absolute-path checks,failed_commandsvariants,sorted,any/outputfields,search,tool_calls), all killed. - Reproductions of the edge cases through
events.convertandprocess_metrics, with no model involved.
Checked, no issues
- Schema versioning: dropping
required: ["grade"]and addingprocess_metricsonly widensrepo_manifest.v1;schema_compatagainst the base reports OK. - Grade-optional rule:
grade_problemsis used bycheck_repoand by_require_gradeinrepo run, so a repository grader withoutentry_points.gradeis refused before the pin. - Docs:
repo-evaluation-contract.mdis in the nav; the manifest rules and theprocesstable match the code.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
antonwolfy
left a comment
There was a problem hiding this comment.
I pushed a commit per nit, up to f090697, which fix the nit from my review:
f090697:dokimi/changelog.d/20261021_140000_napetrov_d4-process-metrics.md:3: the fragment now saysprocessisnullwhen the agent left no readable trace.
make ci-local at f090697: 26 passed, 3 failed (host: gitleaks install, gitleaks scan, pip-audit), 7 skipped.
GitHub CI has not run on these commits: the branch conflicts with main, and Actions runs no workflow on a conflicting PR. CI will check them once main is merged in.
Not approving until CI passes at this head.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com> # Conflicts: # dokimi/tests/test_repo_run.py
| def hits(self, event: Mapping[str, Any]) -> bool: | ||
| fields = ("text", "output") if self.field == "any" else (self.field,) | ||
| return any(self.pattern.search(str(event.get(f) or "")) for f in fields) |
There was a problem hiding this comment.
A command that a PreToolUse hook refused keeps the hook's message as its output ("PreToolUse:Bash hook error: ..."), so an output or any counter searches that message. With errors: {regex: error, match: output}, make all & refused by no_background.py gives counters.errors == 1, though the command never ran. Counting denied commands makes sense for text (the agent asked for it), not for output. Suggestion: skip output for events with denied: true, and say so in §2.
There was a problem hiding this comment.
Fixed in a6b20af: a denied command is now searched in its text only, so the hook refusal message no longer counts as the output. errors with match: output or any stays 0 for a refused make all &. Pinned by test_a_refused_command_is_searched_in_its_text_not_in_the_hook_message. Section 2 of the contract says so too.
| events = list(events) | ||
| commands = [e for e in events if e.get("kind") == "command"] | ||
| # A read that failed (rc 1: the file does not exist) read no guidance. | ||
| reads = {w for e in events if e.get("kind") == "read" and e.get("path") and not e.get("denied") |
There was a problem hiding this comment.
guidance_read counts only read events (the Read tool). An agent that runs cat AGENTS.md or grep -n rule CLAUDE.md through Bash read the guidance, but guidance_read is [], which looks like "did not read it". Either also match the command text against the guidance globs (as arm_uptake does for added files), or rename/document the field as "guidance opened with the Read tool" so an empty list is not read as a result.
| _write_json(rd / "meta.json", meta) | ||
| if transcript.events or transcript.num_turns is not None: # else the agent never started | ||
| events = transcript.events | ||
| process = run_process_metrics(ctx, events) |
There was a problem hiding this comment.
run_process_metrics runs inside the grade try and before agent.update(...). If it raises (or a manifest regex backtracks badly on 8 KB of output), the cell gets grader_failure and loses cost_usd, num_turns and tokens, so a best-effort metric decides the task result. Suggestion: compute process after agent.update, in its own try that leaves it null and adds to trace_errors.
There was a problem hiding this comment.
Fixed in a6b20af: process is computed after agent.update, in its own guard. A metric that raises leaves process null, adds a trace_errors entry, and the cell keeps its cost, turns and grade. Pinned by test_a_process_metrics_error_leaves_process_null_and_keeps_the_cost_and_the_grade.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com> # Conflicts: # dokimi/docs/repo-evaluation-contract.md # dokimi/dokimi/repos/manifest.py # dokimi/dokimi/repos/runner.py # dokimi/tests/repo_eval_fixture.py
…error keeps the cell A PreToolUse hook's refusal is kept as the output of a denied command, so an `output` or `any` counter counted the hook's message as the command's output. A denied command is now searched in its text only (contract §2). `process` was computed inside the grade try, before the cost was recorded: a counter that raised turned the cell into a grader_failure and dropped the cost, turns and tokens. It is now computed after agent.update in its own guard; a failure leaves `process` null and adds to `trace_errors`. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ifest.py Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Thanks for the reviews. Each open item and the commit that answers it, on head 79c20c5:
Ready for another look. |
antonwolfy
left a comment
There was a problem hiding this comment.
LGTM. Both fixes since my last review hold: a refused command is searched in its text only, and a metric that raises leaves process null while the cell keeps its cost, turns and grade. One nit inline.
What I ran, on a Linux review host at 79c20c5:
make ci-local: 29 passed, 0 failed, 7 skipped. 8,426 tests. Changed-line coverage, schema-compat, changelog, docs, gitleaks and pip-audit pass.- A mutation run: 28 mutants of
process_metrics.pyand therun_cellwiring inrunner.pyagainsttest_repo_process_metrics.py,test_repo_run.py,test_repo_manifest.pyandtest_repo_builtin_graders.py; 26 killed. The two survivors are test gaps with no effect on results: aguidance_globspredicate that honours only the first glob (no test uses two globs), andif events is not Nonereplaced byif True(only adds aprocess metrics failedline totrace_errorsfor an unreadable trace). - Reproductions through
events.convertandprocess_metrics, with no model involved: tool_result pairing out of order, an orphan result, a subagent read, a denied command next to a failed one. The output is as the contract says.
@vlad-perevezentsev's point that guidance_read misses guidance read through Bash (cat AGENTS.md) waits for the author. The contract row says "of read events", so the field does what it documents; widening or renaming it is the author's call.
Checked, no issues
- Schema versioning: dropping
required: ["grade"]and addingprocess_metricsonly widensrepo_manifest.v1;schema_compatagainst the base passes. The schema andparse_countergive the same verdict on every shape I tried. - Grade-optional rule:
_require_graderuns before the pin inrepo runandrepo tasks check, and uses the samedokimi:prefix as the per-task check. events.pyrefactor: on the same streams, base and head differ only by the newrconreadevents; command, edit, skill and task events are identical.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
antonwolfy
left a comment
There was a problem hiding this comment.
LGTM. I pushed a commit per nit, up to 654b6b1, which fix the nit from my review:
654b6b1:dokimi/docs/repo-evaluation-contract.md:435:processisnullalso when computing the metrics failed, with the error inagent.trace_errors.
make ci-local at 654b6b1: 29 passed, 0 failed, 7 skipped.
Not merging yet: @vlad-perevezentsev on guidance_read counts only Read-tool events, not guidance read through Bash (cat AGENTS.md); widen it or document/rename it waits for your answer, @napetrov.
A Bash cat of AGENTS.md is not counted; say so in the contract row, the module docstring and the changelog, and point at a counter for it. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
@antonwolfy @vlad-perevezentsev answering the open `guidance_read` question (Vlad's Bash `cat AGENTS.md` point). Kept Read-tool-only and documented it, rather than parsing Bash command text. The contract row, the `process_metrics` docstring and the changelog fragment now say `[]` means no Read-tool read. To count a Bash read, add a `process_metrics` counter on the command text, e.g. `guidance_cat: '\bcat\b.*AGENTS\.md'`; counters also count denied commands. Commit 0696bb6 (docs and docstring only, on top of 654b6b1). Tests: 121 passed on `test_repo_process_metrics.py` and `test_repo_run.py`; ruff clean. Local self-review: no blocking findings. Still pending: reviews from @xaleryb, @jharlow-intel and @vchamarthi. |
#269) Each test below fails on a code change the reviews' mutation runs found no test catching. Tests only: no code changes. - #213 (network policy): the allowlist NO_PROXY value is asserted next to HTTPS_PROXY; a task network_mode of "Public" or "No-Network" is read case-insensitively; allowed_hosts or environment.egress given as one string is one host, not its letters. - #213 (SWE-bench grader): a bare status-word line is skipped by parse_pytest, parse_pytest_options and parse_pytest_v2, as it already was tested for parse_seaborn. - #246: _apply_correction is checked on [0.01, 0.02, 0.03], where BH (0.03, 0.03, 0.03) and Holm (0.03, 0.04, 0.04) differ. - #157: a read that only the second guidance glob matches counts as guidance, and an agent that left no trace gets no "process metrics failed" line in trace_errors. - #188: a cached usage with "model": null is a cache miss. - #187: the dry run prints the skipped-factor lines in --factors order. Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Part of #41 (step D4).
Summary
dokimi/repos/process_metrics.py; counts all events (parent and subagents).failed_commands=rcnot 0/null; denied commands still count for counters (the agent asked for them), as in oneDAL'smetrics.py.repo validaterefuses a regex that does not compile, a badmatch, a bad name;process_metricsneeds a runnable manifest.entry_points.gradeno longer schema-required (widening, v1 in place);check_reporequires it only when some task'sgraderdoes not start withdokimi:, and names those tasks. Generic, so it holds whether or not feat(repos): built-in review graders dokimi:review_seeds and dokimi:review_replay #120 lands first.host:blocks were already validated byrepo validate(feat(repo): task host requirements, unsupported instead of a false pass (#41 D3) #112); added a test that pins it.result.jsonprocesstable. Nothing in §3/§5/§8.Evidence
result.jsonhas noprocess; oneDAL recomputes counts in its ownmetrics.py; a manifest withoutentry_points.gradefails the schema.tests/test_repo_process_metrics.py(each metric, counter fields, 6 regex/shape refusals, grade-optional rule, host validation) andtest_repo_run.pyassertingresult["process"]on a real cell.make ci-local --offline: 0 failed, 15 passed, 11 skipped (network/docker).git merge-treeagainst repo scan: L0 rules for parity with oneDAL static_check (D6) #115 and feat(repos): built-in review graders dokimi:review_seeds and dokimi:review_replay #120 heads: clean.Merge Danger
Door: two-way
Additive key in
result.json; schema widened only.Blast Radius: small
Readers of
result.jsonthat reject unknown keys would seeprocess; none in-tree.Checklist
🤖 Generated with Claude Code