Skip to content

fix(answers,pipeline,judge,gate): Stack P fixes, step 2 of #228 - #274

Merged
antonwolfy merged 76 commits into
intel:mainfrom
napetrov:fix/answers-stack-p-step2
Nov 1, 2026
Merged

antonwolfy merged 76 commits into
intel:mainfrom
napetrov:fix/answers-stack-p-step2

Conversation

@napetrov

@napetrov napetrov commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Step 2 of #228, "Fix Stack P in place". Stack P (EvaluationPipeline + Answerer) gets five fixes and one stamp, one commit each. Each fix flips the rows it owns in the step-1 characterization table (tests/test_answering_stack_parity.py, PINNED), so the diff of this PR shows the change in numbers. No step 3 work is in this PR. eval/arms_run.py, commands/arms.py and commands/subjects.py are not touched (PR #270 owns them).

Which scores move, and why

These are the parity-table values, measured on the six-question fixture. No live benchmark was run for this PR; the size and direction of the live shift are not measured here.

Difference What changes Row (P, before to after)
D1 top_k P asks the doc source for top_k docs (was the default 5). Retrieved context is smaller, so with-docs answers and their judge inputs change. retrieval.max_results 5 to 3; retrieval.passed ("max_tokens",) to ("max_results", "max_tokens")
D2/D3 placeholder With no docs (D2) or a raising reranker (D3), P no longer judges the placeholder with-docs answer "[FALLBACK: No relevant documentation found]". It is recorded, not scored. judge.calls 12 to 10; judge.contexts.q-empty and q-rerank lose the placeholder with-docs context; delta.per_question q-empty and q-rerank 0.0 to None
D9 statistics Follows from D2/D3: P has 4 paired questions, below MIN_PAIRS_FOR_TEST (6). P gets no significance test. stats.delta_mean 13.33 to 20.0 (4 pairs, was 6); significance.n 6 to None; significance.significant False to None
D8 library id With no context7_id, the pipeline resolves the id through the registry before the client. library_id client/resolved to registry/lib
D6 judge temperature benchmark run gains --judge-temperature. The default is still the provider default, so no default score moves. cli.judge_temperature_flag False to True
stamp method_version: 2 in the answers artifact and in the eval artifact of the single Judge. No score moves. artifact.method_version 2 (P), None (A)

Direction of the live shift, from the D2/D3 change: the placeholder answer is scored near zero by a judge, so dropping those rows raises the with-docs average and the delta on any live run with retrieval misses. Old and new runs of the same product are therefore not comparable on those numbers. compare does not read method_version yet, so it will not refuse the mix (out of scope, below).

Default behaviour that changes live runs: benchmark run, orchestrate evaluate and eval score no longer judge a placeholder with-docs answer. Pass --judge-placeholder to restore the old judging.

An eval artifact made by eval score on answers from before this PR is stamped method_version: 1, the version of the answers it judges. The stamp records the judging method; the answers artifact keeps its own stamp (absent, so version 1). Read the two together; a single eval stamp does not say which answers it judged.

Trust gate: numbers, thresholds not changed

Thresholds are unchanged: min_retrieval_hit_rate 0.60, min_retrieval_coverage 0.80, min_questions 10, max_zero_fraction 0.20, and the rest of DEFAULTS.

What changes is the input of the checks:

  1. min_questions counts the both-arm-scored questions. Placeholder questions (no document, not judged by default) drop out of that count. Fewer counted questions makes the minimum harder to reach, so this is a stricter number change, not a weakening. On the 4-of-14 fixture the count goes from 14 to 10, which still passes at min 10.
  2. zero_score_fraction (fail) counts a question whose retrieval missed as unusable, in the numerator and the denominator. On main a miss was judged at 0 and counted as a zero, so this keeps the fail-severity check failing on the same run. The message says "scored 0 or had no document". Commit c517c727. Regression test: tests/test_trust_gate_unjudged_misses.py fails on the parent commit (4 of 14 passes at 0.0) and passes here.
  3. min_with_docs_avg, min_delta and score_cv (warn) run over the both-arm-scored subset only. The placeholder zeros that main counted are gone from that subset, so these three checks are more lenient than on main. Not changed here; see the decision below.
  4. Retrieval hit rate (warn) runs over every eval record. A question with no document is in the denominator as a miss. Before this PR it was in the rate only when judged. Under default flags the placeholder misses were judged before and counted already, so the net change is small.

Thresholds are not changed, and no retrieval check was added with fail severity.

Probe (probes/gate_probe.py in the review scratch; 14 both-arm questions, mock judge scores a FALLBACK answer 0, otherwise 60; re-run on the head after the fix):

Fixture Check Base (intel/main) Head (this PR)
4 of 14 miss (29%) min_questions (fail, min 10) 14, pass 10, pass
4 of 14 miss zero_score_fraction (fail, max 0.20) FAIL, 0.286 FAIL, 0.286
4 of 14 miss retrieval_hit_rate (warn, min 0.60) 0.714, pass 0.714, pass
14 of 14 miss min_questions (fail, min 10) 14, pass; zero FAIL 1.0; retrieval FAIL 0.0 0, FAIL; early return, no zero check; retrieval_hit_rate 0.0, FAIL
all hit all checks unchanged unchanged

Net effect: the fail-severity zero-score check gives the same answer on the same run as main (FAIL at 0.286). A run where 29% of questions get no document still fails the gate. The retrieval miss shows as a warn-severity rate that passes at 71%, as before. A run where every question misses still fails, through min_questions, and has no retrieval line.

Adding a fail-severity retrieval check, or changing its threshold, is a separate design decision. It is not in this PR.

Changes

  • fix(answers): the doc source is asked for top_k docs (was the default). Dead code deleted in the context arm.
  • fix(benchmark): benchmark run --judge-temperature is parsed and passed to the judge. Batch is not changed.
  • fix(pipeline): the library id goes through resolve_library_id_for_product (explicit id, then registry context7_id, then client).
  • fix(judge): the no-retrieval placeholder is not judged by default; --judge-placeholder on benchmark run, orchestrate evaluate and eval score restores it. Trust gate counts all records for retrieval (see above).
  • fix(gate): zero_score_fraction counts retrieval misses as unusable, so the fail-severity check matches main on the 4-of-14 fixture (commit c517c727, regression test tests/test_trust_gate_unjudged_misses.py). Commit 6bec8910 updates the methodology bullet and parses the evaluations once.
  • refactor(answerer): removed the unused self.llm client, the unreachable if not docs: branch, and their imports.
  • feat(artifacts): method_version (defaults.ANSWERING_METHOD_VERSION = 2) on the answers artifact and the eval artifact of the single Judge; documented in answers.v1.json and eval.v1.json. Artifacts without it read as version 1. Nothing reads it yet.
  • test(parity), test(answerer), test(answerer): lint fix (missing List import); the init tests that only read back attributes replaced by a provider allow-list smoke test (flagged by the test-quality audit); a direct test that top_k reaches the doc source.
  • docs: methodology trust-gate list says which checks run on both-arm-scored questions; the method_version comment says nothing reads it yet.
  • Changelog fragment changelog.d/20261028_120000_napetrov_stack-p-step2.md.

The flag registry (dokimi/cli_flags.py) does not exist on main, so the new flags were added to the parsers directly.

Evidence

  • Parity table: each fix commit flips its rows; the table is the red-then-green record for the numbers above. Red runs for the per-fix commits are in the author's scratch directory, not in this PR.
  • The new top_k test: fails on intel/main (the probe sends no max_results), passes on the head.
  • Full suite on the head (6bec8910; the full run was on 3685c29f, and the last commit only changes one comment line, with the targeted gate and docs tests re-run): 9703 passed, 6 skipped.
  • Regression test tests/test_trust_gate_unjudged_misses.py: fails on the parent commit 0fe7452d (value 0.0, expected 0.286), passes on the head.
  • ruff check .: clean. ruff format --check reports 482 files that would be reformatted; the project does not gate on ruff format, so this is pre-existing and not changed here.
  • Self-review (pr-review, local mode): candidates reviewed in review.md of the review scratch.

Merge Danger

Checked by trial merge against intel/main (no rebase needed for this branch: it contains intel/main).

Both #250 and #255 are ours and the coordinator owns those conflicts; this PR does not resolve them.

Out of scope (listed, not changed here)

  • benchmark batch has no --judge-temperature.
  • compare does not read method_version yet, so it does not refuse mixed versions.
  • eval panel-score (JudgePanel) still judges the placeholder answer and writes its artifact without method_version. The panel's trust numbers therefore differ from the single judge's on the same answers.
  • A registered product without context7_id now raises ValueError for clients that require a declared id, in question generation and answer generation (the registry-first lookup). Intended, but a hard stop where the client used to be asked.
  • Trust-gate decisions for the maintainers: (a) the warn-severity score checks (min_with_docs_avg, min_delta, score_cv) run over the both-arm subset, so they are more lenient than on main; (b) a fail-severity retrieval check is not added (retrieval_hit_rate stays warn). Neither is changed here.

Refs #228

🤖 Generated with Claude Code

…tep 2)

The answerer stored top_k and never sent it, so retrieval asked the doc source for its default max_results. It now sends max_results=top_k, the same request DocTreatment makes on Stack A. With the default top_k of 3 the reranker sees 3 raw docs, not 5, so with-docs answers and their judge scores move.

Flips the PINNED rows D1 retrieval.passed and D1 retrieval.max_results. Test fakes that take the doc-source call now accept max_results.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ntel#228 step 2)

benchmark run had no judge temperature flag, so its judge always ran at the provider default while evaluate and arms run took one. The flag is added (default unset, so runs without it are unchanged) and reaches the EvaluationPipeline judge.

Flips the PINNED row D6 cli.judge_temperature_flag. benchmark batch does not get the flag yet.

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

EvaluationPipeline took the library id from --context7-id or the client's
own resolver, so a product whose products.yaml declares context7_id was
still looked up by its registry key. Resolve it with the shared
resolve_library_id_for_product helper, the same order the arms stack uses:
explicit id, then the registry's context7_id, then the client.

Question generation and answer generation both go through the helper.
The registry is injectable (registry=) and defaults to the bundled
products.yaml. Parity row D8 flips from client/resolved to registry/lib.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…el#228)

When no document reached the prompt (no hit, or the reranker raised), the
answerer records "[FALLBACK: No relevant documentation found]" as the
with-docs answer, and the judge scored that text like a model answer. It
inflated the with-docs side of the paired delta on questions the docs did
not answer.

The judge now leaves a context answer whose doc_source is a no-retrieval
source (NO_RETRIEVAL_SOURCES) unjudged, so its with-docs score and delta
are None and the question drops out of the paired statistics. The record
and its retrieval_hit stay. Opt back in with judge_placeholder=True, or
--judge-placeholder on benchmark run, orchestrate evaluate and eval score.

The trust gate's retrieval_hit_rate read only the both-arms-scored
evaluations. Unjudged misses would then vanish from it and an all-miss run
would read as "not measured". It now counts every evaluation's
retrieval_hit. The thresholds are unchanged.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Answerer built a self.llm client in __init__ that nothing read: the answer
call goes through llm_call_with_usage, which picks the provider's client
itself. Only the unsupported-provider ValueError is kept; the client
construction and its imports are gone.

_generate_with_docs had an "if not docs" branch that could not run, because
_generate_answer_pair calls it only when retrieval returned documents. The
branch is deleted with its unreachable "[No documentation retrieved]" record.

No parity row changes: nothing observable on either stack moved.

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

The Stack P answers artifact and the judge's evaluation output now carry
method_version, from defaults.ANSWERING_METHOD_VERSION (2). The steps
before this one are version 1 and have no stamp, so a later compare can
refuse to put runs of different versions side by side. compare does not
read the stamp yet.

answers.v1 and eval.v1 document the field. The arms artifact is not
stamped in this change; the new parity row records that.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
ruff F821 flagged the annotation on _paired_deltas. The module uses
`from __future__ import annotations`, so the missing name did not fail at
import time and the parity tests passed without it.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…set-then-read tests (intel#228)

With the llm construction removed from Answerer.__init__, test_init_openai and
test_init_anthropic only set attributes and read them back, which the test
quality audit flags (set_then_read). The new parametrized test checks what
init does now: each supported provider is accepted. It is marked as a
must-not-raise smoke test.

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

test_calls_mcp_client builds the Answerer with the default top_k, so a literal
max_results=5 would pass it. The new test builds it with top_k=3 and asserts
the doc source is asked for 3. On intel/main the same probe sends no
max_results at all, so the test fails there.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ons (intel#228)

The methodology list and the method_version comment described the trust gate
and the stamp more broadly than the code does. The score checks run over the
both-arm-scored questions, so a question with no document leaves them. Nothing
reads method_version yet; the comment now says so.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…fraction (intel#228)

A question whose retrieval missed is no longer judged with docs, so it has
no context score. The zero-score check counted only scored questions, so a
run where most questions miss passed it: 4 misses of 14 gave 0.0 PASS, where
intel/main gave 0.286 FAIL (the misses were judged at zero there).

The check now counts a miss (no context score, a baseline score, and
retrieval_hit False) as unusable, in the numerator and the denominator. The
message says such rows had no document. Thresholds, min_questions and the
early return are unchanged. The new fail-severity retrieval check is not
added here.

Regression test: tests/test_trust_gate_unjudged_misses.py. It fails on the
parent commit (4 of 14 passes at 0.0) and passes with this change.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ons once (intel#228)

The methodology bullet still said the zero-score fraction leaves out
questions with no document. It counts them now (c517c72). Update it.

Also parse the evaluations once in evaluate_trust. The input may be a
one-shot iterable, and the old code parsed it again for the retrieval check.

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

codecov-commenter commented Oct 29, 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 98.65772% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.10%. Comparing base (b4a0a8c) to head (989d087).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
dokimi/dokimi/report/generator.py 90.47% 1 Missing and 1 partial ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #274      +/-   ##
==========================================
+ Coverage   94.05%   94.10%   +0.05%     
==========================================
  Files         297      297              
  Lines       38018    38106      +88     
  Branches     6424     6438      +14     
==========================================
+ Hits        35756    35858     +102     
+ Misses       1452     1438      -14     
  Partials      810      810              
Files with missing lines Coverage Δ
dokimi/dokimi/commands/answers.py 95.37% <100.00%> (+0.13%) ⬆️
dokimi/dokimi/commands/benchmark.py 91.18% <100.00%> (+1.39%) ⬆️
dokimi/dokimi/commands/evaluate.py 90.11% <100.00%> (+0.18%) ⬆️
dokimi/dokimi/commands/orchestrate.py 86.25% <100.00%> (+0.17%) ⬆️
dokimi/dokimi/dashboard/aggregator.py 94.15% <100.00%> (+0.53%) ⬆️
dokimi/dokimi/defaults.py 100.00% <100.00%> (ø)
dokimi/dokimi/eval/answerer.py 95.02% <100.00%> (-0.31%) ⬇️
dokimi/dokimi/eval/diagnoser.py 100.00% <100.00%> (ø)
dokimi/dokimi/eval/judge.py 94.33% <100.00%> (+0.11%) ⬆️
dokimi/dokimi/gate/trust_gate.py 100.00% <100.00%> (ø)
... and 3 more

... and 3 files with indirect coverage changes

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

@github-actions github-actions Bot added the cli-interface-change Changes dokimi's user-facing CLI surface label Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

CLI interface change detected

This PR changes dokimi's user-facing CLI surface (commands and/or options):

~ `dokimi answers generate` parameters changed:
    ~ changed option `--top-k`: type: 'int' -> 'dokimi.cli.build_parser.<locals>.positive_int#faf57db5ff9f'
~ `dokimi benchmark run` parameters changed:
    + added option `--judge-placeholder`
    + added option `--judge-temperature`
~ `dokimi eval score` parameters changed:
    + added option `--judge-placeholder`
~ `dokimi evaluate` parameters changed:
    + added option `--judge-placeholder`

Updated on every push; the cli-interface-change label is on while the diff is non-empty.

@vlad-perevezentsev vlad-perevezentsev 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 6bec891. The five Stack P fixes do what step 2 of #228 asks, and the zero-score check keeps failing the same runs as main. Not judging the placeholder has side effects outside the gate's score checks (the retrieval coverage, the diagnosis and the pipeline summary), and the registry lookup misses a custom --registry. These are in the line comments.

What I ran, at 6bec891:

  • make ci-local --offline: 0 failed, 17 passed, 13 skipped (network and Docker jobs).
  • Trust gate with 15 judged rows and 5 judge-error rows, on the base and on the head: the retrieval check goes from 100% (15/15) to "not measured".
  • summarise_diagnoses on 10 hits and 4 misses judged through _evaluate_answer_pair, on the base and on the head: empty_retrieval 4 becomes 0 and insufficient_data 0 becomes 4.
  • resolve_library_id_for_product("my_lib", <Context7 client>) returns my_lib unchanged.

Checked, no issues

  • top_k: Answerer sends max_results=top_k, as DocTreatment does. evaluate and answers generate pass their --top-k, and the new test pins the value at the call site.
  • Zero-score check: unjudged misses count in both the numerator and the denominator, so 4 of 14 misses still fail at 0.286, as on main. The new test fails on the parent commit.
  • Judge temperature: benchmark run --judge-temperature reaches the Judge in single and multi-run mode.
  • Dead code: Answerer.llm was never read, and _generate_with_docs is only called with docs. The provider allow-list keeps the same set.
  • Schemas: method_version is an optional new property in answers.v1 and eval.v1. That widens the schemas, and old files still validate.

Comment thread dokimi/dokimi/gate/trust_gate.py Resolved
Comment thread dokimi/dokimi/eval/judge.py Resolved
Comment thread dokimi/dokimi/orchestrator/pipeline.py Resolved
Comment thread dokimi/dokimi/orchestrator/pipeline.py Resolved
Comment thread dokimi/dokimi/eval/judge.py Resolved
Comment thread dokimi/tests/test_trust_gate.py Resolved

@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 6bec891. The five Stack P fixes do what step 2 of #228 asks, and the zero-score check keeps failing the same runs as the base. Three things are worth fixing before merge, all test gaps on the new code (inline). The open problems from @vlad-perevezentsev's review still hold at this head and block the merge.

I agree with @vlad-perevezentsev on the judge-error rows losing the retrieval rate, the insufficient_data diagnosis of misses, the registry lookup in benchmark run --registry, the summary averages over different questions, the unstamped --judge-placeholder runs, the 0-hit gate test and the changelog flag names. New evidence: 10 hits and 4 misses move min_delta from -17.1 (fail) to +0.0 (pass).

What I ran, on a Linux review host at 6bec891:

  • make ci-local: 27 passed, 1 failed, 7 skipped. The failure is pip-audit, which lists advisories on filelock in the lock files. This PR does not touch them.
  • 9,705 tests passed, 4 skipped; the changed-line coverage step passes.
  • Reproductions for the code findings, with the LLM transport mocked.
  • A mutation run on the new code (33 mutants, 13 of which pass the related tests): _library_id, the unjudged_misses filter and the flag wiring in benchmark run, evaluate and eval score each have mutants that pass every test.

Checked, no issues

  • Schemas: method_version is an optional property on an open object in answers.v1.json and eval.v1.json. schema_compat against the base passes, and no version bump is needed.
  • top_k: every doc-source client's get_library_docs accepts max_results, so the new argument raises nowhere.
  • Dead code: no caller of Answerer.llm or of _generate_with_docs with empty docs remains.
  • Zero-score check: 10 hits and 4 misses give 4/14 = 0.286 and fail, as on the base. Judge-error rows are excluded from the new term.
  • Commits: all 12 carry Signed-off-by; the changelog fragment is present.

Comment thread dokimi/dokimi/gate/trust_gate.py Resolved
Comment thread dokimi/dokimi/orchestrator/pipeline.py Resolved
Comment thread dokimi/dokimi/commands/benchmark.py Resolved
@vchamarthi

vchamarthi commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

🔍 OpenCodeReview found 4 issue(s) in this PR.

By severity: 4 low

Severity Category Location Finding
low test test_pipeline_summary.py:87 Nit: no test covers the all-paired case.
low test test_pipeline_library_id.py:56 Nit: _RecordingClient.asked exists to record the fallback, but no test reaches it.
low documentation 20261028_120000_napetrov_stack-p-step2.md:7 benchmark run had no --judge-temperature flag before this change (the diff adds it to the parser).
low test test_diagnoser.py:56 Nit: this covers only the fallback_none happy path.
Review details
  • Commit reviewed: 284b240 against merge base f0f95a2
  • Coverage: 25 files in 6 groups
  • Engine: OpenCodeReview v1.13.3, us.anthropic.claude-sonnet-5-5
  • Usage: 1.04M tokens (785k from cache), 51s
  • Tool calls: 53 (33 code_search, 14 file_read, 3 code_comment, 3 file_read_diff)
  • Changelog and methodology docs: 2 files
  • Benchmark command, defaults, and registry wiring: 3 files
  • Evaluate/orchestrate commands and pipeline library_id: 6 files
  • Answerer refactor, answering stack parity, and schemas: 6 files
  • Judge and diagnoser changes with placeholder tests: 5 files
  • Trust gate and unjudged misses tests: 3 files

…rs-stack-p-step2

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

# Conflicts:
#	dokimi/dokimi/orchestrator/pipeline.py
The retrieval coverage floor counted every judge-error row as an unrecorded
outcome, so a run with more than 20% judge errors lost the retrieval check.

Thread: PRRT_kwDOVBg7g86xgwoY
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
With the no-retrieval placeholder left unjudged its delta is None, and
diagnose() returned insufficient_data before it looked at the doc source.

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

xaleryb commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Merged current main (63c9cdb, after #273 and #301) into this branch as 171a022. The only conflict was the import block of orchestrator/pipeline.py: kept both this PR's resolve_library_id_for_product and #273's same_evaluator. Full suite, ruff, mypy, bandit and the strict benchmark run pass.

Comment on lines +34 to +35
of one version that differ in it are told apart. A `method_version` that is not a positive
integer is refused before any judge call (`eval score` exits 2).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

documentation · low
Nit: method_version: null is accepted as legacy version 1, not refused. answers_method_version() explicitly handles None, and test_a_null_stamp_is_legacy confirms that eval score proceeds. Qualify this claim so it matches the accepted input: an absent or null stamp means version 1; any other value must be a positive integer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0f8835a: an absent or null stamp is version 1; any other value must be a positive integer, or eval score exits 2.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in fork commit 0f8835aa (PR head 7e57031d). The changelog now says: "An absent or null stamp means version 1; any other value must be a positive integer, or eval score exits 2 before any judge call."

Checked against the code at 7e57031d: answers_method_version() returns 1 for an absent or None stamp, and refuses any other non-positive-integer value with a ValueError, which eval score turns into exit 2 before the Judge is built. test_a_null_stamp_is_legacy covers the null case.

Comment thread dokimi/docs/methodology.md Outdated
- minimum `with_docs` average,
- average delta,
- score coefficient of variation,
- minimum evaluated question count (both arms scored; a question with no document is not judged by default, so it is not counted here),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

documentation · low
Nit: qualify the no-document exclusion as the single Judge default. For example, eval panel-score on an answer with doc_source="fallback_none" and a nonempty [FALLBACK: ...] text still scores both arms: JudgePanel._score_answer_entry() only skips missing or empty answers. That row therefore counts toward the trust gate's minimum question count. This page also covers panels, so the current unqualified statement incorrectly describes their behavior.

Suggestion:

Suggested change
- minimum evaluated question count (both arms scored; a question with no document is not judged by default, so it is not counted here),
- minimum evaluated question count (both arms scored; the single judge leaves no-document context placeholders unscored by default, so those questions are not counted here; panel scoring still judges them),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 17d5b40: the single Judge leaves out a question with no document; JudgePanel still judges an answer that is only fallback text, and the line says so.

Comment thread dokimi/docs/methodology.md Outdated
- average delta,
- score coefficient of variation,
- minimum evaluated question count (both arms scored; a question with no document is not judged by default, so it is not counted here),
- zero-score fraction (computed when at least one question is scored in both arms; over those paired questions plus retrieval misses with a scored baseline or a judge error, which count as unusable),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

documentation · low
Nit: only retrieval misses with an unscored context arm are added as unusable. With --judge-placeholder, a miss scored 70 in the context arm and 60 in the baseline contributes no zero, whereas this wording says retrieval misses with a scored baseline count as unusable. evaluate_trust explicitly requires r.score(CONTEXT) is None for the extra rows. Qualify the added misses as unscored to describe the denominator and numerator accurately.

Suggestion:

Suggested change
- zero-score fraction (computed when at least one question is scored in both arms; over those paired questions plus retrieval misses with a scored baseline or a judge error, which count as unusable),
- zero-score fraction (computed when at least one question is scored in both arms; over those paired questions plus retrieval misses with an unscored context arm and either a scored baseline or a judge error, which count as unusable),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 17d5b40: the zero-score rows are only retrieval misses whose context arm is unscored (with a scored baseline or a judge error).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in fork commit 17d5b405 (PR head 7e57031d). The zero-score bullet now counts "retrieval misses with an unscored context arm and either a scored baseline or a judge error, which count as unusable", and nothing else is added.

Checked against evaluate_trust at 7e57031d: unjudged_misses takes rows with a None context score, retrieval_hit is False, and either a baseline score or an error. A miss whose context arm is scored is not added.

@napetrov

Copy link
Copy Markdown
Contributor Author

Mapping of the items in your 10-31 06:49Z comment to commits on this head (synced with main b4a0a8c in 6b5ca65):

  • Dashboard: a no-error retrieval miss with a baseline score counts as context 0.0, so 0 hits no longer shows the baseline as the doc score (52a6064).
  • report eval with no complete pair: no delta printed, and it says there are no complete pairs (e6eb131, c6d6c1f).
  • PR body: method_version: 1 for re-judged legacy answers, and the 14-of-14 row shows retrieval_hit_rate 0.0 FAIL (body edit; bf0f032 is an empty marker commit).
  • exai nits: changelog stamp wording (0f8835a), methodology lines 202 and 203 (17d5b40).

Local: report and dashboard tests 1118 passed; ci-local static and typecheck clean.

…rs-stack-p-step2

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A question the context arm never answered, with no retrieval hit and a baseline score,
counted as no score, so the snapshot averaged the baseline alone (0 hits gave doc_score
equal to the baseline). It now counts as 0.0 for the context arm.

Thread: IC_kwDOVBg7g88AAAABhdSHJQ.a
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
With no question scored in both arms, report eval printed a 0.0 arm mean, an
Overall delta of +0.0 and a delta over 0 pairs. The Summary cells and the Overall delta
line now show an em dash and a note that there are no complete pairs.

Thread: IC_kwDOVBg7g88AAAABhdSHJQ.b
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The changelog said a non-positive-integer stamp is refused; a null or absent one is the
legacy version 1 and is accepted.

Thread: PRRT_kwDOVBg7g86yPRma
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The minimum question count excludes an unjudged question only for the single Judge; a
panel judges a fallback-only answer. The zero-score denominator adds retrieval misses with
an unscored context arm and either a scored baseline or a judge error, and no other miss.

Thread: PRRT_kwDOVBg7g86yPRmb
Thread: PRRT_kwDOVBg7g86yPRmc
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
No code change. The body edit (line 22: method_version 1 for an eval of legacy answers;
the 14-of-14 row: retrieval_hit_rate 0.0 FAIL) is applied to the PR description.

Thread: IC_kwDOVBg7g88AAAABhdSHJQ.c
Thread: IC_kwDOVBg7g88AAAABhdSHJQ.d
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
With no complete pair, the note under the Summary still said the means were over 0
complete pairs. It now says there are no complete pairs and that no arm mean or delta is
given.

Thread: IC_kwDOVBg7g88AAAABhdSHJQ.b
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Comment thread dokimi/dokimi/dashboard/aggregator.py Outdated
without_score = _as_float(ev.score(BASELINE))
if (with_score is None and ev.retrieval_hit is False and without_score is not None
and ev.error is None and (with_s is None or with_s.error is None)):
with_score = 0.0 # a retrieval miss the context arm never answered: the docs did not help

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
Must-fix: this also turns an unjudged miss into a complete pair in _arm_means, changing the reported A/B delta, not just the documentation score/status. With the new test's inputs (one actual pair at 80/60 and one unjudged miss with baseline 60), the dashboard now reports delta -20, while both eval reports report +20 over the sole judged pair; the miss's own QuestionResult.delta remains None. An all-miss run similarly acquires a -60 delta despite having no scored pairs. Keep the zero penalty separate for the documentation health score, and compute the displayed arm means/delta from actual scored pairs so an unjudged placeholder does not become a measured degradation.

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.

Agreed, and it also makes the dashboard and report eval disagree on the same eval file, which #286 had just aligned.

Repro at 7e57031, one eval file with one pair (80/60) and one unjudged miss (baseline 60): ResultsAggregator gives context 40.0, baseline 60.0, delta -20.0; report eval on the same file gives | **All** | 2 (1 unpaired) | 80.0 | 60.0 | **+20.0** | and "context arm is helping". The changelog says an unjudged miss "drops out of the paired statistics" and does not mention the dashboard, so a reader of either output gets the wrong idea about the other.

Suggestion: keep the 0.0 only for doc_score/status (the doc health number, as @xaleryb asked), compute avg_with_docs/avg_without_docs/avg_delta from the real pairs as before, and add a changelog line for the dashboard.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 60f5b09. The arm means and delta now leave an unjudged miss out, since it is no judged measurement. The dashboard matches report eval on the same file (+20 over the one complete pair). The doc score and status still count the miss as 0.
Tests: the miss case in test_dashboard.py (red on the old code), and an all-miss run now has no delta. The changelog says so too.

if not s["unpaired"]:
return []
if not s["valid"]:
return [f'_No complete pairs: {s["unpaired"]} unpaired questions, so no arm mean or delta is given._', ""]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bug · medium
Must-fix: the no-pairs handling only updates the Summary and overall recommendation. With the new test's two unpaired questions, the same report still prints context/baseline 0.0 and delta +0.0 in the Dynamic vs Static breakdown (including Total), and claims “+0.0 vs +0.0 — good consistency, benchmark is stable.” Difficulty rows do the same when a difficulty is present. These pre-existing zero fallbacks now contradict this new statement and the Summary's em dashes. Apply the no-pairs rendering to both breakdown tables and guard the agreement recommendation when either set lacks pairs; extend the regression test to cover these outputs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in ad1f45e. The Dynamic vs Static and difficulty rows print em dashes for a set with no complete pair, as the Summary does. The agreement bullet says there is no delta, instead of "good consistency".
Regression test: test_breakdown_tables_and_agreement_say_no_pair_instead_of_a_zero_delta (red on the old code).

…atchet

The miss-as-zero rule and the no-pairs lines pushed _load_snapshot over the
cap of 10 and generate_report past its baseline of 18. Move them into
_context_score, _overall_line and _delta_summary; no behavior change.

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

@vlad-perevezentsev vlad-perevezentsev 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 7e57031. Everything from my first review is fixed and pinned by tests. The new dashboard change scores an unjudged miss as 0, which also moves the dashboard's arm means and delta. So the dashboard and report eval now disagree on the same eval file. I added a repro in @exai-bot's thread.

Agreed with @exai-bot: the dashboard's arm means and delta now count unjudged misses; with no complete pairs, the report's per-set tables and the agreement line still print +0.0.

What I ran, at 7e57031:

  • make ci-local --offline: 0 failed, 19 passed, 13 skipped (network and Docker jobs). The suite had 12,042 passed and 58 skipped. The branch is 0 commits behind main.
  • One eval file with one pair (80/60) and one unjudged miss (baseline 60). The dashboard shows context 40.0, baseline 60.0, delta -20.0. report eval shows 80.0, 60.0, +20.0.
  • Earlier rounds: mutation runs on the gate filter, the top_k override and the diagnoser branch. Each mutant is now killed.

Checked, no issues

  • Trust gate: in zero_score_fraction, misses with an unscored context arm count as unusable, and a judge-error miss counts too. Judge-error rows keep retrieval_hit. An old error row without that field stays out of the coverage count, after a6a74c4. With no pair, the gate still shows the retrieval rate.
  • Diagnosis: an unjudged miss is empty_retrieval. A miss judged with --judge-placeholder that has no baseline is insufficient_data.
  • Stamps: eval score and the pipeline stamp the eval with the answers' method_version: an absent stamp counts as 1, and a malformed one exits 2 before any judge call (21b3054, f34aa62). judge_placeholder is stamped too.
  • Retrieval: top_k reaches the doc source, and a value below 1 is refused (3d7f8c5). The library id comes from --context7-id, then the mcp: ;id=, then the loaded --registry. The topics cache is keyed on the doc source (b945e44).
  • Summaries: the run summary and report eval average the complete pairs and say how many questions were left out. With no pair they print no delta.
  • Changelog and docs: the fragment names only flags and commands that exist, and says old scores are not comparable. methodology.md gives the subset each gate check uses.

A no-error retrieval miss scores 0 as the context arm, so the doc score
counts it, but it is no judged measurement: pairing it with its baseline
made the dashboard report a -20 delta where the eval report gave +20 over
the same file. The arm means and delta are now over the measured pairs;
the doc health (doc_score, status) still counts the miss as 0.

Thread: PRRT_kwDOVBg7g86yUBEO
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
A set with no complete pair printed 0.0 for its context, baseline and delta
in the Dynamic vs Static and difficulty tables, and the agreement bullet
claimed consistency. Those rows now print an em dash like the Summary, and
the agreement bullet says there is no delta when either set has no pair.

Thread: PRRT_kwDOVBg7g86yUBES
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Copilot AI balanced review requested due to automatic review settings November 1, 2026 05:11

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

Skipping placeholders exposes a critical all-miss reporting bug that presents zero-valued statistics and concludes minimal impact despite having no paired measurements.

1 open issue
What changed in this PR

1 new must-fix finding.

This PR implements step 2 of #228, correcting Stack P’s answering and judging behavior before consolidating the two evaluation stacks.

Changes:

  • Honor retrieval limits, resolve library IDs through the registry, and wire judge controls through CLI entry points.
  • Leave placeholders unjudged by default, preserve retrieval misses in trust checks, and stamp artifacts with method metadata.
  • Update reports, dashboard calculations, documentation, and regression tests for unpaired results.
File Description
dokimi/​tests/​test_trust_gate.py Covers retrieval rates and revised gate messages.
dokimi/​tests/​test_trust_gate_unjudged_misses.py Tests missed retrievals and judge-error handling.
dokimi/​tests/​test_report_arm_agreement.py Tests report sections without complete pairs.
dokimi/​tests/​test_provider_call_budgets.py Updates the retrieval stub signature.
dokimi/​tests/​test_pipeline_summary.py Checks paired and excluded question counts.
dokimi/​tests/​test_pipeline_library_id.py Tests library resolution and topic-cache isolation.
dokimi/​tests/​test_llm_cli.py Checks early refusal of unsupported judge temperature.
dokimi/​tests/​test_judge_placeholder_entry_points.py Tests placeholder flags through command handlers.
dokimi/​tests/​test_eval_score_method_version.py Tests artifact version propagation and validation.
dokimi/​tests/​test_eval_answerer.py Tests provider acceptance and retrieval limits.
dokimi/​tests/​test_diagnoser.py Tests diagnoses for unjudged placeholders.
dokimi/​tests/​test_dashboard.py Checks paired averages and retrieval-miss health scores.
dokimi/​tests/​test_benchmark_registry_wiring.py Verifies custom registries reach benchmark pipelines.
dokimi/​tests/​test_answers_command.py Tests invalid retrieval-limit rejection.
dokimi/​tests/​test_answering_stack_parity.py Updates pinned Stack P behavior and scores.
dokimi/​tests/​test_answering_stack_p_step2.py Tests judge controls and artifact stamps.
dokimi/​tests/​data/​cli_flags_allowlist.txt Allows the placeholder-judging flag.
dokimi/​tests/​answering_stack_parity.py Passes registry and placeholder options to fixtures.
dokimi/​dokimi/​schemas/​eval.v1.json Declares method and placeholder-judging metadata.
dokimi/​dokimi/​schemas/​answers.v1.json Declares answering-method metadata.
dokimi/​dokimi/​report/​eval_report.py Renders unavailable results when no pairs exist.
dokimi/​dokimi/​orchestrator/​pipeline.py Integrates registry resolution, judge metadata, and pair counts.
dokimi/​dokimi/​gate/​trust_gate.py Includes retrieval misses in relevant trust checks.
dokimi/​dokimi/​eval/​judge.py Skips placeholders by default and preserves retrieval metadata.
dokimi/​dokimi/​eval/​diagnoser.py Classifies unjudged misses as empty retrievals.
dokimi/​dokimi/​eval/​answerer.py Honors retrieval limits, stamps outputs, and removes dead code.
dokimi/​dokimi/​defaults.py Defines method versions and stamp validation.
dokimi/​dokimi/​dashboard/​aggregator.py Separates documentation health from paired measurements.
dokimi/​dokimi/​commands/​orchestrate.py Exposes and forwards placeholder judging.
dokimi/​dokimi/​commands/​evaluate.py Validates answer versions and forwards judge options.
dokimi/​dokimi/​commands/​benchmark.py Wires judge controls, sampling checks, and registries.
dokimi/​dokimi/​commands/​answers.py Validates positive retrieval limits.
dokimi/​docs/​reference/​cli-interface.json Refreshes the CLI interface snapshot.
dokimi/​docs/​methodology.md Clarifies trust-check populations.
dokimi/​changelog.d/​20261028_120000_napetrov_stack-p-step2.md Documents behavior changes and compatibility limits.

Review effort: Balanced

Comment on lines +400 to +401
placeholder = context_answer is not None and context_answer.doc_source in NO_RETRIEVAL_SOURCES
if context_answer is not None and context_answer.text and (self.judge_placeholder or not placeholder):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6de55b4. An all-miss run has no complete pair, so the stats carry None instead of zero: the overall, cluster and source rows show an em dash, the advice is suppressed, and the unpaired note says no pairs were made. JSON carries null for those values.
Test: test_an_all_miss_run_prints_no_zero_mean_delta_or_conclusion (tests/test_report_arm_agreement.py).

A run with no complete baseline/skill pair printed zero means, a zero
delta and a conclusion. Stats now carry None without a pair; the
overall, cluster and source rows show an em dash, the advice is
suppressed, and the unpaired note explains that no pairs were made.
Also lower the _format_markdown growth baseline to its new complexity.

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

@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 6de55b4. Two things are worth fixing before merge, and both are test gaps in the newest commits: the no-pair guards that keep a fabricated zero out of the report's per-topic and per-source tables, and the miss rule behind the dashboard's doc score and status. Two nits inline. The code behaves correctly in every case I ran; the problem is that the tests would not notice if it stopped.

What I ran, on a Linux review host at 6de55b4:

  • make ci-local: 30 passed, 0 failed, 7 skipped (Docker, network and org-level jobs). 12,043 tests passed, 59 skipped; the changed-line coverage step and schema_compat against the PR base pass.
  • Reproductions with the LLM transport mocked: one pair (80/60) plus one unjudged miss (baseline 60) gives 80.0 / 60.0 / +20.0 in the dashboard, report eval and the report generate JSON. An all-miss run gives no delta in each. The gate probe rows from the description reproduce (4 of 14 misses: min_questions 10 pass, zero_score_fraction 0.286 FAIL, retrieval_hit_rate 0.714 pass; 14 of 14: min_questions FAIL, rate 0.0).
  • A mutation run on the new code of the dashboard, both reports, the gate, the stamps and the library id: the survivors that decide a reported number are the inline comments below.

Checked, no issues

  • method_version: True, strings, 0, negatives and floats are refused, null or absent is version 1, and eval score exits 2 before any judge call. The schemas only add optional properties.
  • Library id and cache: --context7-id, then the mcp: ;id=, then the registry, then the client; the topics cache is keyed on the doc source, so old entries are re-extracted once and not reused wrongly.
  • Docs: the methodology and changelog claims about the gate, the both-arm subset and the stamps match the code. No generated artifacts, comment blocks within the limit, growth_baseline.json only lowered, no test writes outside tmp_path.
  • Commits: every non-merge commit has Signed-off-by.
Bot points I checked that no longer apply at this head (5)
  • exai-bot's point on eval score stamping legacy answers as version 2 does not apply: eval score and the pipeline now stamp the answers' own method_version (absent means 1), and tests cover both paths.
  • exai-bot's point on an mcp: descriptor's ;id= losing to the registry does not apply to EvaluationPipeline: the descriptor's id is passed before the registry id, and a test pins it.
  • exai-bot's point on the dashboard delta counting an unjudged miss does not apply: the arm means and delta now leave the miss out, and the dashboard gives +20 on the same file, as report eval does.
  • exai-bot's point on the zero deltas in the report eval breakdown tables does not apply: those rows print an em dash and the agreement line says there is no delta.
  • Copilot's point on the pipeline report printing zero means for an all-miss run does not apply: the overall, cluster and source rows print an em dash, the advice is suppressed and the JSON carries null.

Comment thread dokimi/dokimi/report/generator.py Resolved
Comment thread dokimi/dokimi/dashboard/aggregator.py Resolved
Comment thread dokimi/dokimi/eval/judge.py Resolved
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Comment thread dokimi/docs/methodology.md Outdated
- minimum `with_docs` average,
- average delta,
- score coefficient of variation,
- minimum evaluated question count (both arms scored; the single `Judge` does not judge a question with no document, so it is not counted here, while `JudgePanel` judges one whose answer is only a fallback text),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

documentation · low
Nit: this exclusion applies only by default. For example, eval score --judge-placeholder scores a nonempty fallback_none answer, and the trust gate includes that row in its minimum count when the baseline is also scored. Mention the flag exception so users do not interpret an opted-in run's paired count as excluding all retrieval misses.

Suggestion:

Suggested change
- minimum evaluated question count (both arms scored; the single `Judge` does not judge a question with no document, so it is not counted here, while `JudgePanel` judges one whose answer is only a fallback text),
- minimum evaluated question count (both arms scored; the single `Judge` leaves no-document fallback answers unscored by default, but includes them when `--judge-placeholder` is enabled and both arms receive scores; `JudgePanel` also judges nonempty fallback answers),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 52b3fa1: the trust-gate line now says the single Judge skips the no-retrieval placeholder by default, unless --judge-placeholder is set, and JudgePanel judges nonempty fallback answers. I took your suggested wording.

@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.

I pushed a commit per nit, up to 85b0ee4, which fix the two nits from my review:

  • 5ea5432: dokimi/tests/test_diagnoser.py:77: added a test that judges one fallback_none answer through _evaluate_answer_pair with judge_placeholder on and no baseline, and asserts insufficient_data; it fails with context_scored=False.
  • 85b0ee4: dokimi/changelog.d/20261028_120000_napetrov_stack-p-step2.md:20: added that report eval and report generate show — (JSON null) for the means and delta when no question is scored in both arms, and that the run summary prints no means then.

make ci-local at 85b0ee4: 29 passed, 1 failed (timing flake, passes alone), 7 skipped.

Not approving yet. Still open: the two risks from my review.

- A group with no complete pair has no means in the grouped tables (topic and source), in JSON or markdown.
- A retrieval miss counts as a context zero only with a clean baseline score: no baseline score, a top-level error and a with_docs error each leave the doc score alone.
- methodology: the single Judge skips the no-retrieval placeholder unless --judge-placeholder is set; JudgePanel judges nonempty fallback answers.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
placeholder again (this restores only that part of the old method). The dashboard's arm
means and delta leave such a question out too, as `report eval` does; its doc score still
counts it as 0. `report eval` and `report generate` show `—` for the arm means and delta when
no question is scored in both arms (the `report generate --format json` `stats` block carries

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

documentation · low
Must-fix: qualify the dashboard's miss-as-zero claim. _context_score only substitutes 0 when the baseline has a score and neither the evaluation nor context arm has an error. For example, a complete 80/60 pair plus an unjudged retrieval miss with a judge error produces doc_score == 80, not 40 (also pinned in test_a_miss_without_a_clean_baseline_score_leaves_the_doc_score_alone). State these conditions so users do not interpret excluded judge-error misses as zero-score contributions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 989d087: the entry now says the doc score counts a retrieval miss as 0 only when the baseline has a score and neither arm has an error; a miss with an error leaves the doc score alone (test_a_miss_without_a_clean_baseline_score_leaves_the_doc_score_alone). Docs only; full suite 12099 passed, 8 skipped.

Comment on lines +33 to +34
- **Stamped artifacts** — the answers and eval artifacts carry `method_version` (2). Artifacts
from before this change have no stamp and represent the legacy method (version 1); readers do

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

documentation · medium
Must-fix: limit the eval stamping claim to single-Judge artifacts. eval panel-score writes through panel._save_incremental, which includes neither method_version nor judge_placeholder, even when judging newly generated version-2 answers. Consequently, an unstamped eval does not necessarily represent the legacy answering method. Explicitly document the panel exception here and qualify the subsequent claim that eval artifacts carry judge_placeholder.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 989d087: the entry now limits the stamp claim to eval score artifacts (Judge). eval panel-score writes neither method_version nor judge_placeholder, so its artifact reads as version 1; the entry says so.

The doc score counts a miss as 0 only with a baseline score and no error
on either arm, and panel-score artifacts carry neither method_version nor
judge_placeholder. Say so where the entry claims otherwise.

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

@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.

LGTM. Both test gaps from my last review are closed, and the delta since 85b0ee4 is tests and wording only. No new findings.

What I ran, on a Linux review host at 989d087:

  • make ci-local: 30 passed, 0 failed, 7 skipped (Docker, network and org-level jobs). The test run, the changed-line coverage step and schema_compat against the PR base pass.
  • Mutation check on the two new tests: removing any one of the three None if not s.paired guards in _arm_means fails the grouped-table test. Dropping any one of without_score is not None, ev.error is None or with_s.error is None in _context_score fails a case of the dashboard test.

Checked, no issues

  • Changelog: the dashboard miss-as-zero conditions match _context_score. eval panel-score writes neither method_version nor judge_placeholder (panel._save_incremental), as the entry now says.
  • Methodology: the minimum-count line matches Judge (placeholder unjudged unless --judge-placeholder) and JudgePanel (skips only empty answers).
  • Commits: every non-merge commit has Signed-off-by.
Bot points I checked that no longer apply at this head (6)
  • exai-bot's point on eval score stamping legacy answers as version 2 does not apply: eval score and the pipeline stamp the answers' own method_version (absent means 1), and tests cover both paths.
  • exai-bot's point on an mcp: descriptor's ;id= losing to the registry does not apply to EvaluationPipeline: the descriptor's id is passed before the registry id, and a test pins it.
  • exai-bot's point on the dashboard delta counting an unjudged miss does not apply: the arm means and delta leave the miss out, as report eval does.
  • exai-bot's point on the zero deltas in the report eval breakdown tables does not apply: those rows print an em dash and the agreement line says there is no delta.
  • Copilot's point on the pipeline report printing zero means for an all-miss run does not apply: the overall, cluster and source rows print an em dash, the advice is suppressed and the JSON carries null.
  • exai-bot's point on the stamp claim covering eval panel-score does not apply: the changelog now limits the stamp and judge_placeholder claim to eval score artifacts and says the panel writes neither.

@antonwolfy
antonwolfy merged commit d3e2a26 into intel:main Nov 1, 2026
17 checks passed
antonwolfy added a commit to antonwolfy/dokimi that referenced this pull request Nov 2, 2026
Brings in main up to 41b3d8d (intel#335, intel#281, intel#343, intel#344, intel#345, intel#274 and
dependency updates).

Conflict:
- dokimi/dokimi/artifacts.py: the module docstring's list of artifact
  kinds. Main added effective_instructions (intel#335) before need_graph; this
  branch added repo_hits_judged after task_inspect. Kept both, in the
  docstring's order. SCHEMA_VERSIONS auto-merged with both sides' kinds.

Auto-merged and checked: runs/kinds.py, cli_routes.py,
tests/data/cli_flags_allowlist.txt, tests/test_cli_routes.py,
tests/test_runs_scan.py, tests/test_runs_show.py and the contract keep
both sides' entries. docs/reference/cli-interface.json was regenerated
with scripts/cli_surface.py dump and matches the auto-merged file.

Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli-interface-change Changes dokimi's user-facing CLI surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants