Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8cc906e
ci: reusable repo-eval workflow for a repository's no-LLM checks
napetrov Oct 19, 2026
056814f
ci(repo-eval): unbuffered tasks check log; say what the caller grants
napetrov Oct 19, 2026
ffbb6d1
fix(repo-eval): handle whitespace and renames in changed-task selecto…
napetrov Oct 19, 2026
48d103e
fix(repo-eval): handle shared files under tasks dir, normalize manife…
napetrov Oct 19, 2026
631ba99
test(repo-eval): run the Select tasks step itself against a scratch g…
napetrov Oct 19, 2026
4670ea0
fix(ci): repo-eval diffs against the event's base SHA and exempts tas…
napetrov Oct 19, 2026
a5551bd
fix(repo-validate): refuse task ids that contain whitespace
napetrov Oct 19, 2026
f015ed5
docs(changelog): add fragment for repo validate whitespace task-id check
napetrov Oct 19, 2026
fe09af0
ci(repo-eval): diff a pull request's merge commit against its first p…
xaleryb Oct 20, 2026
498b938
ci(repo-eval): treat an empty tasks input as changed
xaleryb Oct 20, 2026
5e6874f
test(repo-eval): pin the empty smoke set and the guidance-only manifest
xaleryb Oct 20, 2026
279db0f
fix(repo-eval): smoke paths win over the Markdown exemption; refuse c…
napetrov Oct 20, 2026
84fd3e8
fix(repo-eval): smoke-paths entries are normalised and split like ids()
napetrov Oct 20, 2026
ec457ba
Merge intel/dokimi main into d8-repo-eval-workflow
napetrov Oct 21, 2026
25686c4
style(repo-eval): ruff format the task-selection tests
napetrov Oct 21, 2026
71dda1d
ci(repo-eval): record a timeout verdict when the job is cancelled; wi…
napetrov Oct 21, 2026
ad1d9b4
ci(repo-eval-self): every path that triggers the self-check selects t…
xaleryb Oct 21, 2026
c6718f8
test(repo-eval): run the Summary step for a cancelled and a failed check
xaleryb Oct 21, 2026
21fbdcd
ci(repo-eval): smoke-paths is space- or comma-separated
antonwolfy Oct 21, 2026
0466d45
docs(repo-eval): document the timeout verdict of a cancelled check
antonwolfy Oct 21, 2026
965ce64
test(repo-eval): cover an empty base sha and a force-push range
antonwolfy Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/repo-eval-self.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright (C) 2026 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

name: repo-eval-self

# dokimi's own evals (.agent-eval/) through the reusable repo-eval.yml, as a
# repository calls it, with two differences: the workflow is this commit's
# (`./`), and so is the dokimi it installs, so a change to the repository
# mode is checked against these tasks before it merges.
#
# A change to a task checks that task. A change to the harness (run.py, the
# Dockerfile, the manifest), to the repository mode or to either workflow
# checks the smoke set: one fix task (hidden tests plus the whole suite) and
# the score-only review task. A dispatch checks all ten by default.
on:
pull_request:
paths:
- '.agent-eval/**'
- 'dokimi/dokimi/repos/**'
- 'dokimi/dokimi/commands/repo*.py'
- 'dokimi/dokimi/schemas/repo_*.json'
- 'dokimi/requirements.lock.txt'
- 'dokimi/pyproject.toml'
- '.github/workflows/repo-eval.yml'
- '.github/workflows/repo-eval-self.yml'
workflow_dispatch:
inputs:
tasks:
description: "`all`, or task ids separated by spaces or commas"
required: false
default: all

permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
self:
permissions:
contents: read
uses: ./.github/workflows/repo-eval.yml
with:
# The commit under test: the merge commit on a pull request.
dokimi-ref: ${{ github.sha }}
dokimi-repository: ${{ github.repository }}
manifest: .agent-eval/repo-eval.yaml
tasks: ${{ inputs.tasks || 'changed' }}
smoke-tasks: fix-degrade-pre review-pr-conventions
# Every `paths:` entry above outside .agent-eval/, as a prefix.
smoke-paths: >-
dokimi/dokimi/repos/ dokimi/dokimi/commands/repo dokimi/dokimi/schemas/repo_
dokimi/requirements.lock.txt dokimi/pyproject.toml .github/workflows/repo-eval.yml
max-parallel: 4
# intel-ubuntu-latest: see ci.yml.
runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }}
timeout-minutes: 60
421 changes: 421 additions & 0 deletions .github/workflows/repo-eval.yml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Changed

- **`repo validate` refuses task ids that contain whitespace or a comma** — a task directory whose name has a space, tab or comma is now a `task.json` error instead of passing validation. The repo-eval workflow's `tasks`, `smoke-tasks` and `exclude-tasks` inputs are split on whitespace and commas, so such a task could never be named there. Rename the task directory to a name without whitespace or commas.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### Fixed

- **repo-eval: a hung oracle leaves a verdict** — the Summary and Upload steps of
`repo-eval.yml` run with `always()`, and a job cancelled by `timeout-minutes` before
`check.json` exists records a `timeout` verdict for the task. The self-check
(`repo-eval-self.yml`) now also runs on changes to `commands/repo*.py`,
`schemas/repo_*.json`, `requirements.lock.txt` and `pyproject.toml`.
58 changes: 57 additions & 1 deletion dokimi/docs/repo-evaluation-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ belongs to the repository's grader and is passed through unread.

| Key | Required | Read by | Meaning |
|---|---|---|---|
| `id` | yes | framework | equals the directory name |
| `id` | yes | framework | equals the directory name, and contains no whitespace or comma: the workflow's `tasks`, `smoke-tasks` and `exclude-tasks` inputs are lists of ids split on whitespace and commas, so `repo validate` refuses an id containing either |
| `family` | yes | framework (reporting) | free string; common values: `build`, `fix`, `feature`, `test`, `review` |
| `base` | yes | framework | commit to snapshot: a hex SHA, optionally followed by `^` (the parent, the usual form for a mined fix). Branch names, tags and `HEAD` are refused, because repetitions of one task must get one snapshot. Use the full 40-character SHA; a shorter one is accepted with a warning. It must resolve in the source. `meta.json` records it as `base_rev` and the resolved full SHA as `base_sha` |
| `prompt` | yes | framework | what the agent is told |
Expand Down Expand Up @@ -870,3 +870,59 @@ python cli.py repo compare repo-runs/pilot --baseline guidance:none --metric roo
`@20250929`) or its provider-prefixed form is the same model, `-v2` against
`-v3` is not. `repo compare` warns the same way when the two arms of a
comparison were answered by different models.

CI for a repository: `.github/workflows/repo-eval.yml` is a reusable workflow
that runs the no-LLM checks on the caller's checkout and needs no secret.

- `validate` runs `repo validate` and gates the other two jobs; `scan` runs `repo scan`
(report only: the claim counts and failing claims go to the job summary, the
`repo_readiness.v1` report to an artifact); `tasks-check` runs `repo tasks check`
for each selected task in a job of its own (docker; the image is built from the
manifest's Dockerfile), and uploads `check.json` and the grade logs. If the job is
cancelled by its `timeout-minutes` before the check finishes, the Summary step
writes a `check.json` with the verdict `timeout` for the task.
- `tasks: changed` (the default, also for an empty value) checks, on a pull request, the tasks whose
directories it touches, plus `smoke-tasks` (empty: every task) when it changes
the eval directory outside its tasks (Markdown there is excepted, unless it is the manifest,
the calling workflow file or under a `smoke-paths` entry), the manifest, the calling workflow
file or a path under `smoke-paths` (entries are space- or comma-separated prefixes,
normalised like the manifest path; keep the trailing slash for a directory). A file in
the tasks directory that is not under an existing task (a shared helper, or the files of
a deleted task) does too. On a push it diffs against the previous tip, on any other event
it checks every task. `all` or a list of
task ids selects directly; `exclude-tasks` always drops tasks.
- Pin dokimi by a commit SHA or a release tag, the same one in `uses:` and in
`dokimi-ref` (a branch is refused). It installs
`dokimi @ git+https://github.com/intel/dokimi@<sha>#subdirectory=dokimi` with
that commit's `requirements.lock.txt` as constraints, so a rerun checks with the
same framework and dependencies. Moving the pin is a change to the calling
workflow, so it runs the smoke set.
- Other inputs: `manifest`, `max-parallel` (4), `warm` (`--warm`, for a cache that
starts empty on every runner), `runs-on` (`ubuntu-latest`), `timeout-minutes`
(180, per task) and `dokimi-repository` (`intel/dokimi`).

```yaml
# .github/workflows/agent-evals.yml in the repository
name: Agent evals
on:
pull_request:
paths: ['dev/agent_evals/**', '**/AGENTS.md', '.github/workflows/agent-evals.yml']
schedule:
- cron: '0 3 * * 0'
workflow_dispatch:
permissions: {}
jobs:
evals:
permissions:
contents: read
uses: intel/dokimi/.github/workflows/repo-eval.yml@<sha> # vX.Y.Z
with:
dokimi-ref: <sha>
manifest: dev/agent_evals/repo-eval.yaml
smoke-tasks: build_kmeans_host fix_finiteness_mut review_seeded_fc
max-parallel: 4
warm: true
```

dokimi runs the workflow on its own `.agent-eval/` (`repo-eval-self.yml`), with
the commit under test as `dokimi-ref`.
6 changes: 6 additions & 0 deletions dokimi/dokimi/repos/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,12 @@ def _glob_regex(pattern: str) -> re.Pattern[str]:
def _check_task(tree: RepoTree, task_dir: str, report: RepoCheckReport) -> None:
name = PurePosixPath(task_dir).name
where = f"{task_dir}/task.json"
if re.search(r"[\s,]", name):
# tasks, smoke-tasks and exclude-tasks are lists of ids split on whitespace and commas, so an id
# containing either could never be named there
report.errors.append(f"{where}: task id {name!r} contains whitespace or a comma; task ids are "
"split on whitespace and commas in the workflow's tasks, smoke-tasks and exclude-tasks inputs")
return
try:
raw = json.loads(tree.read_text(where))
except (json.JSONDecodeError, UnicodeDecodeError) as exc:
Expand Down
Loading