Repository navigation
fix(eval): panel judge output goes through save_artifact (#277 B1b) - #450
Conversation
`eval panel score` wrote its incremental output with a plain json.dump, so the file had no schema_version and no run envelope (run_id, dokimi_version), and `runs list` could not see it. It is saved as an eval.v1 artifact now, on every question, with the same schema check as the other writers: a rejected save stops the run at that question and keeps the payload beside the output as panel.json.rejected. Tests: the envelope and scan visibility of the panel output, a rejected save keeps its payload, and a missing output directory is created. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
There was a problem hiding this comment.
🟡 Changes recommended
The release note and test reference use the invalid command eval panel score instead of the registered eval score panel and need correction.
1 open issue
What changed in this PR
1 new finding (nit).
This PR routes panel-judge output through the shared artifact writer, continuing #277’s run-envelope work and making panel results discoverable by runs list.
Changes:
- Replace manual incremental JSON writes with
save_artifact, adding schema validation and run metadata. - Add tests for the envelope, scan discovery, rejected payload preservation, and output-directory creation.
- Document the change in a changelog fragment.
| File | Description |
|---|---|
dokimi/tests/test_run_envelope.py |
Tests panel artifact metadata, discovery, rejection, and directory creation. |
dokimi/dokimi/eval/panel.py |
Saves incremental panel results through the shared artifact writer. |
dokimi/changelog.d/20261106_220000_napetrov_eval-panel-envelope.md |
Announces the panel output’s run envelope and validation. |
Review effort: Balanced
| @@ -0,0 +1,3 @@ | |||
| ### Fixed | |||
|
|
|||
| - **`eval panel score` output carries the run envelope**: its incremental output is saved through `save_artifact` as an `eval.v1` artifact, so it gets `schema_version`, `run_id` and `dokimi_version` and shows in `runs list`. A save the schema rejects stops the run on the first save, not after the last judge call. | |||
There was a problem hiding this comment.
Fixed in 0e68afd: the changelog fragment and the test docstring now say eval score panel, the registered command (evaluate.py:578).
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #450 +/- ##
==========================================
- Coverage 95.31% 95.31% -0.01%
==========================================
Files 378 378
Lines 50337 50333 -4
Branches 8679 8679
==========================================
- Hits 47981 47975 -6
- Misses 1481 1482 +1
- Partials 875 876 +1
🚀 New features to boost your workflow:
|
Review fix for PR intel#450 (Copilot): the registered command is `eval score panel`. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Already fixed in 0e68afd, as in the thread above. No change: this is the Codecov report, and it finds all modified lines covered (patch 100%). Nothing to act on here. |
antonwolfy
left a comment
There was a problem hiding this comment.
LGTM. Routing the panel output through save_artifact is the right fix: the file now carries eval.v1, run_id and dokimi_version, keeps the atomic write and the parent-directory creation, and a rejected save keeps its payload in panel.json.rejected. No findings.
What I ran, on Linux at e6a6510:
make ci-local: lint, mypy (378 files), deptry,schema_compat, changelog fragment, comment blocks and docs build pass. Two timeout errors in the full pytest run (test_import_architecture.py,test_audit_test_quality.py) pass when run alone (225 passed). The gitleaks install failed on a download error, so I did not scan the commits.- Mutation run on the three new tests and
test_panel.py: 12 mutants (wrong kind, dropped call, plainjson.dumpwith and withoutmkdir, swallowed validation error, renamedpanel_config, and others). 9 killed. The three survivors arerun_metadata,judge_modelandlibrary_name;test_baseline_compare.pycovers the first two, and nothing pinslibrary_name.
Checked, no issues
- Readers:
baseline savecopies the file as is, and_recorded_modelsreadsjudge_modelandpanel_configonly. Theeval.v1schema has noadditionalProperties: false, so the extra keys are harmless. - Other writers: no other
json.dumporos.replaceof a registered kind is left undereval/. - Rejected save: the
ArtifactValidationErroris not caught incmd_eval_score_panel, butrecords()already refuses a missing or non-string/integerquestion_idbefore any judge call, so the first save cannot be rejected through the CLI.
Conflict in dokimi/eval/panel.py (imports): keep re, drop os (unused after save_artifact), restore json (PR head had dropped it while json.dumps stays). Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Summary
Part of #94; continues #277 (B1). The other run-artifact writers already go through
save_artifact.A scan of the package for direct
json.dump/write_textof registered run kinds found no otherbypass: the remaining direct writes are reports, logs, caches, exports, or unregistered payloads
(no
schema_version), whichruns listdoes not read. #278 (groupruns listby run_id) is alreadyin #427 and is not repeated here.
Changes:
dokimi/eval/panel.py:_save_incrementalsaves throughsave_artifact;json/osimports dropped.dokimi/tests/test_run_envelope.py: the panel output is aneval.v1artifact with the envelope andis listed by
scan(the coderuns listuses); a rejected save (question_idNone) writes nothingand keeps
panel.json.rejectedwith its payload; a missing output directory is created.dokimi/changelog.d/: Fixed fragment.No CLI surface change (no flag, route or exit-code change); no
cli-interface-changelabel.Evidence
panel.py,pytest tests/test_run_envelope.py -k panel->2 failed(the rejected-save and envelope tests; the missing-directory test passes on base, as expected).
pytest tests/test_run_envelope.py tests/test_baseline_compare.py tests/test_panel.py->
209 passed(the panel path, including the existing fullevaluate_answerstest with realverdict rows, runs through the new save). Wider run before the last two tests were added:
tests/test_writers_stop_on_rejected_save.py tests/test_artifacts.py tests/test_runs_*.py tests/test_eval_judge.pyin the same batch passed.pytest tests:16746 passed, 31 skipped, 27 failed. 26 of the failures also fail on theunmodified base (
test_judge_sampling,test_llm_delegates,test_model_defaults,test_openai_compatible_route,test_repo_typecheck_grader): environment (locked litellm pricing,provider routes, typecheck grader), not this change.
test_process.py::test_sighup_kills_the_live_groups_toofailed once in the full run and passes alone (72 passed): flaky.
ruff checkon the changed files andmypyoneval/panel.pyandartifacts.py: clean.Merge Danger
Door: two-way. The change is a writer swap; reverting restores the old unstamped file.
Blast Radius:
eval score paneloutput only. The file gainsschema_version,run_idanddokimi_versionat the top and is now schema-checked on every incremental save: a record the eval.v1schema rejects stops the panel run at that question (the paid results so far stay on disk, the bad payload
goes to
panel.json.rejected). Readers that readevaluations/panel_configare unaffected;baseline savecopies the file as is.
🤖 Generated with Claude Code