Eval suite authoring
eval-suite-authoring · v1.0.0
ivyx
Use when someone wants to test, evaluate or regression-check their prompt, instruction class or assistant answers (eval, test seti, golden set, değerlendirme) with cases and a pass bar; writes the cases and the suite, binds an engine and runs it once.
Who is asked
Risk medium: you are asked when it loads, and that yes covers reading its files in the same chat.
Tools it may call
While this skill is active, a call to any other tool is refused. These still meet every policy rule; the list narrows, it approves nothing.
evals.enginesfs.listfs.readFilefs.writeFileevals.suite.runWhat it tells the agent
The published SKILL.md, as written.
--- name: eval-suite-authoring description: Use when someone wants to test, evaluate or regression-check their prompt, instruction class or assistant answers (eval, test seti, golden set, değerlendirme) with cases and a pass bar; writes the cases and the suite, binds an engine and runs it once. allowed-tools: evals.engines fs.list fs.readFile fs.writeFile evals.suite.run metadata: risk: medium ---
Writing an eval suite
A suite is a jsonl file of cases and a block in .punica/evals.yaml that
names a target, the dataset, an engine and thresholds. A suite with no
engine is silently dropped by the hub, so the engine is the first thing
you check, not the last.
1. Which engines exist
evals.engines first, with no arguments. Read engines: for answers that
are text (a hint, a subject line, a JSON verdict) the engine is text;
for a node or agent manifest it is manifest; for retrieval it is rag.
If catalogReadable is false or the engine you need is absent, say so and
stop: a suite cannot be bound to an engine that is not installed.
2. Read what exists
fs.readFile on .punica/evals.yaml. If it exists, you add a suite to it
and keep every other suite as it is; if it does not, you create it.
Then read the target itself. A workspace instruction class <id> lives at
.punica/instruction-classes/<id>.ic.yaml; a prompt lives where the
person said. Read its system prompt: it says what the target is given
(a policy text, a document, a record) and what it must never do. When it
names material the target answers from, find that material in the
project: fs.list with path: "." shows the top of the project, and the
policy, document or record files are in the folder whose name says so
(docs, policies, content); fs.list that folder, then read each
file in it with fs.readFile. Never guess a path and never write the
material yourself:
the cases come from those files, not from what an assistant of this kind
usually says, and if the project holds no such file, say so and ask for it
before writing a case.
3. Write the cases
Read the exact shape from the file suite-template.md of this skill (ask
for it with { "file": "suite-template.md" }), then write
.punica/evals/<suite-id>.jsonl with fs.writeFile: one JSON object per
line, four to eight cases, each with an id, an input (the whole prompt
the target gets, as text: when the target is given a policy or a document
and a question, the input holds that text and the question, in the order
the system prompt describes) and an expect object holding only checks
the engine can run (contains, notContains, matches, maxLines,
maxChars, requiredKeys) and a note saying what a person would look
for. An expectation is a fact the material states (a number of days, a
price, a city, a word the target must not use), so a wrong answer fails
and a vague one does not pass; at least one case asks something the
material does not answer and expects the target to say so. Cases come
from the person's examples when they gave any; otherwise write them from
the material and say they are theirs to edit.
4. Write the suite
fs.writeFile .punica/evals.yaml with the block from the template: id,
target (kind and id), dataset, samples: 1, engines with the
engine id from step 1, thresholds with expect.satisfied at the bar the
person wants (1.0 unless they said otherwise), onFailure: stop. Keep
version: 1 and baseline at the top level.
5. Run it once
evals.suite.run with suite set to the new id. Read pass, reason and
report: how many cases ran, the expect.satisfied mean, which cases
failed and on which check. A run that invokes a local model takes minutes;
wait for it.
6. Report
- The files written, with the case count and the engine.
- The run: pass or fail, the mean against the bar, each failing case by id with its first failing check.
- What to do with a failing case: fix the target, or change the case when the expectation was wrong; never lower the bar to make the run green.
- Answer in the language the person wrote in.
Files it reads
Loading the skill lists these to the agent. It reads one when the instructions send it there.
suite-template.md
# Suite and case shapes
## `.punica/evals.yaml`
```yaml
# Eval suites. Each suite runs a target over a jsonl dataset, scores the
# output with the listed engines, and must clear its thresholds.
version: 1
suites:
- id: <suite-id> # lower-case, dashes
target:
kind: instructionClass # or capability
id: <the instruction class or capability id>
dataset: .punica/evals/<suite-id>.jsonl
samples: 1 # repeats per case; more costs more model calls
engines:
- id: text # from evals.engines; options are optional
options:
json: false # true when the answer must be JSON
thresholds:
expect.satisfied: 1.0 # fraction of declared checks that must hold
onFailure: stop
baseline: .punica/evals/baseline.json
```
Add a second suite as another `- id:` entry under `suites:`; never remove
one that is there.
## `.punica/evals/<suite-id>.jsonl`
One object per line. `input` is the whole prompt the target receives.
`expect` holds checks and a `note`; a prose `expect` declares nothing and
sends the case to a person.
```json
{"id": "short-answer", "input": "…the prompt text…", "expect": {"maxLines": 3, "notContains": ["```"], "note": "one short answer, no code fence"}}
{"id": "names-the-file", "input": "…", "expect": {"contains": ["README"], "maxChars": 200, "note": "mentions the file it is about"}}
{"id": "json-verdict", "input": "…", "expect": {"requiredKeys": ["verdict", "reason"], "note": "a JSON object with both keys"}}
```
Checks the `text` engine runs: `contains`, `notContains`, `matches`,
`notMatches`, `minChars`, `maxChars`, `maxLines`, `requiredKeys`.
`expect.satisfied` is the share of these that held across the run.