Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d3551fd
feat(runs): scan the runs root and tell each artifact's kind from its…
vlad-perevezentsev Oct 30, 2026
60a1424
feat(runs): runs list
vlad-perevezentsev Oct 30, 2026
1a94525
feat(runs): runs show
vlad-perevezentsev Oct 30, 2026
aaf8e15
docs(runs): the runs root, runs list and runs show
vlad-perevezentsev Oct 30, 2026
6ec9ed5
fix(runs): print unknown versions escaped; an mtime past 9999 does no…
vlad-perevezentsev Oct 30, 2026
f14148f
fix(runs): a root or an address that cannot be looked up exits 2
vlad-perevezentsev Oct 30, 2026
35f5885
fix(runs): nothing outside the runs root is read
vlad-perevezentsev Oct 30, 2026
bba4f96
test(runs): a time that is not ISO 8601, an artifact that cannot be o…
vlad-perevezentsev Oct 30, 2026
bdfefd4
docs(runs): only a file of a registered kind is parsed, so only it ca…
vlad-perevezentsev Oct 30, 2026
98caf53
fix(runs): name the address and the root escaped in error messages
vlad-perevezentsev Oct 30, 2026
13a0738
fix(runs): runs list does not walk into a Windows junction
vlad-perevezentsev Oct 30, 2026
33f8422
fix(runs): runs show reads the file the filesystem opens for link/..
vlad-perevezentsev Oct 30, 2026
bdc14ac
test(runs): raise the RecursionError instead of finding it by nesting
vlad-perevezentsev Oct 30, 2026
942ecdf
docs(runs): the schema_version must open the first 256 bytes
vlad-perevezentsev Oct 30, 2026
2f31691
fix(runs): runs show lists neighbours from inside the root only
vlad-perevezentsev Oct 30, 2026
34e76e1
fix(runs): runs show takes an absolute address under a root named wit…
vlad-perevezentsev Oct 30, 2026
b4b0fd6
Merge branch 'main' into feat/runs-list
napetrov Oct 31, 2026
32b0d91
Merge intel/main into feat/runs-list
xaleryb Oct 31, 2026
391a9f6
ci(windows): run the runs scan and show tests in windows-smoke
xaleryb Oct 31, 2026
7b73110
fix(benchmark): raise max_docs to 37 for docs/runs.md
xaleryb Oct 31, 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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -487,5 +487,6 @@ jobs:
cd "$RUNNER_TEMP" && python "$smoke"
- name: Install test deps
run: python -m pip install --require-hashes -r requirements.lock.txt
# test_runs_scan.py holds the only junction test of the runs-root containment; it runs on Windows only.
- name: Run a few fast tests
run: pytest -q tests/test_cli_help.py tests/test_console_encoding.py
run: pytest -q tests/test_cli_help.py tests/test_console_encoding.py tests/test_runs_scan.py tests/test_runs_show.py
2 changes: 1 addition & 1 deletion dokimi/benchmarks/spec.v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ future:
# that matched almost nothing, max_docs one that escaped; raise it with each new doc.
golden_manifest:
min_docs: 10
max_docs: 36
max_docs: 37
include:
- docs/**/*.md
exclude:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!--
Copyright (C) 2026 Intel Corporation
SPDX-License-Identifier: Apache-2.0
-->

### Added

- **`dokimi runs list` lists the artifacts under the runs root** — the root is
`results/` in the working directory, or `DOKIMI_RUNS_DIR`. One row per artifact:
its `schema_version`, subject, model, recorded time and path, newest first;
`--kind` (repeatable) and `--subject` (an exact match) filter the rows, and
`--json` prints them with the fields `schema_version`, `kind`, `run_id` (always
`null` until artifacts carry one), `subject`, `model`, `generated_at`, `path`
and `size_bytes`. A file is an artifact when its first 256 bytes open with
the `schema_version` of a registered kind, as `save_artifact` writes it; every other file is counted by why it
was skipped, and a file that cannot be read is listed with its reason. A
missing `results/` prints "no runs" and exits 0; a `DOKIMI_RUNS_DIR` that does
not exist or is not a directory exits 2. The command only reads.
- **`dokimi runs show PATH` shows one artifact** — `PATH` as `runs list` prints
it (relative to the root), or an absolute or working-directory path inside the
root once symlinks are resolved (neither command reads a file outside the
root). It prints the artifact's kind, its fields, the length of the lists it
holds (answers, evaluations, cells, ...) and the files next to it, and checks
it against its schema: an artifact that fails the check is shown with
"schema: invalid, N errors" and every error, and the command still exits 0.
A path outside the root, a missing file, a directory, or a file that is not a
readable artifact of a registered kind exits 2. `--json` prints the same
fields as a `runs list` row plus `schema`, `schema_valid`, `schema_errors`,
`contents` and the neighbours.
3 changes: 3 additions & 0 deletions dokimi/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ code.
- [quickstart.md](quickstart.md) — end-to-end LLM evaluation pipeline:
personas → questions → answers → judge → report. Includes troubleshooting
and a cost estimate.
- [runs.md](runs.md) — what the runs have written: `cli.py runs list` lists
every artifact under `results/` (or `DOKIMI_RUNS_DIR`) and counts what it
skipped, `cli.py runs show` checks one against its schema. No LLM call.
- [user-flow.md](user-flow.md) — the canonical end-to-end path: pick a target,
preflight, run, then pin the question set so runs stay comparable.
- [benchmarking-and-comparison.md](benchmarking-and-comparison.md) — how to
Expand Down
2 changes: 1 addition & 1 deletion dokimi/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ points up:
| Layer | Modules |
| --- | --- |
| front end | `cli`, `commands`, `__main__` (the argparse front end; no layer below imports it) |
| tracks | `runner`, `report`, `subjects`, `novelty`, `harnesses`, `context`, `repos`, `explore`, `tool_preference`, `external`, `dashboard`, `orchestrator`, `datasets_export`, `coverage`, `task_formats`, and the checkers `config_check`, `schema_check`, `schema_compat` |
| tracks | `runner`, `runs`, `report`, `subjects`, `novelty`, `harnesses`, `context`, `repos`, `explore`, `tool_preference`, `external`, `dashboard`, `orchestrator`, `datasets_export`, `coverage`, `task_formats`, and the checkers `config_check`, `schema_check`, `schema_compat` |
| evaluation | `eval`, `personas`, `questions`, `treatments`, `plugins` |
| components | `ingest`, `metrics`, `gate`, `mcp`, `skills`, `agent_profiles` |
| foundation | `__init__` (the `dokimi` package itself), `_version`, `artifacts`, `records`, `llm`, `process`, `container_policy`, `caching`, `config`, `registry`, `intents`, `defaults`, `console`, `utils`, `metrics/usage.py` |
Expand Down
3 changes: 2 additions & 1 deletion dokimi/docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ at such a checkout.
Everything the pipeline produces — answers,
eval scores, benchmark snapshots, reports — stays under the git-ignored
`answers/`, `eval/`, `baselines/`, `reports/` and `results/` directories. See
[`data/README.md`](../data/README.md).
[`data/README.md`](../data/README.md). `python cli.py runs list` lists the
artifacts under `results/` ([runs.md](runs.md)).

## Cost estimate

Expand Down
173 changes: 173 additions & 0 deletions dokimi/docs/reference/cli-interface.json
Original file line number Diff line number Diff line change
Expand Up @@ -12820,6 +12820,179 @@
"subcommand_required": null,
"subcommands": {}
},
"runs": {
"allow_abbrev": true,
"params": [
{
"action": "_HelpAction",
"choices": null,
"const": null,
"default": "==SUPPRESS==",
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": true,
"key": "--help/-h",
"kind": "option",
"nargs": 0,
"position": null,
"required": false,
"type": null
}
],
"path": "dokimi runs",
"subcommand_required": true,
"subcommands": {
"list": {
"allow_abbrev": true,
"params": [
{
"action": "_HelpAction",
"choices": null,
"const": null,
"default": "==SUPPRESS==",
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": true,
"key": "--help/-h",
"kind": "option",
"nargs": 0,
"position": null,
"required": false,
"type": null
},
{
"action": "_StoreTrueAction",
"choices": null,
"const": true,
"default": false,
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": true,
"key": "--json",
"kind": "option",
"nargs": 0,
"position": null,
"required": false,
"type": null
},
{
"action": "_AppendAction",
"choices": [
"'answers'",
"'arms'",
"'cli_routes'",
"'eval'",
"'explore_runs'",
"'guidance_reach'",
"'matrix_rollup'",
"'novelty'",
"'plan'",
"'plugin_delta'",
"'questions'",
"'repo_budget'",
"'repo_environment'",
"'repo_readiness'",
"'result'",
"'subject_scorecard'",
"'task_inspect'",
"'task_runs'",
"'tool_preference'"
],
"const": null,
"default": null,
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": false,
"key": "--kind",
"kind": "option",
"nargs": null,
"position": null,
"required": false,
"type": null
},
{
"action": "_StoreAction",
"choices": null,
"const": null,
"default": null,
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": false,
"key": "--subject",
"kind": "option",
"nargs": null,
"position": null,
"required": false,
"type": null
}
],
"path": "dokimi runs list",
"subcommand_required": null,
"subcommands": {}
},
"show": {
"allow_abbrev": true,
"params": [
{
"action": "_StoreAction",
"choices": null,
"const": null,
"default": null,
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": false,
"key": "path",
"kind": "argument",
"nargs": null,
"position": 0,
"required": true,
"type": null
},
{
"action": "_HelpAction",
"choices": null,
"const": null,
"default": "==SUPPRESS==",
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": true,
"key": "--help/-h",
"kind": "option",
"nargs": 0,
"position": null,
"required": false,
"type": null
},
{
"action": "_StoreTrueAction",
"choices": null,
"const": true,
"default": false,
"group_key": null,
"group_required": null,
"hidden": false,
"is_flag": true,
"key": "--json",
"kind": "option",
"nargs": 0,
"position": null,
"required": false,
"type": null
}
],
"path": "dokimi runs show",
"subcommand_required": null,
"subcommands": {}
}
}
},
"subjects": {
"allow_abbrev": true,
"params": [
Expand Down
118 changes: 118 additions & 0 deletions dokimi/docs/runs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
depends_on:
- dokimi/dokimi/runs/**
- dokimi/dokimi/commands/runs.py
---
<!--
Copyright (C) 2026 Intel Corporation
SPDX-License-Identifier: Apache-2.0
-->

# The runs root: `runs list` and `runs show`

Most commands that run an evaluation write their artifacts under `results/` by
default (a few write elsewhere, listed below). `runs list` shows what is there
and `runs show` opens one artifact. Both only read files: no model call, no
network access, nothing written.

The runs root is `results/` in the working directory. Set `DOKIMI_RUNS_DIR` to
read another directory.

## `runs list`

```text
$ python cli.py runs list
SCHEMA SUBJECT MODEL TIME (UTC) PATH
arms.v1 onetbb gpt-6.1-sol 2026-10-29 14:02 arms/onetbb.json
plugin_delta.v1 onetbb gpt-6.1-sol 2026-10-28 10:11* arms/plugin_delta.json
eval.v1 - claude-sonnet-5-5 2026-10-28 10:05 onetbb_r7/eval/onetbb.json
answers.v1 onetbb gpt-6.1-sol 2026-10-28 09:40 onetbb_r7/answers/onetbb.json
novelty.v1 skill:skills/onetbb-tuning claude-sonnet-5-5 2026-10-27 18:30 novelty/skill_skills_onetbb-tuning/novelty.json

5 artifacts under results/ (* the file's mtime: the artifact records no time)
skipped: 1 JSON with no leading schema_version, 1 JSON of an unknown schema_version (pairwise.v0: 1), 1 not .json
```

- **Each row is one artifact file.** A file is an artifact when its first 256
bytes open with the `schema_version` of a registered kind: `save_artifact`
has written it as the first key since
[#27](https://github.com/intel/dokimi/pull/27), well inside that. Superseded
versions such as `questions.v1` count too. Files that open with anything
else are skipped without being read in full, so a large file of another kind
costs nothing.
- **Subject, model and time** come from the artifact's own fields, and which
fields depends on the kind: `product` of a questions set, `library_name` of
an arms run, `artifact.ref` of a novelty run, `repo.id` of a reach run,
`judge_model` and `evaluated_at` of an eval. An artifact that records no
subject shows `-` (an eval, a task run, a questions set from `questions
generate`, which records the model but not the product); nothing is guessed
from the path. Rows are newest first. When an artifact records no time, its
file's mtime is used for the order and shown with `*`.
- **Nothing is dropped silently.** The summary counts the files skipped, by
reason: JSON that does not open with a `schema_version` (other output, and
artifacts saved before #27, which did not put it first), JSON of an unknown
`schema_version` (named), `*.tmp` and `*.rejected` files left by
`save_artifact`, other files, entries that are not regular files (a FIFO),
symlinked directories and Windows junctions, which are not walked, and
symlinks to files outside the root, which are not read. A file or directory
that cannot be read is
listed as unreadable, as is a file that opens with a registered
`schema_version` but does not parse (a file of no registered kind is never
parsed). The text summary names the first one's reason; `--json` lists all.
- `--kind KIND` (repeatable, for example `--kind arms --kind eval`) and
`--subject NAME` (an exact, case-sensitive match) filter the rows.
- `--json` prints `root`, `artifacts`, `total` (before filtering), `skipped`,
`unknown_versions` and `unreadable`. Each artifact has `schema_version`,
`kind`, `run_id`, `subject`, `model`, `generated_at` (the time the artifact
records, `evaluated_at` for an eval; `null` when none), `path` (relative to
the root) and `size_bytes`.

**What it does not show yet.** No artifact records a `run_id` yet
([#277](https://github.com/intel/dokimi/issues/277)), so rows are not grouped
into runs and `run_id` is `null`. Files outside the root are not listed:

- `repo run` writes its batches to `repo-runs/`, and its files carry no
`schema_version`;
- `answers generate` and `eval score` write to `answers/` and `eval/` unless
`--run-id` is given, and `eval panel-score` writes to `eval/`;
- `questions generate` and `questions refine` write to `questions/`,
`repo reach` to `reach-out/`, `tools preference` to `tool_preference.json`
in the working directory;
- an output flag can point anywhere.

## `runs show`

```text
$ python cli.py runs show arms/onetbb.json
path: arms/onetbb.json
kind: arms (arms.v1)
schema: valid
subject: onetbb
model: gpt-6.1-sol
time: 2026-10-29T14:02:11Z
run_id: - (no artifact records one yet)
size: 3,503 bytes
contents: arms: 4, answers: 0
next to it: onetbb.md, plugin_delta.json (plugin_delta.v1)
```

The path is the one `runs list` prints, relative to the root. An absolute
path, or one from the working directory (`results/arms/onetbb.json`), works
too if it lies inside the root once symlinks are resolved: neither command
reads anything outside the root. The artifact is checked against the schema of
the version it declares. An artifact that fails the check prints
`schema: invalid, N errors` and each error, and the command still exits 0:
`runs show` reads, it does not gate. `contents` gives the length of the lists
the kind holds, and `next to it` the first 20 entries of the artifact's
directory. `--json` prints the fields of a `runs list` row plus `root`,
`schema`, `schema_valid`, `schema_errors`, `contents`, `siblings`,
`siblings_total` and `siblings_error`.

## Exit codes

| Case | Exit |
| --- | --- |
| Listed or shown, including an artifact that fails its schema | 0 |
| `results/` does not exist (`runs list` prints "no runs") | 0 |
| `DOKIMI_RUNS_DIR` does not exist, or the root is not a directory | 2 |
| `runs show`: no root, a path outside the root (or a symlink that leads out of it), a missing file, a directory, or a file that is not a readable artifact of a registered kind | 2 |
2 changes: 2 additions & 0 deletions dokimi/dokimi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
directories; [`task_formats`][dokimi.task_formats] names the format of other
eval-task files and directories (detection only).
- [`context`][dokimi.context] reads what a run's context window held.
- [`runs`][dokimi.runs] lists what the runs wrote: the artifacts under the runs
root (`results/`, or `DOKIMI_RUNS_DIR`).
- Content novelty: [`novelty`][dokimi.novelty]. Repositories as subjects:
[`repos`][dokimi.repos]. Subject scorecards, one per run:
[`subjects`][dokimi.subjects].
Expand Down
2 changes: 2 additions & 0 deletions dokimi/dokimi/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
repo,
report,
run,
runs,
subjects,
tasks,
tools,
Expand Down Expand Up @@ -143,6 +144,7 @@ def positive_int(value: str) -> int:
repo.register(sub, positive_int) # repo (repositories as subjects)
tools.register(sub, positive_int) # tools (tool-choice probes)
context.register(sub, positive_int) # context (context window of a run)
runs.register(sub, positive_int) # runs (the artifacts under the runs root)
backends.register(sub, positive_int) # backends (CLI providers)
data.register(sub, positive_int) # data (schema; M2 adds export, import and models)

Expand Down
Loading
Loading