Repository navigation
Repository evaluation: move the eval mechanics out of oneDAL dev/agent_evals into dokimi #41
Description
Repository evaluation today has two implementations of the same mechanics. The framework side (dokimi repo validate | tasks check | run | report | scan) prepares workspaces, applies arms, runs agents and graders in containers, and gates results. The first repository to use it, uxlfoundation/oneDAL#3818 (dev/agent_evals, not merged), still carries its own copy: workspace prep, self-check, the repo_grade.v1 writer, process metrics, generic graders and a guidance claim checker, about 1.8k lines of Python, kept so the tasks run without the framework.
oneDAL reviewers asked why a mostly generic harness lives in their codebase. The answer is that it shouldn't: the tasks belong with the code they test, the mechanics belong here. This issue tracks moving the mechanics into dokimi so that a repository keeps only its evals.
Target split
oneDAL dev/agent_evals/ (after)
├── repo-eval.yaml manifest: guidance globs, image, defaults for built-in graders
├── Dockerfile, bin/bazel the environment tasks build in
├── tasks/<id>/ task.json, patches, hidden tests, review gold (data only)
├── graders/build.py, make.py the only graders that know oneDAL
└── README.md
.github/workflows/agent-evals.yml ~30 lines calling dokimi's reusable workflow
dokimi (gains)
├── repo compare arm vs arm, paired by task
├── dokimi:* graders review seeds, review replay, hidden tests, mutation
├── task `host` requirements unsupported instead of a false pass
├── process metrics from events.jsonl, plus manifest-declared counters
├── L0 parity the remaining oneDAL static_check rules
├── arms guidance from another revision, patch arms
└── reusable CI workflow validate, scan, tasks check
Decided: oneDAL drops its standalone mode, and #3818 is trimmed to evals-only before it merges, so there is no later deletion PR.
Steps
Each step is one PR based on main. They are independent unless noted.
- D1.
repo compare(feat(repo): repo compare, treatment arms against a baseline arm paired by task #42, merged): per-task paired deltas between a baseline arm and treatment arms over one or more batches, with a sign-flip permutation p (exact at reps=1, where it equals McNemar's exact test), a task bootstrap CI, Holm correction across comparisons, tasks up/down/tied, cost and turns, and anygrade.jsonmetric (--metric). Todayrepo reporthas no arm comparison; every analysis so far was an ad-hoc script. - D2. Built-in graders,
dokimi:review_seedsanddokimi:review_replay:task.jsongrader: "dokimi:<name>"is graded by framework code mounted read-only in the grading container (stdlib only); other grader names still go to the repository'sgradeentry point. Review tasks become pure data (manifest/gold JSON plus the answer). Ported from oneDALgraders/review.pyandreplay.py; dokimi's own review task is the second user. - D3. Host requirements (feat(repo): task host requirements, unsupported instead of a false pass (#41 D3) #112, merged):
task.jsonhost: {min_cpus, cpu_vendors, cpu_flags}, checked against the container's view (affinity and cgroup quota).tasks checkandrunreport the task as unsupported with the reason. Needed for a thread race that only reproduces with 3+ CPUs on Intel hosts. - D4. Process metrics in the framework (feat(repo): process metrics in result.json, manifest process_metrics, grade optional for dokimi: graders #157): tool calls, guidance files read (from the manifest's globs), denied and failed commands, computed from
events.jsonlintoresult.json. The manifest declares repository counters as regexes (process_metrics:, for example a bazel command count or a missing-compiler message), so oneDAL'smetrics.pygoes away. - D5.
dokimi:hidden_testsanddokimi:mutation(after D2): hidden files restored from a revision of{source_dir}, thentest_cmdandregression_cmdtemplates, an optionalformat_cmd,root_cause, and diff size. Mutation: the agent's tests must pass and kill each mutant patch, with library sources restored from base bytest_globs. Command defaults live in the manifest, not in every task. - D6. L0 parity with oneDAL
static_check.py(repo scan: L0 rules for parity with oneDAL static_check (D6) #115, merged): diff the claims both produce on oneDALmainand port the missing rules into the L0 adapters (Starlark macro keyword arguments,--confignames from.bazelrc, and anything else the diff shows). Thestaticentry point then becomes optional for oneDAL. - D7. Arms beyond guidance on/off (feat(repo): guidance:rev:<rev> and patch:<file> arms (D7) #117, merged):
guidance:rev:<sha>(the guidance of another revision on the task's base, the "does this PR's AGENTS.md help?" question, which needed generated task twins so far) andpatch:<file>(tooling or layout changes). - D8. Reusable CI workflow plus a pinned install (ci: reusable repo-eval workflow (validate, scan, tasks check) with dokimi pinned #113, merged): a workflow repositories call to run
repo validate,repo scanandrepo tasks checkon the tasks a PR touches; a repository pins dokimi by tag or SHA, which the manifest records. - oneDAL: trim #3818 (after D2–D6 and D8): delete
common.py,config.py,contract.py,metrics.py,run.py,static_check.pyand the generic graders; switch tasks todokimi:*graders andhost:; replace the workflow.
Parallel plan (2026-10-18)
Each wave-1 PR keeps to its own files and contract sections. Together they stay clear of the open PRs: #66 and #71 edit the contract head, §1 and the §8/§9 seam, and others edit architecture.md, README.md, AGENTS.md, docs/README.md, mkdocs.yml, ci.yml and the lines l0/adapters.py 51–58.
| step | wave | code it owns | contract section it owns |
|---|---|---|---|
| D2 built-in review graders | 1 | new repos/graders/, grading.py |
new §4.5, §5 |
| D3 host requirements | 1 | new repos/host.py, selfcheck.py, runner.py |
§3 key list, §4.3 |
| D6 L0 parity | 1 | l0/adapters.py outside lines 40–70, a new test file |
§8 body, not its last paragraph |
| D7 arms | 1 | workspace.py, commands/repo_run.py |
§3 workspace preparation |
| D8 reusable CI | 1 | new .github/workflows/repo-eval.yml |
end of §9 |
| oneDAL phase A | 1 | uxlfoundation/oneDAL#3818 only: drop run.py check/validate/list and prep, CI on pinned dokimi |
none |
| D4 process metrics | 2, after D3 | runner.py, events.py, manifest.py, manifest schema |
§2, §4.1 |
| D5 hidden tests, mutation | 2, after D2 | repos/graders/ |
§4.5 |
| oneDAL trim | 3 | #3818 | none |
Acceptance for the oneDAL step
- Regrade every stored oneDAL run with the old and the new graders: verdicts and gate metrics agree on every run (the previous contract migration reached 308/308).
dokimi repo tasks checkis OK for all 27 tasks in the oneDAL image, with the host-dependent task unsupported rather than passing on hosts that cannot show its bug.repo scanon oneDALmainreports no fewer real failures thanstatic_check.py.
Risks
- Grader semantics now ship with dokimi, so a regrade depends on its version: the manifest pins it, and
grade.jsonrecords the grader version. - Without standalone mode, a contributor needs docker and dokimi to self-check a task.
- Bazel details (
--nocache_test_results, the output base shared by agent and grader) become manifest strings; only the regrade andtasks checkcatch a mistake there. - Contract §5 ("the harness stays in oneDAL and keeps working standalone") is rewritten in D2.
Activity
docs(preparing-a-repository): state what repo run compares today; tie…
feat(repo): repo compare, treatment arms against a baseline arm paire…
napetrov commentedon Oct 19, 2026
Additions to D4 and D7, from oneDAL measurements of tools that help coding agents. The figures are in #65 and its evidence page.
D7: an arm has to be able to change the environment, not only the tree. Three of the setups worth testing need more than a patch:
AGENTS.mdlinePATH(the command)AGENTS.md)Proposal: an arm is a named configuration in the run config passed to
repo run, never in the repository's manifest. The tools stay ordinary repository or image artifacts. It is made of parts:guidance:rev:<sha>andpatch:<file>from the plan are the single-part cases.repo comparecan warn when two batches of "the same" arm differ.D4: tool uptake per arm. Without an uptake count, "the arm made no difference" cannot be told apart from "the agent never used the tool":
So per cell,
result.jsonshould count the calls to whatever the arm added (MCP tool names, or commands matching the arm'sPATHentries).repo compareshould then print the uptake next to the delta and warn when a treatment arm's uptake is 0. This needs a trace the profile can convert, which today is onlyclaude-stream-json. A Codex profile with its--jsontrace converted is the next harness to add, because the review-rules gain in the study was measured on Codex.Two small changes to
repo compareandrepo run, PRs to follow:ci(repo-eval): unbuffered tasks check log; say what the caller grants
agent_evals: self-check with dokimi; drop run.py check, validate, list
docs(contract): oneDAL dropped its policy-only static rules
feat(repo): guidance:rev:<rev> and patch:<file> arms (D7) (#117)
docs: preparing a repository for coding agents (#65)
feat(repo): task host requirements, unsupported instead of a false pa…
ci: reusable repo-eval workflow (validate, scan, tasks check) with do…
feat(repo): record whether the agent used what a patch: arm added (in…
repo scan: L0 rules for parity with oneDAL static_check (D6) (#115)
feat(repos): built-in review graders dokimi:review_seeds and dokimi:r…
feat(repo): record whether the agent used what a patch: arm added (#41 …