fix(grade): read assertions from the live evals.json (#295) - #300
Merged
Conversation
`grade` read the eval definition from the copy the iteration froze
(`iteration-N/.skills/<skill>/evals/evals.json`), so assertions authored
after the run — the workflow `eval-magic docs judging` prescribes — were
never graded. The result was `Judge tasks: 0` with no error and no
warning, and no way to tell that the file being graded was not the file
just edited.
Freezing is right for the treatment: what the agent loaded must not
change after the dispatch it explains. Assertions are not the treatment.
They are the measuring instrument, and the documented loop authors them
from the run's own paired evidence.
So grade now splits the file. `assertions` and `skill_should_trigger`
come from the live `<skill>/evals/evals.json`, matched per eval id;
prompt, files, turns, codebase, guard, and runs stay as the run captured
them. `command_check.setup_files` resolve from whichever tree supplied
the assertion that names them.
Every invocation prints the file it measured against, so a zero-task
summary is never ambiguous, and each `grading.json` records it under
`assertion_source` with a digest of the graded assertion set. An
unreadable live file falls back to the run-time copy with a warning; an
invalid one fails rather than grading around a broken edit. Eval ids
present in one file and not the other are named.
Cached judge verdicts and command-check results are keyed by assertion
id, so an assertion edited in place is now reported by name with the
`--overwrite` remedy instead of being silently reused.
Before / after, same iteration, one 5-sample llm_judge added after the
run:
Judge tasks: 1 (1 skill-invocation meta-judge(s))
Judge tasks: 11 (1 skill-invocation meta-judge(s))
Verification: cargo fmt --check, cargo clippy --all-targets -D warnings,
cargo test (923 + 213 + 201 passing).
Schema updates: `grading.schema.json` gains optional `assertion_source`;
`command-check.schema.json` gains optional `definition_digest`. Both are
absent in older artifacts and read as making no claim.
Docs: `grade --help`, `eval-magic docs judging` (new "Which evals.json
grade reads" section, plus the missing step in the explore-first loop),
`eval-magic docs isolation`, and RUNBOOK step 2, which previously ended
at `compare` with no route back into grading.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MrfVg8vNeeZN7upRmWerYC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #295.
The problem
graderead the eval definition from the copy the iteration froze(
iteration-N/.skills/<skill>/evals/evals.json), so assertions authored after therun — the workflow
eval-magic docs judgingprescribes — were never graded. Theresult was
Judge tasks: 0, with no error and nothing saying the file being gradedwas not the file just edited.
Freezing is right for the treatment: what the agent loaded must not change after
the dispatch it explains. Assertions are not the treatment. They are the measuring
instrument, and the documented explore-first loop authors them from the run's own
paired evidence.
The change
gradesplits the file.assertionsandskill_should_triggercome from the live<skill>/evals/evals.json, matched per eval id;prompt,files,turns,codebase,guard, andrunsstay as the run captured them. Resolution lives in anew
src/pipeline/grade/instrument.rs.command_check.setup_filesresolve fromwhichever tree supplied the assertion that names them, so a check added after the run
finds its held-out file.
Every invocation names the file it measured against:
Each
grading.jsonrecords the same underassertion_source(path, digest,refreshed). An unreadable live file falls back to the run-time copy with a warning; an
invalid one fails rather than grading around a broken edit. Eval ids present in one
file and not the other are named.
Two follow-on silences also closed: cached judge verdicts and command-check results
are keyed by assertion id, so an assertion edited in place is reported by name with
the
--overwriteremedy rather than silently reused.Before / after
Same iteration, one 5-sample
llm_judgeadded after the run, no hand-copying into theworkspace:
Previously the second run also printed
Judge tasks: 1.Schema updates
grading.schema.jsongains optionalassertion_source.command-check.schema.jsongains optionaldefinition_digest.Both are absent in older artifacts and read as making no claim, so existing iterations
still validate and
aggregatereads them unchanged.Documentation
grade --helpstates the split and where it is recorded.eval-magic docs judging— new "Which evals.json grade reads" section, plus the stepthe explore-first loop was missing: which file to edit, and what re-reads it.
eval-magic docs isolation— the copy freezes the treatment, not the assertions, sothe two guides no longer contradict each other.
comparewith no route back into grading; goldenfixtures re-blessed.
Verification
Plus a manual end-to-end run of the ticket's reproduction against the built binary,
covering the refresh, the recorded
assertion_source, the stale-rubric warning, themissing-live-tree fallback, and the invalid-live-file failure (exit 1).
One existing test,
tests/run/skill_source.rs::grading_reads_the_eval_definitions_the_run_copied,encoded the superseded contract by making the live file unreadable. It now asserts the
narrower one: the eval definitions still come from the copy, assertions are the
deliberate exception, and both sides of an eval-set mismatch are reported.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MrfVg8vNeeZN7upRmWerYC