eval-report
Skill132 starsChanged 8 days agoMITView on GitHub
Write the summary a person reads after an evaluation run: what ran, what it scored, which failures were the MODEL and which were the EVAL itself (out of turns, contaminated, unestablished golden, unreadable judge), what was learned, and what to do next. Every claim links to the artifact behind it. Use at the end of any run, before quoting a number to anyone. Never scores an answer (eval-answer) or decides who owns a failure (eval-diagnose).
3 other projects use a version of this.
Adopted by
Most recently taken up by
- agentscope-ai/OpenJudgetheir own version, first seen Oct 25, 2026
- modelscope/OpenJudgetheir own version, first seen Oct 9, 2026
- Terum-Inc/terum-skillstheir own version, first seen Sep 20, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- modelscope/OpenJudge★ 1.1ksame name
- agentscope-ai/OpenJudge★ 1.1ksame name
- Terum-Inc/terum-skills★ 28same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository37 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- fix-scan-findingSkill
- publisher-releaseSkill
- eval-answerSkill
- eval-diagnoseSkill
- eval-importSkill
- eval-improveSkill
- eval-judgeSkill
- eval-loopSkill
- malloy-analysis-reportSkill
- malloy-analysisSkill
- malloy-chartsSkill
- malloy-classifySkill
- malloy-compliance-reviewSkill
- malloy-dashboardsSkill
- malloy-define-agentSkill
- malloy-defineSkill
- malloy-discoverSkill
- malloy-documentSkill
- malloy-getting-startedSkill
- malloy-gotchas-modelingSkill
- malloy-html-data-appsSkill
- malloy-lookml-reviewSkill
- malloy-materializationSkill
- malloy-model-as-you-goSkill
- malloy-modelingSkill
- malloy-modelSkill
- malloy-notebooksSkill
- malloy-phrase-detectionSkill
- malloy-powerbi-reviewSkill
- malloy-publishSkill
- malloy-queriesSkill
- malloy-reportsSkill
- malloy-reviewSkill
- malloy-run-agentSkill
- malloy-source-unreachableSkill
The file
We split this skill into the rules it gives an agent
10 rules. 0 are common, 6 are distinctive, and 4 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
Do not read harness source to satisfy your own curiosity about a number.
- Distinctive: line 37
Read it when a number you must report cannot be explained any other way, and stop when it can.
- Distinctive: line 39
Do not propose harness fixes, refactors, flags or "while I was in there" improvements.
- Distinctive: line 41
When a harness defect DID change this run's number, the report gets one sentence: what the number should be and why.
- Distinctive: line 44
Keep a defect that changed nothing out of the report entirely.
- Very specific: line 61
If the truth server is not running it says so and prints the curl to run once it is.
- Very specific: line 91
Put it in the repository beside the set, as RESULTS.md.
- Distinctive: line 176
Do not invent this section.
- Very specific: line 445
Write a local path bare, as /abs/path/to/file, not as text.
- Very specific: line 449
Never link a run LABEL or a qid as though it were a path.
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Report a Run
A run directory is JSONL and a console summary that scrolls away. Neither is a result. This skill turns one into something a person can open, and it is the last step of every run, including a run that failed.
The complaint this exists to answer, in a reviewer's words: "eval runs and a bunch of stuff happens and it is hard to know the actual results."
Scope boundary: this skill reports what the ledger already says. It does not
score an answer (skill:eval-answer), decide who owns a failure
(skill:eval-diagnose), or edit a model (skill:eval-improve). If a number is
not in the ledger, do not put it in the report.
The report is about THIS RUN, not about the harness. Defects you find in the eval tooling while running it are real and worth filing, and they do not belong here: the reader wants to know what their model scored and why, not what is wrong with the thing that measured it. File those against the harness. The one exception is anything that qualifies THIS run's number -- a truncated attempt, a contaminated one, an unestablished answer key -- which the "Eval failures" section exists for.
Do not audit the harness. You were asked to measure a model. This is the most common way this job goes wrong, and it does not look like going wrong: a run turns up something odd in a script, the odd thing is genuinely a bug, and the reply comes back as a critique of the tooling with the model's score somewhere underneath. The reader asked what their model scored. Answer that.
So, unless the user asked you to work on the harness:
- Do not read harness source to satisfy your own curiosity about a number. Read it when a number you must report cannot be explained any other way, and stop when it can.
- Do not propose harness fixes, refactors, flags or "while I was in there" improvements. Not in the report, not in the chat reply.
- When a harness defect DID change this run's number, the report gets one sentence: what the number should be and why. Not the mechanism, not the file, not the fix.
- Keep a defect that changed nothing out of the report entirely. Mention it once in chat, in a line, and let the user decide whether they want it chased.
A harness bug you found and did not chase is not a loose end. It is the job being done. If the user wants it fixed they will say so, and then it is a different task with its own turn.
Step 1: build the artifacts, before writing a word
python3 skills/eval-loop/scripts/eval.py package --set <set-dir> --label <label>That builds a Malloy package over the run's own CSVs, registers it with no
restart, and prints the two URLs below. It registers on the TRUTH server,
because the package holds the answer key and the answerer must not reach it.
If the truth server is not running it says so and prints the curl to run
once it is. A set with no truth server gets no registration: the only
Publisher is the answerer's. Add a [truth] section and
eval.py serve truth, or pass --on-model-server, which prints the curl
and the DELETE to run before the next run.
It refuses a run with no diagnosis, since the report's cluster views would be
empty. Run eval.py diagnose first; when this run skipped diagnosis on purpose,
pass --without-diagnosis and say in the report that it has no clusters.
It gives you two things to link:
| Artifact | What it is | URL |
| The case matrix app | Every question, its verdict, which needed entities retrieval delivered, and a drawer per case holding the reference answer, the judge's reasoning, the re-executed rows and every query the answerer ran | <publisher>/environments/<env>/packages/eval-<label>/ |
notebooks/eval_run.malloy | The aggregate tables: pass rate, effort, cost, most-missed entities, the backlog | <publisher>/<env>/eval-<label>/notebooks/eval_run |
Those two URLs are in different path spaces, and guessing costs a 404. The
app is served by the in-package public/ handler, which owns
/environments/<env>/packages/<pkg>/<file>. The notebook is a MODEL, rendered
by the Console, whose routes are /<env>/<package>/<model path> with no
environments or packages segment at all. Putting the notebook under the
app's prefix 404s, because the public-file handler answers there and the
notebook is not in public/. Verified both, on a real package, by opening
them.
Pass --run more than once to put two arms side by side.
Step 2: write the report
Put it in the repository beside the set, as RESULTS.md. Not in a chat log,
not in ~/Downloads, both of which have lost a findings document before.
Derive every number from events.jsonl and run.json, not from memory or
from the console. A figure retyped from scrollback is a figure nobody can
check, and the console rounds.
Keep it under a page and a half
Budget: about 80 lines, and 120 is the ceiling for a run with several distinct failures. Reports have shipped at 200 and the length is not thoroughness -- it is the artifact links restated as prose, the cascade explained twice, and a paragraph apologising for a figure nobody disputed. A reader who wants the detail opens the case matrix, which is why step 1 builds it. What cannot be recovered from the artifacts is your judgement: what broke, why, and what to do. Spend the lines there.
What earns its place: the headline, one row per case, one short entry per failure, the retrieval numbers, and the next steps. What does not: restating a number you already gave, explaining what a cascade is before showing it, defending a decision nobody questioned, or any section whose content is that nothing happened -- except "Eval failures", where "None." is the point.
Being brief is not being terse with the vocabulary. The reader does not
know what near_match, a cluster, recall or a holdout is, so the first time
one appears, say what it means in the same sentence -- "near_match, which is
excluded from the pass rate" -- and then use it. Cutting the explanations is
how a short report becomes an unreadable one; cutting the restatements is how
it becomes a good one.
The template
# <set> -- <label>
<one sentence: what was being measured, against what, and why now>
## What ran
- Set `<name>` v`<datasetVersion>`, N cases (N dev, N holdout)
- Target `<env>/<package>`, model `<modelPath>`, pinned `<modelSha or targetVersion>`
- Answerer `<model>`, judge `<model>`, cap `<maxTurns>` turns
- Steps run: scrape/run, eval<, diagnose><, improve>
- Cost $X answerer + $Y judge, N turns median / N p90, N entities delivered per answer
## The result
**N of M decided (P%).** <-- or: **No pass rate.** See "Eval failures" below.
| qid | question | verdict |
|---|---|---|
[case matrix](<url>) - [aggregate tables](<url>)
## Coverage, retrieval, accuracy, cost
**Coverage N of M** -- how many questions the model can express an answer to at
all. **Entity recall N%** -- one number, first, before the cascade. It is the share
of the entities an answer needed that retrieval actually handed the agent, and
it is the headline of this section for the same reason the pass rate is the
headline of the last one. Then the cascade: Covered? -> Retrieved? -> Correct?,
with the per-arm numbers under each. Say in one clause what recall counts, then
give the number.
## Model failures
One entry per wrong answer. What it got wrong in plain words, then the
mechanism, then the link.
## Eval failures
What went wrong with the MEASUREMENT rather than the model. Empty is a real
and good answer; say "none" rather than dropping the section.
## Query errors
Malloy the answerer wrote that would not run, and whether it recovered.
## What this run taught us
## What to do next"What to do next" is a checklist, and most of it is derived
Do not invent this section. Most of it falls out of what the run did NOT do, and a reader should be able to see that nothing was skipped silently. Walk these in order and put every one that fires into the list, with its command:
| If the run shows | Then the next step is |
coverage: unmeasured | the run was given --no-coverage, or the measurement failed and said so. It is measured by default, so say in "Eval failures" that this score cannot tell a model gap from a bad answer, and re-run without the flag |
goldenCheck: skipped or the set names no truthPackage | build one with init_truth_package.py; until then the goldens were derived through the model under test and certify themselves |
any golden still provisional | re-derive and verify_goldens.py --promote |
| a stale entity name warning | fix expectedEntities; it scores as a retrieval miss on every run until you do. A next step, not a section: it goes in this list and nowhere else in the report |
| a passing case with recall below 1.0 | check whether required over-specifies one path |
truncated non-empty | re-run those cases at a higher cap with --from |
| diagnose did not run | run it, or say the failures have no owner yet |
a cluster with owner: model | skill:eval-improve, then the acceptance check |
a cluster with owner: agent-skill | edit that skill. This is NOT a dead end |
a cluster with owner: dataset | the golden side door in skill:eval-loop |
| only one arm exists | note that no noise band has been measured for this set yet |
An agent-skill cluster is work, not an absence of work. eval-improve
may not touch it, and writing "nothing to do, the model is fine" there is how a
real defect gets closed as a non-finding. Name the skill, name the rule to add
or change, and say who owns it. The model being innocent is a statement about
the model, never about the run.
Write it for someone who was not there
The reader is a colleague who did not run this and does not know the loop's vocabulary. Two rules, both learned by handing a report to one:
- Never make a bare count carry the meaning. "The only cluster is
owner: agent-skill" tells a reader nothing: what is a cluster, and what follows from it? Write what happened and what to do: "The one wrong answer came from the agent picking the wrong kind of field, so the fix is in the analysis skill, not in the model." - Do not open with a negation of something you just reported. A section that says "nothing to do" directly under a section reporting a wrong answer reads as a contradiction, and the reader stops trusting both. Lead with the wrong answer and what closes it; put anything genuinely needing no action after that, and say why.
The two failure sections, and why they are separate
This is the part most reports get wrong. A wrong answer and a broken measurement look identical in a pass rate and have nothing else in common: one is work for the model owner, the other is work for whoever runs the harness. Mixing them sends a modelling agent to fix a model that is fine.
Model failures are cases that were fairly asked, fairly answered and got the wrong answer. One entry each: what the answer said, what was right, and the mechanism in one sentence. Do not write a cluster id here; write what it got wrong.
Eval failures are the things that stopped the run measuring the model, and only those. The test is one question: did this cost a verdict, or make one untrustworthy? If no, it does not appear in the report at any length.
That rules out most of what is tempting to put here, and all of it has been put
here on a real run: a -dirty model pin, a stale-entity-name warning that cost
no case, a defect you hit in the harness and worked around, a setup step that
took two tries, anything you would open with "worth knowing, though it changes
no verdict". A reader wants to know what their model scored. Harness defects are
real and belong in a harness issue, filed against the harness -- that is the
skill's opening rule, and this section is where it gets broken.
The section is usually two words. "None." is a complete and good answer, and a reader who sees it learns exactly what they need to. Do not pad it into a paragraph explaining the absence.
Report every one that DID occur, with its count and its qids, and say plainly that these are NOT evidence about the model:
| What happened | How it reads in the ledger | Who fixes it |
| Answerer ran out of turns | reason: answerer_truncated, and run.json truncated | raise --max-turns, re-run those cases with --from |
| Isolation breached | reason: contaminated, contamination_reasons on the attempt | harness config; the answerer held a tool it should not |
| Harness or server failed | reason: environment_failure: <what> | fix the environment, re-run the arm |
| Answer key not established | reason: golden_provisional / _invalid / _ambiguous / _missing | derive the key (skill:eval-import), or the golden side door |
| Judge reply unreadable | reason: judge_unparseable | it already retried; read artifacts/<qid>/judge.md |
| Judge doubted the key | gold_status suspect / verified_wrong, run.json doubtedGoldens | the golden side door in skill:eval-loop, NOT improve |
| Rubric quoted a stale figure | verify_goldens.py check 2 review items | repair the rubric against its own golden rows |
| Arm stopped early | run.json status: aborted | four consecutive dead attempts; fix and re-run |
If any of the first three occurred, there is no pass rate to quote. The
harness prints INCOMPLETE and withholds the percentage; the report does the
same. Give the counts and the re-run command instead of a number with a caveat,
because the number is what gets repeated and the caveat is what gets dropped.
The four measurements, and why a report carries all of them
A pass rate says an answer was wrong. It does not say where, and on its own it makes every failure look like the model's. A run measures four things, in this order, and each one tells you whether the next is even a fair question:
| Measures | A failure here means | |
| a. Coverage | can the data and the model express an answer at all | nothing downstream was winnable. Fix the model |
| b. Retrieval | did get_context hand the agent the entities it needed | the model has it and the agent never saw it: the docs, or the search wording |
| c. Accuracy | did the agent get the answer right | it had what it needed and still missed: the agent, or a doc that misleads |
| d. Cost | dollars, turns, wall-clock, entities per answer | it works and cannot be afforded, which is its own kind of not working |
Coverage first, and a report without it is incomplete. A model that cannot
express an answer can never succeed at that question, so a pass rate quoted
without coverage cannot distinguish a bad model from an unanswerable set.
run_baseline.py measures it by default; if a run skipped it, say so in
"Eval failures" and treat the score as provisional.
Cost is a result, not an aside. Report dollars, the turn and call counts, and the entity payload per answer. An agent that answers correctly in 40 turns and 75 delivered entities per question is a different product from one that does it in 8 and 5, and only the report says which you have.
Report them all, because a report that omits them makes every failure look like the model's:
Copy the cascade the run printed. Do not re-derive it. cascade_lines()
in run_baseline.py already renders every rung, and it carries one field a
hand-made table keeps losing: how many cases answered correctly anyway
after stopping on that rung. Paste its block into the report.
A finer measurement must never revise the headline result. This is the rule
the re-derivation breaks. Written as a funnel with a shrinking denominator --
5 of 10 covered, then 3 of the 5, then 3 of the 3 -- the last rung reads
as "only 3 of 10 succeeded" on a run where all ten answers were correct.
That report went to a reviewer, and the objection was the right one: adding
detail about retrieval cannot turn a 10 of 10 into a 3.
Two things stop it:
- Report every rung over all N cases, never over the survivors of the rung above. The rungs are three measurements of the same cases, not a narrowing of them.
delivered, rightis NOT the pass rate. The passing cases aredelivered, rightpluspassed_not_coveredpluspassed_not_retrieved. A report that omits those last two has dropped the only numbers that reconcile the cascade with the score.
Label a rung for what it measures, not for what a reader will assume.
"Can the model express an answer?" is wrong for most of what lands on the no
side of the first rung. RULE_UNWRITTEN means the data is present and the
model does not encode the rule for combining or filtering it; AMBIGUOUS means
several candidates and no doc saying which the question means. The model can
express an answer in both -- it does not say WHICH answer is meant. So the rung
asks whether the model NAMES what the question needs.
Give the per-entity number too, and label both.
- per CASE, the cascade: did this case get everything it needed
- per ENTITY, recall: of the N entities the answers depended on, how many were delivered
They answer different questions, and the entity number is the more natural read
of "how did search do". One run reported 3 of 5 cases and buried 16 required
entities, 80% delivered, and the case number was the only thing a reader saw.
- Covered? Can the model express a correct answer at all? Not computed by
the run:
check_coverage.pyreads the MODEL rather than the answers, and the run consumes its report through--coverage. If it was not run, sayunmeasuredrather than leaving the row out -- an unmeasured coverage label is not evidence that the model covers the question. - Retrieved? Of the entities the golden answer depends on, how many did
get_contexthand back. Quoterequired_count,delivered_countand how many were ranked rather than merely mentioned in a returned source's docs. - Correct? The pass rate, which is the rung the other two qualify.
Two numbers here are routinely misread, so qualify them in the report or leave them out:
- Entity precision is a PAYLOAD number, not a retrieval-quality one. Report it as what it measures: how much context the agent was handed against how much it needed. 52 entities returned per attempt for the 2 an answer used is a real cost, in tokens and in attention, and it is worth tracking. What it cannot tell you is whether retrieval worked, because the denominator is everything returned and it ignores rank: an entity that came back at rank 3 of 51 scores identically to one at rank 51.
- For quality, report rank. "Required entities came back at median rank 3, 9 of 16 in the top 5" is a statement about retrieval. Precision alone reads as an indictment of it and is mostly a statement about how many fields the package has.
- Report the misses split by cause, not as one recall figure. The run
attributes each to
never asked(no target of a type that could return it -- deterministic, becausetarget_typeis a hard filter on the server, so no documentation could have delivered it) ornot retrieved(a compatible target was issued and it still did not come back, which is the docs or the wording, and diagnose separates them). Those have different owners and one recall number hides which you have. - A miss on a case that PASSED is still a miss, and the report says so. The
answer was right by another route; name the route. Measured on one run, 3 of
4 findings were on passing cases and the route was always the same: the agent
rebuilding the model's own measure inline, which held for
count()and failed on the one measure carrying a grain rule. - Read the entities that did NOT come back, and what was asked for. That is
where the retrieval signal actually is, and the misses are rarely independent.
Measured on one run: 5 required entities never came back as ranked results,
and 4 of the 5 had the same cause -- the agent sent only
sourceanddimensiontargets, andtarget_typeis a hard filter, so no measure could be returned however well the model documents it. One defect, five symptoms, and the same one that produced the run's only wrong answer. A per-case list of misses beside what was asked for would have shown it in a glance. - A PASSING case with recall below 1.0 is usually an expectation defect, not
a retrieval miss: the set named one path to an answer the agent reached by
another. Report the count and read it as a prompt to check
required.
Query errors are worth a section of their own
Malloy the answerer wrote that would not compile or run, whether or not the
case passed. These sit on tool_call events as error, and nothing else
surfaces them: a case that errored twice and then recovered scores exactly like
one that got it right first time, so the effort disappears.
They are the sharpest available evidence on whether the skills and the docs are leading agents astray, because each one names a specific thing the agent believed and the language does not support. Report the count, the distinct error kinds, and for each whether an existing skill already covers it. An error whose fix IS documented, in a skill the answerer did not open, is a finding about the skills rather than about the agent.
eval-diagnose does not see these today: it reads failing cases only, so an
error inside a passing case is invisible to it. Say so rather than implying
they were triaged.
Say what did not run, and why
A step that did not run is a result, and it is indistinguishable from having
forgotten unless it is written down. The common one: improve does not run when
every cluster came back owner: agent-skill or owner: dataset, because
skill:eval-improve may only touch owner: model. That means the model is not
at fault, which is worth a sentence rather than a silence.
Same for diagnose. If it did not run, no failure in this report has an owner yet, and the report says so rather than implying the failures are the model's.
Findings and improvement ideas
Two different things, kept apart.
- Findings are what this run established, each with the evidence beside it. A finding with no artifact behind it is an opinion.
- Improvement ideas are what somebody might do about them, tagged with who owns each: the model, the answer key, the analysis skills, or the harness. An idea is a proposal, not a decision, and the report does not pretend a cluster has been triaged when it has not.
Rank by how many cases each would move, and say when the answer is "nothing": a 90% run whose one failure is a known fan-out trap may need no action at all.
Report facts, do not instruct the reader. "This set has had one arm, so no noise band exists for it" is a fact they can act on. "Do not quote 90% until X" is a lecture about their own number, and they did not ask for one. State what was and was not measured; what to do with it is theirs.
Make the links clickable
A report whose evidence cannot be opened is a report nobody checks.
- Absolute paths, never relative ones. A reader is not in your working directory, and a terminal only makes an absolute path clickable.
- The served URL for the app and the notebook, with the real host and port the run used, not a placeholder.
- Per case, link the FILES, not the directory:
artifacts/<qid>/answer.md,artifacts/<qid>/judge.md,artifacts/<qid>/answerer.jsonl. Afile://link to a directory opens nothing in most editors, which is a dead link that looks live. - Move the run somewhere durable before you link it. A run written under a
session scratch directory (
/tmp/..., a host's per-session sandbox) is deleted when the session ends, and many editors will not linkify a path there even while it exists. Copy the run directory, artifacts included, next to the report under the set, then link that. A report whose evidence is in a temp directory is a report with no evidence by next week. This was caught twice by a reader clicking and getting nothing. - Write a local path bare, as
/abs/path/to/file, not as[text](file:///...). Terminals and editors linkify a bare absolute path; afile://markdown link is frequently inert, and an inert link is worse than the path in plain text because the reader has nothing to copy. - Never link a run LABEL or a qid as though it were a path.
faa-v1-baseline-01is a label; the path is the run directory. A backticked label that looks like a link and resolves to nothing is worse than plain text, and it has already wasted a reader's time.
Related skills
skill:eval-loop: conducts the run this reports on.skill:eval-answer: the verdicts and the ledger schema this reads.skill:eval-diagnose: owners and clusters, when it ran.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

