Skip to content

refactor(eval): give answer and eval records one reader (eval/records.py) - #80

Open
napetrov wants to merge 18 commits into
intel:mainfrom
napetrov:refactor/eval-records
Open

napetrov wants to merge 18 commits into
intel:mainfrom
napetrov:refactor/eval-records

Conversation

@napetrov

@napetrov napetrov commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Part of #4.

Summary

Readers of answers.v1 / eval.v1 / arms.v1 records each picked their own keys. The judge, panel, reports, gates and judge-reliability readers listed below now go through one module, eval/records.py. It is the only code that knows the binary shape (with_docs/without_docs) and the arm shape (arms/scores, legacy inline evaluation.aggregate_score):

 dokimi/eval/
+├── records.py          # read(data) / load(path) -> Run(shape, conditions, baseline, meta, records)
+│                       #   Record: question_text, ground_truth, expected_skills, retrieval_hit,
+│                       #           answer(cond) -> Answer(text, docs, context), score(cond) -> aggregate | None
+│                       #   arm_answers / arm_scores: only `arms` / `scores` (the arm judges read these)
+│                       #   RecordError(ValueError): names the file and the record
 ├── judge.py            - answer["with_docs"].get("retrieved_docs")  + rec.answer(CONTEXT).context
 ├── panel.py            - answer.get("question_text") or .get("question") or .get("text")
 ├── diagnoser.py        - retrieval_hit(answer dict)  (moved into records)
 ├── ragas_eval.py, grounding.py, degrade.py, pairwise.py, checklist.py, cross_judge.py
 ├── arm_runner.py, arm_summary.py, plugin_delta.py, stats.py, interactions.py
 gate/trust_gate.py
 report/generator.py, eval_report.py, model_compare.py, arms_report.py
 dashboard/aggregator.py
 commands/evaluate.py, judge_reliability.py, baseline.py, questions.py, orchestrator/pipeline.py

That is about 40 reader sites in 24 files. Some are easy to miss: the judge's console summary, the report eval Q&A sidecar, the dashboard, baseline save/compare, stats.paired_vectors/arm_significance, the interaction contrasts, the arm runner's judge_context source count, and the answers branch of questions panel-review. A few raw-key reads remain and give the same result today: orchestrator/pipeline.py's step summary, judge_context.py's chunk text, and the raw metadata in model_compare.py and eval_report.py. AGENTS.md makes records.py the rule for new readers.

Values are read as written. An id stays the str/int it was (0 is an id) and an aggregate stays the int/float it was, so reports print what they printed before. The persisted names with_docs/without_docs do not change. The arm judges (eval pairwise, checklist, cross-judge) still read only arms answers and evaluations[].scores, as on main. The averages are untouched: reconciling them is the eval/compare.py PR.

Evidence

No number moves on well-formed files. I ran every migrated reader on main and on this branch with the same fixtures. The eval.v1 file has int and string ids, an id of 0, int and float aggregates, one question scored in one arm only, an errored question, panel agreement and a stamped retrieval_hit. There is also its answers.v1 file and three arms.v1 runs (current, a second model, legacy inline scores). Outputs compared: report generate (md and json), report eval (md and Q&A sidecar), the trust gate, the dashboard snapshot, the baseline summary, grounding, model-compare, plugin delta, cross-judge, pairwise, checklist, arm summaries, the degradation pool, and the judge/panel/arm runner outputs and prompts with a stubbed LLM. diff -r before after is empty. Both reviewers' differential runs agree.

Readers that read the wrong key (tests/test_record_readers.py). On main this file stops at collection because it imports dokimi.eval.records. With that module present, each test fails on main's reader as shown and passes now:

Test On main's reader
test_report_generate_shows_the_question_of_a_panel_eval_file ['', '', ''] == ['How do I use feature 0?', …]
test_report_eval_shows_the_question_of_a_panel_eval_file question cells blank, sidecar question_text: null
test_report_generate_skips_a_question_whose_panel_votes_all_failed TypeError: unsupported operand type(s) for +: 'float' and 'NoneType'
test_question_panel_review_reads_the_questions_of_an_answers_file [] == ['How do I use feature 0?', …]
test_model_compare_reads_a_legacy_run_with_a_null_arm AttributeError: 'NoneType' object has no attribute 'get'

Review fixes. Each test fails on the commit before its fix:

  • baseline save wrote the file and then crashed on an id-less record: test_baseline_save_refuses_a_malformed_eval_file_before_writing.
  • One bad answers record blanked the report eval sidecar: test_report_eval_sidecar_drops_only_the_malformed_answers_record.
  • cross-judge raised on unknown_q ids: test_stored_scores_read_an_arms_run_with_the_fallback_question_id.
  • An unscored arm was averaged as 0: test_baseline_summary_leaves_out_a_question_with_an_unscored_arm / …_unscored_baseline.
  • Unscored arms broke the report's top/bottom tables: test_report_top_deltas_leave_out_a_bool_aggregate, test_report_top_and_bottom_deltas_are_ordered.
  • A stray scores: null / arms: null hid a binary record's conditions: test_a_binary_record_with_a_stray_null_*.
  • The arm judges paid for binary files and took legacy inline scores as the stored judge:
    test_the_arm_judges_refuse_a_binary_answers_file_before_any_call[pairwise|checklist|cross-judge]
      at 6f1962b: with_docs/without_docs judged as two arms, exit 0
      now:        exit 2 naming the file, 0 judge calls, no output written
    test_cross_judge_on_a_legacy_inline_score_run_has_nothing_to_compare
      at 6f1962b: exit 0, inline scores taken as the stored judge
      now:        exit 2 "nothing to compare", as on main
    
    Also test_the_arm_judges_send_no_binary_answer_to_a_judge and test_cross_judge_takes_no_stored_measure_from_*.

The reader itself. tests/test_records.py covers the binary, arms and legacy shapes, ids and values as written, and every RecordError. It also pins join precedence: the evaluation's score over an inline one, metadata when both sides differ, conditions and raw order, eval-only questions, and the stamped retrieval_hit. It pins question_id over id too. Every join mutant from the review now fails the suite, and so do the _top_deltas order, baseline filter and arms: null mutants. records.py passes mypy --strict.

Checks. The full suite passes on two random seeds (31337, 2718): 5250 passed, 2 skipped. make ci-local CI_LOCAL_ARGS=--offline gives 0 failed, 14 passed, 7 skipped (network/docker). Changed-line coverage is 97% and records.py is at 100%. schema_compat is OK: the only schema change is the retrieval_hit description, which pointed at the removed diagnoser.retrieval_hit. The changelog fragment check passes.

Merge Danger

Door: two-way. No persisted format, schema version or flag changes, and no score changes for well-formed files, so a revert restores today's readers.

Blast Radius: malformed-input

  • A malformed record is an error that names it (answers.json answers[3]: missing question_id). This covers a missing id, an id that is neither a string nor an integer (question_id must be a string or an integer, got 1.5), a condition/arm payload that is neither an object nor null, and a bad expected_skill. Before, such a record was skipped, given a made-up id, or crashed a paid run part-way. eval score and arms run check before their first call. report generate/eval, model-compare and plugin-delta exit 1 naming the file. baseline save writes nothing. The report eval sidecar drops only the bad record. The dashboard leaves the file out with a warning.
  • question_id wins over id when a hand-merged record carries both (utils.question_id preferred id).
  • A non-numeric aggregate ("80", a bool) is unscored everywhere. The dashboard used to parse "80". baseline save and the report's top/bottom tables leave out a question with an unscored arm.
  • baseline compare matches ids as strings: an int 1 and a string "1" are the same question.
  • eval cross-judge --judges on a binary answers file exits 2 before any call. It used to print an empty table. pairwise and checklist already exited 2 on such a file.
  • Signatures: diagnose(context_answer, delta) takes the context Answer and the delta. diagnoser.retrieval_hit moved to Record.retrieval_hit. The private stats._aggregate_of and plugin_delta._scores_by_question are gone. A patch on Judge._evaluate_answer_pair receives a Record.
  • Busy files for the next wave-4 PR (eval/compare.py): report/*, eval/stats.py, gate/trust_gate.py.

Checklist

  • Tests added/updated and pytest passes
  • ruff, mypy, and schema checks pass
  • Docs updated (or N/A) — see the docs-drift checklist in CONTRIBUTING.md
  • No ad-hoc run artifacts committed
  • New files carry the SPDX license header (or are exempt in .github/.licenserc.yaml)

🤖 Generated with Claude Code

….py)

No module owned the answers.v1 / eval.v1 / arms.v1 record shape, so every
consumer picked its own keys, and some picked keys no writer writes.

eval/records.py is now the one reader: read()/load() take any artifact
(wrapped or bare list, binary or N-arm, arms.v1 answers and evaluations
joined by question id, the legacy inline evaluation.aggregate_score) and
return a Run (shape, conditions, baseline, meta) of Records: question text,
ground truth, expected_skills, per-condition Answer (text, docs, context)
and Score (aggregate, agreement), retrieval_hit (an evaluation's stamped
value wins over what an answer implies). Values are read, not normalised:
an id stays the str or int it was written as, an aggregate the int or float.
Malformed input is one RecordError (a ValueError) naming the file and the
record. Persisted formats are unchanged; with_docs/without_docs stay the
binary condition names.

Readers migrated: judge (and its summary), panel, RAGAS, grounding,
diagnoser, trust gate, report generate, report eval (and its Q&A sidecar),
dashboard aggregator, baseline save/compare, model-compare (both formats),
arms report, plugin delta, cross-judge, pairwise, checklist, degradation
pool, arm runner judge/summary/judge_context sources, arm summaries
(agentic usage, skill selection, cost), stats.paired_vectors /
arm_significance, interaction contrasts, the answers loaders of eval
score / panel-score / ragas / grounding / judge-validity, judge_reliability
and the pipeline, and questions panel-review's answers branch.

No published number moves: a before/after run of every migrated reader on
fixture eval.v1, answers.v1 and arms.v1 files (report generate md+json,
report eval md+sidecar, trust gate, dashboard, model-compare, plugin delta,
arm summaries, judge and panel outputs with a stubbed LLM, ...) is
byte-identical.

Deliberate behaviour change: a record with no question_id (or a payload
that is neither an object nor null, or an expected_skill that is not a
name, list of names or null) is a RecordError instead of being skipped,
renamed or crashing later; the judge checks every record, question text
included, before its first call.

Fixed (wrong keys or values read):
- report generate / report eval read question_text only; the panel writes
  `question`, so a panel run's tables were blank.
- report generate summed a panel aggregate of null (every vote failed)
  and raised TypeError.
- questions panel-review read question/text from an answers file, whose
  records carry question_text, and reviewed nothing.
- model-compare's legacy path called .get on a null arm payload.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@codecov-commenter

codecov-commenter commented Oct 16, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 96.74355% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.71%. Comparing base (6c6d5b9) to head (c06126a).

Files with missing lines Patch % Lines
dokimi/dokimi/commands/baseline.py 88.46% 3 Missing ⚠️
dokimi/dokimi/commands/judge_reliability.py 86.95% 3 Missing ⚠️
dokimi/dokimi/commands/questions.py 57.14% 3 Missing ⚠️
dokimi/dokimi/eval/ragas_eval.py 76.92% 1 Missing and 2 partials ⚠️
dokimi/dokimi/report/model_compare.py 92.10% 1 Missing and 2 partials ⚠️
dokimi/dokimi/commands/evaluate.py 93.75% 2 Missing ⚠️
dokimi/dokimi/eval/cross_judge.py 93.54% 1 Missing and 1 partial ⚠️
dokimi/dokimi/orchestrator/pipeline.py 50.00% 1 Missing and 1 partial ⚠️
dokimi/dokimi/eval/degrade.py 85.71% 0 Missing and 1 partial ⚠️
dokimi/dokimi/eval/panel.py 92.85% 0 Missing and 1 partial ⚠️
... and 1 more
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #80      +/-   ##
==========================================
+ Coverage   90.30%   90.71%   +0.41%     
==========================================
  Files         206      207       +1     
  Lines       24356    24655     +299     
  Branches     3923     3944      +21     
==========================================
+ Hits        21995    22367     +372     
+ Misses       1695     1634      -61     
+ Partials      666      654      -12     
Files with missing lines Coverage Δ
dokimi/dokimi/commands/arms.py 89.04% <100.00%> (+1.08%) ⬆️
dokimi/dokimi/commands/report.py 73.14% <100.00%> (+5.80%) ⬆️
dokimi/dokimi/dashboard/aggregator.py 90.07% <100.00%> (+1.83%) ⬆️
dokimi/dokimi/eval/arm_runner.py 93.68% <100.00%> (+1.17%) ⬆️
dokimi/dokimi/eval/arm_summary.py 97.41% <100.00%> (+1.68%) ⬆️
dokimi/dokimi/eval/checklist.py 93.71% <100.00%> (+0.03%) ⬆️
dokimi/dokimi/eval/diagnoser.py 100.00% <100.00%> (ø)
dokimi/dokimi/eval/grounding.py 100.00% <100.00%> (ø)
dokimi/dokimi/eval/interactions.py 97.77% <100.00%> (+0.05%) ⬆️
dokimi/dokimi/eval/judge.py 94.92% <100.00%> (+0.01%) ⬆️
... and 20 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 2fe185b. The reader is sound and the migration matches main on well-formed files: I diffed report, gate, baseline, grounding, ragas, cross-judge and arm outputs against main on the same fixtures and found no drift. The problems are in the new strictness. Two commands now lose data or leave state behind on an id-less record, and the reader's join is almost untested. I'd fix the two bugs and the cross-judge crash before merge.

What I ran, on a Linux review host at 2fe185b:

  • make ci-local: 19 passed, 0 failed, 4 skipped (docker/network/semgrep, job conditions). 4,713 tests, changed-line coverage 97%. Python 3.13, so the matrix legs on 3.11/3.12/3.14 did not run.
  • Differential runs of main and the PR head on the same fixtures (ids of 0, int and string ids, null arms, legacy inline scores, duplicate and missing ids), with the LLM transport mocked, for the reproductions below.
  • A mutation run on records.py: 119 mutants against test_records.py and test_record_readers.py, 73 survive; three of the join ones also pass the full suite (see the inline comment).
  • schema_compat vs the base: OK (the retrieval_hit description change is allowed in place). The changelog fragment name, categories and headers are valid. ruff, mypy and mypy --strict on records.py are clean.

Beyond the inline comments

  1. The "every reader goes through records.py" claim in the PR text and AGENTS.md is too strong. A few sites still pick their own keys: orchestrator/pipeline.py:304 reads e["with_docs"]["aggregate"], eval/judge_context.py:227 reads context_chunks/snippet from answer.raw, and report/model_compare.py:567 and report/eval_report.py:444 read rec.raw.get("question_text") and friends instead of the Record fields. None of them gives a wrong result today. Nothing enforces the rule for the next reader, though, so a test that greps dokimi/ for these keys outside records.py would keep it true.
  2. Nit: the five "fails on main" tests do not fail with the quoted messages as committed. test_record_readers.py:20 imports dokimi.eval.records at the top, so on main the file stops at collection with ModuleNotFoundError. The quoted assertions only appear once the import exists.

Checked, no issues

  • Numbers: report generate (md and json), report eval markdown, the trust gate verdict and every check, baseline save/compare on valid files, model-compare, arms report, eval ragas/grounding/panel-score and the arm runner's judge/summaries give the same output as main on well-formed files, including id 0, int vs float aggregates, one-arm-scored questions and NaN deltas.
  • Panel files: the five listed regression tests pass on the head and fail on main once the import exists; the panel question key and an all-failed panel question now read correctly.
  • Schema and docs: only the retrieval_hit description changed in eval.v1.json; no stale references to diagnoser.retrieval_hit, stats._aggregate_of, plugin_delta._scores_by_question or the old diagnose( signature remain in docs, comments or tests. No generated artifacts, new files carry the SPDX header, no new doc page is needed for the nav.
  • Tests are offline and test_record_readers.py builds its eval file with the real panel writer.

Comment thread dokimi/dokimi/commands/baseline.py
Comment thread dokimi/dokimi/report/eval_report.py Outdated
Comment thread dokimi/dokimi/eval/cross_judge.py Outdated
Comment thread dokimi/dokimi/eval/judge.py Outdated
Comment thread dokimi/dokimi/records.py
Comment thread dokimi/dokimi/eval/records.py Outdated
Comment thread dokimi/changelog.d/20261016_120000_eval_records.md Outdated
claude and others added 2 commits October 16, 2026 23:49
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Review follow-ups for eval/records.py:
- baseline save computes its summary before writing and exits 1 on a
  RecordError, so a bad file leaves no orphan baseline.
- report eval's Q&A sidecar reads answers per record; a bad one is left
  out with a warning instead of blanking every answer.
- cross-judge's stored scores read answers and evaluations without a
  join, so arms files with the unknown_q fallback id still read; the
  command turns a RecordError into a clean exit.
- eval score exits 1 naming the record when evaluate_answers refuses it.
- a question_id of the wrong type says so instead of 'missing'.
- tests for the binary join's precedence and fallbacks.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW8mAicb3ibXdzHMV83wDn
@napetrov
napetrov force-pushed the refactor/eval-records branch from 69c20eb to 895727b Compare October 16, 2026 23:49
The records reader refuses such an id, so `arms run` paid for every
answer and then lost the run in its own summaries, writing nothing.
The questions loader now refuses it before the first call.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
`report generate` and `report eval` let RecordError escape as a
traceback; `report generate`, `report model-compare` and
`arms plugin-delta` named the record but not the file, though the
last two read several. Each now exits 1 with `<file> <section>[i]: ...`.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
@xaleryb

xaleryb commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Differential run main vs 895727b over the fixtures, 199 stored artifacts and pipeline artifacts produced with a stub LLM: well-formed files give identical output (dashboard identical, 58 model-compare runs byte-identical), eval score checks every record before the first judge call, #10's retrieval_hit semantics hold over a 3000-record fuzz, and the four bugs from the earlier review stay fixed.

Pushed (each test fails on this head):

  • fd82b7d: arms run paid for every answer (8 calls) and then lost the run on a float or bool question id; the id is now refused before the first call, which also covers the matrix run.
  • 72246ec: report generate and report eval ended in a raw traceback on a malformed record, and model_compare and arms plugin-delta named the record but not the file; all four now exit 1 with Error: <file> ....

Open, your call:

  1. baseline save on an eval with an aggregate null or "80" next to a numeric delta: exit 0 with with=52.33, while the trust gate on the same file uses 62.8 (n=5); report generate markdown on a bool aggregate raises TypeError at generator.py:395 (main rendered it). Unscored arms should stay out of the mean.
  2. eval cross-judge on a legacy inline-score arms file: exit 2 becomes 0 with stored-judge verdicts, and with --judges the Holm p goes 0.727 to 1; binary answers files now also get paid checklist, pairwise and cross-judge calls. Keep those readers arms/evaluations-only, or announce it.
  3. A binary record with a stray scores: null silently loses its with/without scores: trust gate PASS n=12 becomes FAIL n=4.

#69 and #73 conflict with this PR in cross_judge.py, judge_reliability.py and judge_context.py, and some hunks git merges cleanly still break: rec["question_id"] in arm_runner._score raises TypeError (16 tests) and data in cmd_eval_judge_validity raises NameError (6 tests).

napetrov and others added 2 commits October 17, 2026 05:27
Resolve conflicts with intel#78/intel#69/intel#43:
- arms: keep main's questions.v2 loader; keep the id-type check before the run.
- cross-judge: keep main's judge_materials gate; stored scores and the
  pairwise/checklist gate read records without a join, and a RecordError
  exits 1 naming the record (tests updated from exit 2).
- arm_runner: digest lookup uses Record.question_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW8mAicb3ibXdzHMV83wDn
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Review follow-ups (xaleryb):
- baseline save averaged a null/"80" aggregate as 0 next to a numeric
  delta; it now counts only questions with both arms scored.
- report generate's top/bottom delta rows left out unscored arms, which
  raised TypeError on a bool aggregate.
- a binary record with a stray `scores: null` was read as an arms record
  and lost its with/without scores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW8mAicb3ibXdzHMV83wDn
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@xaleryb

xaleryb commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

e6290bc covers items 1 and 3, so we dropped our own commits for them. Item 2 (eval cross-judge on legacy inline-score arms files, and paid checklist, pairwise and cross-judge calls on binary answers files) is still your call. The merge with main took in #69; the conflict with #73 is still ahead.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at e6290bc. The follow-up commits fix the reported bugs: baseline save refuses a bad file before writing, the Q&A sidecar drops only the bad record, cross-judge reads unknown_q files and exits 1 on a bad record, and eval score exits 1 instead of a traceback. Two things are still worth fixing before merge: eval cross-judge, eval checklist and eval pairwise accept more file shapes than on the base and the changelog does not say so (@xaleryb's item 2), and the join precedence rules are still not pinned.

What I ran, on a Linux review host at e6290bc:

  • make ci-local: 19 passed, 0 failed, 4 skipped (docker/network/semgrep, job conditions). 5,066 tests, changed-line coverage 97%. Python 3.13, so the matrix legs on 3.11/3.12/3.14 did not run.
  • Differential runs of the base and the head on the same fixtures (well-formed and malformed eval, answers and arms files, with the LLM transport mocked): baseline save/compare, trust gate, report generate and report eval, dashboard, model-compare, arms report, arms plugin-delta, grounding, cross-judge. Well-formed files give identical output.
  • Mutation runs on records.py and the follow-up code: 134 mutants, 68 of which pass the full suite (5,065 passed).
  • schema_compat vs the base: OK (only the retrieval_hit description changed). The changelog fragment passes the checker, new files carry the SPDX header.

1. I agree with @xaleryb's open item 2: legacy inline-score and binary answers files now go through eval cross-judge, eval checklist and eval pairwise, and the changelog does not say so. It still holds at this head; details in the inline comment at cross_judge.py:152.

2. Tests for the follow-up code leave real regressions through. Mutation check on the head, full suite each time (5,065 passed, 2 skipped):

  • records.py:280: "arms" in item or item.get("scores") is not None passes. test_a_binary_record_with_a_stray_null_scores_keeps_its_scores pins scores: null only. A binary answers record with "arms": null and with_docs/without_docs payloads would be read as an arms record with no conditions and lose both answers.
  • generator.py:209: deltas.sort(reverse=False, ...) in _top_deltas passes, so "top improvements" would list the worst questions. test_report_top_deltas_leave_out_a_bool_aggregate only checks which ids appear, with one valid row. Add a test with three scored rows, limit=2, and assert the order in both _top_deltas and _bottom_deltas.
  • baseline.py:38-42: dropping the e.score(BASELINE) is not None half of the new filter passes. test_baseline_summary_leaves_out_a_question_with_an_unscored_arm nulls only the context side. Add a row with without_docs.aggregate: null and a numeric delta.

Still open from my earlier review:

  • records.py:370 (join tests): the new test pins the stamped retrieval_hit, the eval-only question and the one-sided metadata fallbacks. scores={**e.scores, **a.scores} still passes the full suite, and so do the union order and one-sided sources of conditions, the raw merge order, and the persona/ground_truth/error/diagnosis/expected_skill preferences when both sides carry a different value. Add one test where the answers record has an inline legacy score and the evaluation a different one for the same arm, and one where both sides carry different metadata.
  • judge.py:297 (empty question_text): the eval score traceback is gone. cross-judge --judges, pairwise, checklist and ArmRunner.judge still send "" to the judge for the same record, and degrade.py skips it. The changelog states the rule for eval score only; #81 takes it up for the other paths.

Checked, no issues

  • Earlier bugs: baseline save with an id-less record exits 1 naming the file and writes nothing; the report eval sidecar keeps the other answers and warns; an arms file with unknown_q for every question reads in cross-judge (stored and --judges) as on the base; a RecordError in cross-judge exits 1 without a traceback in the stored, --judges, --pairwise and --checklist paths.
  • Numbers: report generate md and json, report eval, the trust gate, baseline save/compare, model-compare, arms report, the dashboard and plugin-delta match the base on well-formed files. A "80" or bool aggregate is unscored, as the changelog says.
  • Question ids: question_id that is present but not a string or an integer now reports question_id must be a string or an integer, got 1.5. The question_id-over-id rule is in the changelog.
  • Repo rules: changelog fragment valid, no generated artifacts, no new doc page needed for mkdocs.yml, tests offline.

Comment thread dokimi/dokimi/eval/cross_judge.py Outdated
Comment thread dokimi/dokimi/report/generator.py Outdated
napetrov and others added 2 commits October 17, 2026 17:10
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…s-judge/checklist/pairwise

- join: tests for the evaluation's score over an inline legacy score, one-sided
  scores, conditions order, raw merge order, and every metadata preference
  when both sides differ; answers-only/eval-only order; duplicate answers.
- records: a binary record with arms: null keeps its answers.
- report: top/bottom deltas order with limit; baseline summary leaves out a
  question with an unscored baseline.
- changelog and --answers help: binary answers files are read as arms
  with_docs/without_docs by cross-judge, checklist and pairwise, and incur
  paid judge calls; a CLI test covers pairwise on such a file.
- generator._columns docstring: the deltas are the persisted ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW8mAicb3ibXdzHMV83wDn
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
napetrov added a commit that referenced this pull request Oct 17, 2026
…#81)

* fix(judge): refuse a record with no question text on every judge path

evaluate_answers, the panel, ArmRunner.judge, pairwise, checklist,
score_measure and the degradation pairs disagreed on a record whose
question text is missing or blank: some sent "" to the judge, degrade
skipped the record, evaluate_answers recorded a per-question error only
for a missing key. A shared helper (dokimi.eval.question_text) now checks
every record before the first judge call and raises EmptyQuestionError
naming the record; the CLI prints "Error: ..." and exits 1.

Follow-up to review on #80.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW8mAicb3ibXdzHMV83wDn
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

* fix(arms): refuse a blank question before any answer is paid for

The question-set reader accepts a whitespace-only question, and
`arms run --judge` reached the judge's refusal only after every arm had
answered every question; the run then exited 1 without writing its
output, so the paid answers were lost. Check the questions where the
judge is built, before the sampling probe and the first answer.

Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>

* fix(eval): refuse a blank question only where it is judged, before the probe; exit 2

- RAGAS: check rows before the first RAGAS call; the command wrapper lets
  EmptyQuestionError through instead of turning it into a warning.
- pairwise/checklist/cross-judge: the jobs are built (and checked) before the
  --judge-temperature sampling probe, so no billed call precedes the refusal.
- A record that would never be judged (answer-error, empty answers, no points,
  no baseline, under the length floor) is skipped, not refused.
- Exit 2, the input-error code of these commands; 1 stays the verdict code.
- arms run/matrix: the refusal is no longer reported as an arm setup error;
  the runner reads the question with the guard's key order; ArmRunner.judge
  reads the text once for the call and the row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW8mAicb3ibXdzHMV83wDn
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

---------

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Alexander Rybkin <alexander.rybkin@intel.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Alexander Rybkin <alexander.rybkin@intel.com>
Resolve conflicts with intel#81 (empty-question rule): the judge paths keep the
record objects and call require_question_texts / require_question_text on
the parsed records before the first judge call; the duplicate RecordError
question check in Judge.evaluate_answers is replaced by intel#81's
EmptyQuestionError (exit 2). checklist_jobs, pairwise_jobs and
scored_answers read records. A record without question_id stays a
RecordError (intel#80) in the degradation pairs; unreproducible_arms keeps
intel#87's skip of malformed records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RW8mAicb3ibXdzHMV83wDn
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
cross-judge, checklist and pairwise read answers under `arms` and stored
scores under `evaluations[].scores` before this PR. Through the shared
reader they also took a binary answers file's with_docs/without_docs as
two arms (paid judge calls where the base made none) and a legacy inline
`arms[name].evaluation` as the stored judge (cross-judge exit 2 became 0,
and the extra measure moved the Holm-adjusted p of --judges).

- records: arm_answers / arm_scores / has_arms read only those fields.
- scored_answers, pairwise_jobs, checklist_jobs, stored_measure and
  unreproducible_arms use them.
- eval pairwise, eval checklist and eval cross-judge --judges exit 2
  naming the file, before any judge call, when no record has `arms`.
- changelog: the widening entry is replaced by the refusal; the
  --answers help is the base text again.
- test: question_id wins over id, id is the fallback (pins the rule
  the changelog states).

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov

Copy link
Copy Markdown
Contributor Author

@antonwolfy @xaleryb, thanks for both rounds. Every point is now addressed at 1892bf4 (main is merged in at 6f1962b, #81 included). Could you re-review?

Point Status
baseline save wrote the file, then crashed on an id-less record 895727b: test_baseline_save_refuses_a_malformed_eval_file_before_writing
One bad record blanked the report eval sidecar 895727b: test_report_eval_sidecar_drops_only_the_malformed_answers_record
stored_measure/unreproducible_arms raised on unknown_q ids 895727b: test_stored_scores_read_an_arms_run_with_the_fallback_question_id
eval score traceback on an empty question_text; one rule across judge paths 895727b (traceback); #81 for every judge path, merged in
join barely tested, precedence unpinned 2e3b68a, 895727b: every reported join mutant now fails test_records.py
Wrong-type id reported as "missing"; question_id over id 895727b (message); 1892bf4 pins the precedence the changelog states
Fragment omitted non-numeric aggregate, diagnose(), string-id compare, unknown_q 895727b, 2e3b68a
arms run paid for answers, then lost the run on a float/bool id fd82b7d (xaleryb)
Report/model-compare/plugin-delta tracebacks, or no file named 72246ec (xaleryb)
Unscored arm averaged in baseline save; bool aggregate in the report tables (xaleryb 1) e6290bc
Stray scores: null / arms: null on a binary record (xaleryb 3, antonwolfy records.py:280) e6290bc, 2e3b68a
_top_deltas order and the baseline-filter mutants 2e3b68a: test_report_top_and_bottom_deltas_are_ordered, …_unscored_baseline
_columns docstring vs code 2e3b68a
Cross-judge/checklist/pairwise take binary and legacy inline-score files (xaleryb 2) 1892bf4: refused, not documented. The arm judges read arms and evaluations[].scores only, as on main. A file with no arms exits 2 before any judge call, and a legacy inline-score file has no stored measure again.
"Every reader goes through records.py" is too strong PR text now names the remaining raw-key reads; AGENTS.md states the rule for new readers. I did not add the grep guard: writers and other formats share the keys, so it needs an allow-list. It can be a follow-up if you want it.
"Fails on main" tests stop at collection PR text now says so

The one change left vs main for the refusal is in the fragment: cross-judge --judges on a binary file exits 2, where it printed an empty table with no judge call.

Locally the full suite passes on two seeds (5250 passed, 2 skipped). make ci-local --offline has 0 failed, changed-line coverage is 97%, and records.py is at 100%.

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 1892bf4. The refusal in 1892bf4 does what the thread asked: pairwise, checklist and cross-judge --judges exit 2 before any call on a file with no arms, and a legacy inline-score file has no stored measure again. One regression is left from the same family: report model-compare now treats a binary file's with_docs/without_docs as arms.

What I ran, on a Linux review host at 1892bf4:

  • make ci-local: 19 passed, 0 failed, 4 skipped (docker/network/semgrep, job conditions). 5,251 tests, changed-line coverage 97%. Python 3.13, so the matrix legs on 3.11/3.12/3.14 did not run.
  • Differential runs of the base and the head with the LLM transport mocked: the arm judges' pure functions (scored_answers, pairwise_jobs, checklist_jobs, stored_measure, unreproducible_arms) over 25 cases, and the three arm-judge commands over 17 file shapes. Output, exit code and judge-call count match the base on well-formed arms files, unknown_q ids, id 0, null arms and duplicate ids.
  • Reproductions for the two code findings below, and a check of report generate, trust, baseline save and the dashboard against the base on well-formed files: no drift.

Still open from my earlier review: nothing. The eval score traceback, the join mutants, the arm judges' file shapes and the _columns docstring are all fixed at this head.

Checked, no issues

  • Arm judges: all three call sites of _require_arms sit after _load_answers, so a RecordError still exits 1 first. Binary files exit 2 with 0 judge calls on pairwise, checklist and cross-judge --judges. Making arm_answers or arm_scores return the binary fields fails the new tests.
  • Join: swapping the precedence of text, category, scores, raw order, the stamped hit or the duplicate-id checks fails the suite.
  • Repo rules: with_docs/without_docs keys and schema versions are unchanged (only the retrieval_hit description), the changelog fragment passes, new files carry the SPDX header, no generated artifacts, tests are offline.

Comment thread dokimi/dokimi/report/model_compare.py Outdated
Comment thread dokimi/dokimi/eval/arm_runner.py Outdated
Comment thread dokimi/changelog.d/20261016_120000_eval_records.md Outdated
Copilot AI balanced review requested due to automatic review settings October 19, 2026 14:04
non_baseline_arms and scored_arms read rec.conditions which for binary files
includes with_docs/without_docs, conditions not treatment arms. This caused
check_run_consistency to falsely report two binary files as measuring different
treatments.

Fix: read only arm_scores and arm_answers, which exclude binary conditions.
Add test that runs check_run_consistency on two binary files.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
ArmRunner.judge looped over rec.conditions, which for binary records includes
with_docs/without_docs conditions that are not treatment arms. This caused
binary records to be judged as two-armed.

Fix: loop over arm_answers(rec) instead, which returns only treatment arms.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The record goes through EmptyQuestionError handler which exits 2, not 1.
RecordError exits 1.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

No open issues 🎉

What changed in this PR

Refactors all readers of answers.v1 / eval.v1 / arms.v1 artifacts to go through a single canonical parser (dokimi.eval.records), ensuring consistent key handling, shape detection, and error reporting across the judge/panel/reports/gates/dashboard/CLI.

Changes:

  • Added dokimi/eval/records.py as the single source of truth for reading/joining/validating record shapes and values.
  • Migrated ~40 call sites (reports, gates, CLI commands, eval helpers) to consume Record/Run via records.read/load/records.
  • Added extensive regression and reader-shape tests + updated docs/schema/changelog to reflect the new contract.
File Description
dokimi/​tests/​test_writers_stop_on_rejected_save.py Updates writer-stop test to use Record-based judge callback inputs.
dokimi/​tests/​test_stats.py Aligns stats fixtures with arms evaluation record shape (adds question_id).
dokimi/​tests/​test_skill_selection.py Updates skill-selection fixtures and asserts RecordError on malformed arm payloads.
dokimi/​tests/​test_records.py New comprehensive unit tests for dokimi.eval.records parsing/joining/errors.
dokimi/​tests/​test_record_readers.py New regression tests for previously-wrong key reads + malformed-record behaviors.
dokimi/​tests/​test_model_compare.py Adds regression for binary run consistency; tightens malformed input handling.
dokimi/​tests/​test_matrix_rollup.py Ensures arms run refuses unusable question IDs before any paid calls.
dokimi/​tests/​test_judge_reliability.py Updates reliability tests to expect early refusal on malformed IDs.
dokimi/​tests/​test_empty_question_rule.py Updates degradation-pair behavior to refuse missing IDs via RecordError.
dokimi/​tests/​test_diagnoser.py Migrates diagnoser tests to Answer/delta API and records fixtures.
dokimi/​tests/​test_artifact_fixtures.py Updates fixtures to account for Record objects passed into judge hooks.
dokimi/​tests/​test_arms_report.py Ensures arms report fixtures include question_id for record parsing.
dokimi/​tests/​test_arm_runner.py Updates arm-runner fixtures to include question_id.
dokimi/​tests/​test_agent_runner.py Updates agent-runner fixtures to include question_id.
dokimi/​dokimi/​schemas/​eval.v1.json Updates retrieval_hit schema description to reference eval.records.
dokimi/​dokimi/​report/​model_compare.py Migrates model-compare parsing/extraction to Record-based reader.
dokimi/​dokimi/​report/​generator.py Migrates report generator to parse via records and handle unscored arms safely.
dokimi/​dokimi/​report/​eval_report.py Migrates eval report to Record reader; improves Q&A sidecar resilience.
dokimi/​dokimi/​report/​arms_report.py Migrates arms report scoring table + agentic fallback reading via records.
dokimi/​dokimi/​orchestrator/​pipeline.py Uses records.read/load to avoid stale disk reads and unify parsing.
dokimi/​dokimi/​gate/​trust_gate.py Migrates trust gate computation to Record inputs and consistent scoring rules.
dokimi/​dokimi/​eval/​stats.py Migrates paired-vector/significance helpers to consume RecordLike.
dokimi/​dokimi/​eval/​records.py New canonical record reader/joiner enforcing shapes and emitting RecordError.
dokimi/​dokimi/​eval/​ragas_eval.py Migrates RAGAS evaluator to read answers via records/Record APIs.
dokimi/​dokimi/​eval/​plugin_delta.py Uses records to validate and compute deltas; wraps RecordError as domain error.
dokimi/​dokimi/​eval/​panel.py Migrates panel answer evaluation to Record inputs and Record.retrieval_hit.
dokimi/​dokimi/​eval/​pairwise.py Migrates pairwise job generation to Record inputs and arm-only answers.
dokimi/​dokimi/​eval/​judge_validity.py Updates validity runner signatures to accept RecordLike.
dokimi/​dokimi/​eval/​judge_sequential.py Updates sequential runner signatures to accept RecordLike.
dokimi/​dokimi/​eval/​judge.py Migrates judge evaluation to Record parsing and new diagnose(answer, delta) API.
dokimi/​dokimi/​eval/​interactions.py Migrates interaction statistics to Record parsing and numeric-safe extraction.
dokimi/​dokimi/​eval/​grounding.py Migrates grounding evaluation to use Record.answer(CONTEXT).context.
dokimi/​dokimi/​eval/​diagnoser.py Changes diagnoser API to (context_answer, delta) and migrates summarization to records.
dokimi/​dokimi/​eval/​degrade.py Migrates degradation pool/pair building to use canonical record parsing.
dokimi/​dokimi/​eval/​cross_judge.py Migrates cross-judge to Record parsing; adds unjoined-reader helper for legacy runs.
dokimi/​dokimi/​eval/​checklist.py Migrates checklist job generation to Record parsing + typed question IDs.
dokimi/​dokimi/​eval/​arm_summary.py Migrates arm summary aggregations to Record parsing.
dokimi/​dokimi/​eval/​arm_runner.py Migrates arm judging + summaries to Record parsing and consistent validation.
dokimi/​dokimi/​dashboard/​aggregator.py Migrates dashboard snapshot reading to canonical evaluator record parsing.
dokimi/​dokimi/​commands/​report.py Catches RecordError and exits cleanly with consistent messaging.
dokimi/​dokimi/​commands/​questions.py Reads questions from answers artifacts via records.read(section="answers").
dokimi/​dokimi/​commands/​judge_reliability.py Uses canonical answers loader + rejects binary files for arm-judge commands.
dokimi/​dokimi/​commands/​evaluate.py Centralizes answers loading via records.load(section="answers") and handles RecordError.
dokimi/​dokimi/​commands/​baseline.py Uses canonical evaluation parsing for summaries/compare; rejects malformed evals early.
dokimi/​dokimi/​commands/​arms.py Adds preflight validation for question id types to avoid paid-work loss.
dokimi/​docs/​architecture.md Documents eval/records.py as the single reader for record artifacts.
dokimi/​changelog.d/​20261016_190000_napetrov_empty-question-rule.md Updates changelog to reflect RecordError behavior for missing IDs.
dokimi/​changelog.d/​20261016_120000_eval_records.md New changelog entry describing reader refactor + behavior changes.
AGENTS.md Adds contributor guidance: all record reads must go through eval/records.py.

Review effort: Lite

Copilot AI balanced review requested due to automatic review settings October 19, 2026 14:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Null-score precedence, omitted null arms and whitespace question fallback can alter evaluation results.

3 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Whitespace-only question text bypasses valid fallback

dokimi/​dokimi/​eval/​records.py:501

A whitespace-only question_text masks a valid fallback question or text. require_question_texts() accepts the fallback, but the single judge, panel, arm runner and RAGAS then use rec.question_text, sending only whitespace to grading calls. Match question_text_of()'s nonblank check so validation and the prompt use the same question.

Review effort: Balanced

Comment thread dokimi/dokimi/eval/records.py
Comment thread dokimi/dokimi/eval/records.py Outdated
Comment thread dokimi/dokimi/eval/arm_runner.py Outdated

@antonwolfy antonwolfy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 5573776. The three fixes from my last review hold: model-compare no longer sees with_docs/without_docs as arms, ArmRunner.judge no longer judges a binary record, and the changelog says exit 2. The same fixes introduced two regressions on null payloads, and neither new test would catch them. I'd fix these before merge.

What I ran, on a Linux review host at 5573776:

  • make ci-local: 19 passed, 0 failed, 4 skipped (docker/network/semgrep, job conditions). 5,252 tests passed, 1 skipped.
  • Differential runs of the base, 1892bf4 and the head for non_baseline_arms, scored_arms, check_run_consistency, extract_scores and report model-compare over 15 artifact shapes (current and legacy arms files, null arms, binary files, answers-only, evaluations-only), with the LLM transport mocked. Only the all-null-treatment shape differs, see the inline comment.
  • ArmRunner.judge with a mocked judge on records with a null skill arm and a null baseline arm, base vs head.
  • Mutation runs: the model_compare.py fix reverted fails the new test; the arm_answers(rec) loop reverted to rec.conditions passes the full suite (5,251 passed, 2 skipped).

I agree with @copilot-pull-request-reviewer on arm_runner.py:451 (null arms dropped), records.py:296/:366 (scores.X: null does not clear a legacy inline score) and records.py:501 (whitespace-only question_text). New evidence:

  • skill: null: the head drops skill from scores, so extract_scores(force_treatment_arm="skill") exits with "not found"; with baseline: null the question leaves the comparison (base 1 row, head 0).
  • paired_vectors on the legacy-inline shape gives ([], []) on the base and ([60.0], [80.0]) on the head.
  • question_text: " " with question: "Real?": the base sent Real?, the head sends ' ' to the judge, because require_question_texts strips and Record.question_text does not.

Checked, no issues

  • Exit codes: EmptyQuestionError ends in exit 2 (cli.py:97-101) and a RecordError from _load_answers in exit 1, as the fragment now says.
  • Legacy inline scores in model-compare: base scored_arms and non_baseline_arms already counted legacy arms[x] payloads through the answers section, and the head still does through arm_answers. Output is identical on the legacy shapes.
  • Repo rules: both changelog fragments keep a valid format, eval.v1.json changes one description only, schema_compat is OK, no generated artifacts, tests are offline.

Comment thread dokimi/dokimi/report/model_compare.py Outdated
Comment thread dokimi/dokimi/eval/arm_runner.py Outdated
Comment thread dokimi/tests/test_model_compare.py Outdated
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

# Conflicts:
#	dokimi/changelog.d/20261016_190000_napetrov_empty-question-rule.md
#	dokimi/dokimi/commands/evaluate.py
…p null arms

An explicit null under scores clears a legacy inline score for that arm, and a
null evaluation in a join no longer keeps the answer's inline score. A new
arm_names reader lists every arm an arms record ran, null ones included, so the
arm judge writes an unscored row for a failed arm and non_baseline_arms still
sees a treatment that produced nothing. Adds the missing judge and
model-compare regression tests.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
trust_gate (components) imported dokimi.eval.records (evaluation), an
upward import. records.py depends only on the standard library, so it
moves to dokimi/records.py in the foundation layer, which both the gate
and the evaluation engine may import. Imports, docs, schema description,
and the changelog fragment now name the new path.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Comment thread dokimi/dokimi/records.py
# output writes `question`, a question record `text`.
for key in ("question_text", "question", "text"):
value = item.get(key)
if isinstance(value, str) and value:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still open from the Copilot review: this accepts a whitespace-only question_text, while require_question_texts() skips it and checks question instead. A record with question_text " " and question "How do I X?" passes the guard, and then the judge, panel, ArmRunner.judge, RAGAS and degrade get " " as the question. Please use the same check as question_text_of(): if isinstance(value, str) and value.strip():, and add a test.

return load(path, section="answers")
except RecordError as exc:
print(f"Error: {exc}", file=sys.stderr)
sys.exit(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A RecordError exits 1 here (and in judge_reliability.py:33 and :270), but cli.py says bad input exits 2 because 1 is the verdict of eval judge-validity and of the --strict gates. eval judge-validity on a file with a record without question_id now exits 1, the same code as a failed validity gate. In eval score a missing id exits 1 and a blank question exits 2. Please exit 2 for a RecordError too, and say so in the fragment.

on (the reproducibility gate only looks for stored-material stamps).
"""
if isinstance(data, Run):
return data.records

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a Run this returns all its records and ignores key, so stored_measure() on a Run loaded with section="answers" finds no stored measure. Either accept only the dict here, or check run.section against key.

The fragment kept from an earlier commit is replaced by a new file named for
this branch, with the new dokimi.records path and the null-score fix from
4502d26.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants