Skip to content

feat(subjects): subjects evidence renders a scorecard as skill_evidence.v1 - #344

Merged
napetrov merged 10 commits into
intel:mainfrom
napetrov:feat/evidence-format
Nov 2, 2026
Merged

napetrov merged 10 commits into
intel:mainfrom
napetrov:feat/evidence-format

Conversation

@napetrov

@napetrov napetrov commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Summary

New dokimi subjects evidence SCORECARD [--out FILE] (free, no model calls) renders a saved subject_scorecard as skill_evidence.v1 (experimental schema): one row per (cell, non-baseline arm) with paired n, delta, CI, corrected p and a verdict (improved/regressed only when significant after correction, else inconclusive), keyed by subject.distribution_digest. Nothing is measured; values the scorecard lacks stay null (e.g. effort). Rollup and scorecard cells now carry baseline_arm.

Stacked on #343 — review only the top commit until that merges.

CLI v2 (#94): maps to report RUN --per-subject --format evidence.

Evidence

  • tests/test_subjects_evidence.py, runs show/scan kind rows, CLI snapshot regenerated (py3.13); full suite 12018 passed locally.

Merge danger

Low–medium: new artifact kind + baseline_arm added to matrix_rollup.v1 cells.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings November 1, 2026 07:19
@napetrov
napetrov requested a review from a team as a code owner November 1, 2026 07:19
@github-actions github-actions Bot added the cli-interface-change Changes dokimi's user-facing CLI surface label Nov 1, 2026
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

CLI interface change detected

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

+ added command `dokimi subjects evidence`
    + argument `scorecard`
    + option `--help/-h`
    + option `--out`
~ `dokimi runs list` parameters changed:
    ~ changed option `--kind`: choices: ["'answers'", "'arms'", "'cli_routes'", "'eval'", "'eval_coverage'", "'explore_runs'", "'guidance_reach'", "'matrix_rollup'", "'need_graph'", "'novelty'", "'plan'", "'plugin_delta'", "'questions'", "'repo_budget'", "'repo_environment'", "'repo_readiness'", "'result'", "'subject_scorecard'", "'task_inspect'", "'task_runs'", "'tool_preference'"] -> ["'answers'", "'arms'", "'cli_routes'", "'eval'", "'eval_coverage'", "'explore_runs'", "'guidance_reach'", "'matrix_rollup'", "'need_graph'", "'novelty'", "'plan'", "'plugin_delta'", "'questions'", "'repo_budget'", "'repo_environment'", "'repo_readiness'", "'result'", "'skill_evidence'", "'subject_scorecard'", "'task_inspect'", "'task_runs'", "'tool_preference'"]

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

@exai-bot

exai-bot commented Nov 1, 2026 •

Copy link
Copy Markdown

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

By severity: 1 medium

Severity Category Location Finding
medium bug subjects.py:196-197 Must-fix: the rejected-sibling protection still misses hard links.
Review details
  • Commit reviewed: 581ed93 against merge base 575e59e
  • Coverage: 15 files in 3 groups
  • Engine: OpenCodeReview v1.13.3, us.openai.gpt-6.1-sol
  • Usage: 473k tokens (301k from cache), 48s
  • Tool calls: 36 (28 file_read, 7 code_search, 1 code_comment)
  • Skill evidence schema, generation, CLI, and tests: 8 files
  • Skill evidence artifact registration and run discovery: 4 files
  • Matrix rollup and subject scorecard integration: 3 files

Comment thread dokimi/dokimi/commands/subjects.py Outdated
scorecard = load_artifact("subject_scorecard", Path(args.scorecard))
except (OSError, ValueError, ArtifactValidationError) as exc:
usage_exit(f"cannot read subject scorecard {args.scorecard}: {exc}")
evidence = build_skill_evidence(scorecard, source=str(args.scorecard))

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: malformed cell data can escape input validation and crash this command. For example, take a valid subject_scorecard.v2 fixture, set cells to [null], and run dokimi subjects evidence sc.json: the scorecard schema only constrains cells to an array, so loading succeeds, but build_skill_evidence raises AttributeError at cell.get(...). Neither this handler nor the CLI's top-level handler catches it, resulting in a traceback instead of an actionable exit-2 error. Validate the cell/summary/per-arm shapes before conversion, translate malformed input into a usage error, and add a regression test for this input.

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.

Answered with evidence, no new change: 778a1ac already turns this into a usage exit. cmd_subjects_evidence maps AttributeError and TypeError from the build to usage_exit (exit 2), and test_subjects_evidence_refuses_a_scorecard_with_a_wrong_typed_cell now runs cells as [null], a non-object summary, a non-object per_arm, a non-object row, and a non-object significance.
Passes at 4eefca5. Left unresolved for your check.

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

Malformed nested scorecard content can trigger an uncaught AttributeError instead of a diagnostic and exit code 2, so input validation or rendering error handling needs correction.

1 open issue
What changed in this PR

1 new must-fix finding.

Adds a free subjects evidence command that turns saved subject scorecards into publishable skill_evidence.v1 documents, using the distribution digest introduced by #343.

Changes:

  • Exports paired statistics and significance-based verdicts without model calls.
  • Carries baseline-arm metadata through rollups and scorecards.
  • Registers the artifact and CLI route, with schema, fixtures, tests, and changelog updates.
File Description
dokimi/​tests/​test_subjects_evidence.py Tests evidence rendering, statistics, and CLI safeguards.
dokimi/​tests/​test_subjects_digest.py Tests distribution digests from the prerequisite change.
dokimi/​tests/​test_runs_show.py Adds evidence artifact summary expectations.
dokimi/​tests/​test_runs_scan.py Adds evidence artifact discovery expectations.
dokimi/​tests/​fixtures/​artifacts/​skill_evidence.v1.json Provides a sample evidence document.
dokimi/​dokimi/​subjects/​scorecard.py Records baseline arms and distribution digests.
dokimi/​dokimi/​subjects/​evidence.py Renders scorecard statistics as evidence.
dokimi/​dokimi/​schemas/​subject_scorecard.v2.json Allows the subject distribution digest.
dokimi/​dokimi/​schemas/​skill_evidence.v1.json Defines the experimental evidence format.
dokimi/​dokimi/​schemas/​matrix_rollup.v1.json Allows cell baseline-arm metadata.
dokimi/​dokimi/​runs/​kinds.py Registers evidence discovery and summary fields.
dokimi/​dokimi/​eval/​matrix_rollup.py Preserves each cell’s baseline arm.
dokimi/​dokimi/​commands/​subjects.py Adds the evidence command and output handling.
dokimi/​dokimi/​cli_routes.py Classifies evidence generation as free.
dokimi/​dokimi/​artifacts.py Registers the evidence schema version.
dokimi/​docs/​reference/​cli-interface.json Updates the CLI interface snapshot.
dokimi/​changelog.d/​20261031_130000_napetrov_skill-evidence.md Documents evidence export and baseline metadata.
dokimi/​changelog.d/​20261031_120000_napetrov_distribution-digest.md Documents the prerequisite distribution digest.

Review effort: Balanced

Comment thread dokimi/dokimi/commands/subjects.py Outdated
scorecard = load_artifact("subject_scorecard", Path(args.scorecard))
except (OSError, ValueError, ArtifactValidationError) as exc:
usage_exit(f"cannot read subject scorecard {args.scorecard}: {exc}")
evidence = build_skill_evidence(scorecard, source=str(args.scorecard))

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.

Answered with evidence, same as the thread above: 778a1ac routes AttributeError and TypeError from the build through usage_exit (exit 2). The wrong-typed test covers cells: [null], a non-object summary, per_arm, per-arm row and significance.
Passes at 4eefca5. Left unresolved for your check.

…dex carries

subject_scorecard.v2 gains subject.distribution_digest: sha256 of the
SKILL.md bytes for a file ref or a directory holding only SKILL.md, the
value a .well-known/agent-skills index.json entry of type skill-md
carries (agentskills PR intel#254). An archive's bytes depend on how it was
packed, so a skill with resources gets null, as does a non-local ref.
ref_digest is unchanged.

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

A free leaf that reads a saved subject_scorecard and writes the document
a publisher puts next to a skill: one result per (cell, non-baseline
arm) with model, harness, effort, n, delta, CI, corrected p-value and a
verdict, keyed by subject.distribution_digest so a client can match it
to the .well-known/agent-skills index entry it installed from. Values
the scorecard does not hold stay null.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…il a run records it

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
@napetrov
napetrov force-pushed the feat/evidence-format branch from d25d6f1 to d0f1704 Compare November 1, 2026 07:43
"""
if not ref:
return None
path = Path(ref.removeprefix("local:"))

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: non-local references can acquire a digest from unrelated local files. On POSIX, create base/context7:/org/lib/SKILL.md as the directory's only entry, then call distribution_digest("context7:/org/lib", base=base): this returns that file's SHA-256 instead of None. The descriptor loader accepts this reference, and the evidence renderer publishes the resulting digest as subject identity. Reject non-local reference schemes before filesystem lookup (while preserving Windows drive paths), and add a collision regression test; the existing non-local test only exercises a nonexistent path. This affects both subject and member digests through this helper.

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 4eefca5. distribution_digest returns None for a URI-scheme ref (context7:/..., https:...) before any filesystem lookup, after the local: prefix is stripped. A drive letter is not a scheme, so Windows paths are unchanged.
Regression test: a context7:/org/lib directory holding a SKILL.md gives None (POSIX only).
Left unresolved for your check.

@codecov-commenter

codecov-commenter commented Nov 1, 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 91.89189% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.21%. Comparing base (575e59e) to head (581ed93).

Files with missing lines Patch % Lines
dokimi/dokimi/subjects/evidence.py 89.47% 2 Missing and 2 partials ⚠️
dokimi/dokimi/commands/subjects.py 94.44% 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     #344      +/-   ##
==========================================
- Coverage   94.21%   94.21%   -0.01%     
==========================================
  Files         304      305       +1     
  Lines       38766    38840      +74     
  Branches     6567     6578      +11     
==========================================
+ Hits        36524    36593      +69     
- Misses       1429     1432       +3     
- Partials      813      815       +2     
Files with missing lines Coverage Δ
dokimi/dokimi/artifacts.py 95.12% <ø> (ø)
dokimi/dokimi/cli_routes.py 100.00% <ø> (ø)
dokimi/dokimi/eval/matrix_rollup.py 90.00% <ø> (+2.00%) ⬆️
dokimi/dokimi/runs/kinds.py 100.00% <ø> (ø)
dokimi/dokimi/subjects/scorecard.py 91.86% <ø> (ø)
dokimi/dokimi/commands/subjects.py 87.79% <94.44%> (+1.09%) ⬆️
dokimi/dokimi/subjects/evidence.py 89.47% <89.47%> (ø)
🚀 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 d0f1704. The renderer is small, reads every number from the scorecard and leaves a missing one null, and the verdict rule (significant after correction, else inconclusive) is right. Two risks are worth fixing before merge, both gaps in what the tests pin: one inline, one below. One nit, and a note on the stacking.

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

  • make ci-local: 27 passed, 3 failed, 7 skipped; 11,986 tests passed, 1 failed; changed-line coverage passes. The 3 failures are not the PR's: one timing assertion (test_unbalanced_quote_in_hook_completes_quickly, 1.02 s against a 1 s limit) passes when rerun alone and the PR does not touch that code; the two gitleaks steps could not install or run the tool.
  • Mutation run on the new code and tests: 60 mutants, 29 pass test_subjects_evidence.py, test_subjects_digest.py, test_runs_scan.py and test_runs_show.py.
  • Reproductions for the code findings, with the LLM transport mocked: malformed scorecards through dokimi subjects evidence, and distribution_digest with a colliding directory.

Findings that cannot be anchored inline:

  1. The baseline fallback and the interval null handling in evidence.py are unpinned. _baseline_arm (evidence.py:80-84) picks the baseline for any scorecard recorded before baseline_arm existed. The only fallback test has two delta-less arms, so it covers the "skip the cell" branch only. Replacing the fallback with return None, and dropping and not row.get("significance"), both pass the four test files. Likewise for ci (evidence.py:105-108): never returning null ({"lo": null, "hi": null, ...} for an arm with too few pairs) and keying the null check on ci_hi both pass. Add a cell with one delta-less arm and no baseline_arm (that arm is the baseline, the other is published), a cell whose delta-less arm carries significance (not treated as baseline), and an arm with ci_lo=None (expect ci is None). Also unasserted, each passing when removed: the subject kind is ... not 'skill' warning, the task-suite warning, and scorecard warnings carried through.

  2. The PR carries #343's commit. The first commit (099c2b8, distribution_digest) belongs to that PR. Rebase onto main once #343 merges, so the diff holds only this PR's two commits.

Checked, no issues

  • Pins: the verdict sign flip, ignoring significant_adjusted, the delta source, arm != baseline, a recorded baseline winning over the fallback, the source basename, and the --out-equals-scorecard and empty-results exits each fail a test when broken.
  • Schemas: subject_scorecard.v2 and matrix_rollup.v1 only gain optional nullable properties in objects that allow extra keys, so older artifacts still validate. skill_evidence.v1 is registered in SCHEMA_VERSIONS and runs/kinds.py, and the emitted document validates against it.
  • CLI: exit 2 with a message for a missing scorecard, a directory, --out naming the scorecard (also as ./sc.json), and an unwritable target. source.path keeps only the file name.
  • Commits: all three carry Signed-off-by.
Bot points I checked that no longer apply at this head (1)
  • exai-bot's point on a non-local ref picking up a digest from a local path does not apply to this PR's own change: distribution_digest comes from #343's commit, and I take it up in the review of #343.

Comment thread dokimi/dokimi/subjects/scorecard.py Resolved
Comment thread dokimi/dokimi/commands/subjects.py Resolved
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Comment thread dokimi/dokimi/subjects/evidence.py Outdated
Comment on lines +117 to +118
if delta is None or not sig.get("significant_adjusted"):
return "inconclusive"

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: a schema-valid scorecard with significance: {"delta_mean": 12.0, "significant_adjusted": "false"} is published with verdict improved. The scorecard schema does not constrain nested cell statistics, and the string is truthy, so neither input validation nor the new exception handler rejects it. This silently turns malformed significance data into a positive claim. Validate this flag as boolean/null (reject other types with a handled input error), and add a regression test for the string "false"; only the boolean true should permit a directional verdict.

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 4eefca5: _verdict gives a direction only when significant_adjusted is True. The string "false" is inconclusive.
Regression test: test_a_significance_flag_that_is_not_a_boolean_gives_no_direction, which failed before the change.
Left unresolved for your check.

@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 778a1ac, which fix the nit from my review:

  • 778a1ac: dokimi/dokimi/commands/subjects.py:178: wrapped build_skill_evidence so AttributeError/TypeError exits 2 with a not-well-formed message; added a cells: [null] test.

Left as is:

  • The body's only nit-labelled point is not present; findings 1 and 2 are risks/stacking, left to the author.

make ci-local at 778a1ac: 28 passed, 2 failed (gitleaks, host), 7 skipped.

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

…erdict

distribution_digest looks a ref up on disk only for a local ref, so a
scheme ref such as context7:/org/lib can no longer collide with a
directory of that name. A verdict needs significant_adjusted to be the
boolean true; the string "false" gives inconclusive. The baseline_arm of
a scorecard cell and the wrong-typed-cell exits are pinned by tests.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Comment thread dokimi/dokimi/commands/subjects.py Outdated
Comment on lines +188 to +189
with exit_on_unwritable(out):
save_artifact("skill_evidence", evidence, out)

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: a scorecard with cells[0].summary.per_arm["skill:s"].significance.n set to "6" passes the scorecard schema and rendering, but fails the evidence schema here. save_artifact raises ArtifactValidationError; neither exit_on_unwritable (which catches only OSError) nor the top-level CLI handler catches it, so the command produces a traceback and exit 1 instead of an input diagnostic and exit 2. Catch output validation failures and route them through usage_exit, and add a regression test for a wrong-typed nested statistic that survives rendering.

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 ac2b33c: a value the evidence schema rejects at save (here a string n inside significance) now goes through usage_exit, exit 2 with a diagnostic, not a traceback. Regression test test_subjects_evidence_refuses_a_nested_statistic_the_evidence_schema_rejects was red before the change. Local: test_subjects_evidence.py 19 passed; full suite 12047 passed, 8 skipped.

…ceback

A scorecard whose nested significance n is a string passes the scorecard
schema and renders, then save_artifact raised ArtifactValidationError
past both the output guard and the CLI handler. It now goes through
usage_exit like the other input diagnostics. Regression test: red before.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Comment thread dokimi/dokimi/commands/subjects.py Outdated
Comment on lines +186 to +187
if out.resolve() == Path(args.scorecard).resolve():
usage_exit("--out names the scorecard itself; pick another file")

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: on supported Python 3.11–3.12 (requires-python >=3.11), Path.resolve() raises RuntimeError for a symlink loop. For example, create loop -> loop and run dokimi subjects evidence sc.json --out loop with a valid scorecard: this check fails before exit_on_unwritable, and the CLI does not catch RuntimeError, so it prints a traceback instead of an exit-2 diagnostic. Catch resolution failures (RuntimeError on these versions and OSError) around this check and add a symlink-loop regression test. Moving it inside the current output guard alone is insufficient because that guard catches only OSError.

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 842720f: resolving --out now catches RuntimeError as well as OSError, so a symlink loop exits 2 with a diagnostic. Regression test test_subjects_evidence_refuses_an_out_that_is_a_symlink_loop, red before (traceback).

Comment thread dokimi/dokimi/commands/subjects.py Outdated
Comment on lines +186 to +187
if out.resolve() == Path(args.scorecard).resolve():
usage_exit("--out names the scorecard itself; pick another file")

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 protects only the output file, but save_artifact also writes or deletes &lt;out>.rejected. With a valid scorecard saved as sc.json.rejected, subjects evidence sc.json.rejected --out sc.json succeeds and then deletes the input scorecard as a stale rejected payload (artifacts.py:188–190). If evidence validation fails, the same path is overwritten with the rejected evidence instead. Reject an output whose .rejected sibling resolves to the input as well, and add a regression test that the scorecard remains intact.

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 842720f: --out is refused when its .rejected sibling resolves to the scorecard, since save_artifact removes or overwrites that sibling. The scorecard stays intact. Regression test test_subjects_evidence_refuses_an_out_whose_rejected_sibling_is_the_scorecard, red before (the valid save deleted the input). Local: test_subjects_evidence.py 21 passed; full suite 12049 passed, 8 skipped.

…sibling of the scorecard

Path.resolve() raises RuntimeError on a symlink loop before Python 3.13,
which escaped as a traceback. save_artifact also keeps and removes
"<out>.rejected", so an --out whose sibling is the scorecard would
delete or overwrite it. Both now exit 2; tests red before the change.

Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
try:
out_real = out.resolve()
# save_artifact keeps and removes "<out>.rejected"; that sibling must not be the scorecard.
stale_real = out.with_name(out.name + ".rejected").resolve()

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: with a valid scorecard, dokimi subjects evidence sc.json --out . (or --out / on POSIX) reaches out.with_name(...) with an empty out.name, which raises ValueError. This handler catches only OSError and RuntimeError, and the CLI does not catch this ValueError, so an invalid output path produces a traceback instead of a usage diagnostic (exit 2). Reject paths without a filename before constructing the sibling, or catch ValueError here, and add regression tests for current-directory and root output paths.

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 e5fa6df: the --out guard now catches ValueError too. with_name on . or / (no file name) exits 2 with a usage diagnostic, no traceback. Tests --out . and --out / (red before).

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

A bug and a nit in the new --out handling; the rest of the delta since 778a1ac holds. The path checks added for the bot findings crash on --out . and --out /, which exited 2 before.

What I ran, on a Linux review host at 842720f:

  • make ci-local: 30 passed, 0 failed, 7 skipped (Docker, conda, semgrep and other-Python legs not run locally); changed-line coverage passes.
  • Reproductions for the code findings, with the LLM transport mocked: dokimi subjects evidence with --out of ., /, .. and an empty string.
  • Mutation run on the new delta: 11 mutants, 8 killed by the test files. The survivors are the RuntimeError catch (the symlink-loop test passes without it on Python 3.13) and the one-letter drive-letter exclusion in the scheme regex.

Checked, no issues

  • Pins: dropping is True, the .rejected sibling check, the ArtifactValidationError handler and the scheme check each fail a test.
  • Baseline field: the scorecard test now calls build_subject_scorecard and asserts baseline_arm.
  • Commits: all 7 carry Signed-off-by.
Bot points I checked that no longer apply at this head (7)
  • Copilot's and exai-bot's points on a cells: [null] or other wrong-typed nested value do not apply: the build is wrapped and exits 2 with a diagnostic.
  • Copilot's point on non-object summary, per_arm, row or significance values does not apply: they exit 2 with a diagnostic at this head.
  • exai-bot's point on a non-local ref taking a digest from a local path does not apply at this head: a URI-scheme ref returns None before any filesystem lookup.
  • exai-bot's point on significant_adjusted: "false" giving improved does not apply: the verdict needs the boolean true.
  • exai-bot's point on a string n failing the evidence schema with a traceback does not apply: ArtifactValidationError now exits 2.
  • exai-bot's point on a symlink-loop --out does not apply: RuntimeError is caught and exits 2.
  • exai-bot's point on the .rejected sibling deleting the scorecard does not apply: that --out is refused.

Comment thread dokimi/dokimi/commands/subjects.py Resolved
Comment thread dokimi/dokimi/subjects/scorecard.py Resolved
antonwolfy and others added 2 commits November 1, 2026 20:51
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
with_name() on a path with no file name raises ValueError, which the
--out guard did not catch. It now exits 2 with a usage diagnostic.
Tests: --out . and --out / refused (red before). The drive-letter
scheme pin is Anton Volkov's test on the branch (541b928).

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. The delta since 842720f is one except tuple and two tests, and it fixes both open threads: --out . and --out / now exit 2 with a usage diagnostic, and the drive-letter exclusion in the scheme regex is pinned.

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

  • make ci-local: 30 passed, 0 failed, 7 skipped (Docker, conda, semgrep and other-Python legs not run locally); changed-line coverage passes.
  • Mutation run on the delta: removing ValueError from the except fails both --out cases; changing + to * in the scheme regex fails the drive-letter test.
  • Reproductions with the LLM transport mocked: dokimi subjects evidence with --out of ., / and .. all exit 2 without a traceback.

Checked, no issues

  • Commits: all 9 non-merge commits carry Signed-off-by.
  • Schemas and CLI snapshot: schema_compat, the CLI snapshot check and the registry checks pass.
Bot points I checked that no longer apply at this head (8)
  • Copilot's and exai-bot's points on a cells: [null] or other wrong-typed nested value do not apply: the build is wrapped and exits 2 with a diagnostic.
  • exai-bot's point on a non-local ref taking a digest from a local path does not apply at this head: a URI-scheme ref returns None before any filesystem lookup.
  • exai-bot's point on significant_adjusted: "false" giving improved does not apply: the verdict needs the boolean true.
  • exai-bot's point on a string n failing the evidence schema with a traceback does not apply: ArtifactValidationError now exits 2.
  • exai-bot's point on a symlink-loop --out does not apply: RuntimeError is caught and exits 2.
  • exai-bot's point on the .rejected sibling deleting the scorecard does not apply: that --out is refused.
  • exai-bot's point on --out . or --out / raising ValueError does not apply: ValueError is caught and exits 2.

# Conflicts:
#	dokimi/changelog.d/20261031_120000_napetrov_distribution-digest.md
#	dokimi/dokimi/subjects/scorecard.py
#	dokimi/tests/test_subjects_digest.py
Comment on lines +196 to +197
if stale_real == card_real:
usage_exit(f"{out}.rejected is the scorecard; a save to {out} would delete or overwrite it; pick another --out")

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 rejected-sibling protection still misses hard links. Take the schema-valid scorecard used by the string-n rejection test, run ln sc.json evidence.json.rejected, then dokimi subjects evidence sc.json --out evidence.json. The resolved paths differ, so this passes the guard; validation fails and save_artifact writes the rejected payload with rejected.write_text(...), truncating the shared inode and replacing the input scorecard with evidence. Check existing rejected siblings with samefile() as well as resolved-path equality, and add a regression asserting the scorecard bytes remain unchanged. The main output write uses atomic replacement, so an ordinary hard-linked --out does not have this problem.

@napetrov
napetrov merged commit 90a5637 into intel:main Nov 2, 2026
17 checks passed
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.

5 participants