@slowdini/slow-powers-opencode 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +174 -0
- package/bootstrap.md +16 -0
- package/opencode/plugins/slow-powers.js +86 -0
- package/package.json +66 -0
- package/skills/auditing-slow-powers-usage/SKILL.md +157 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/BASELINE.md +22 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/NOTES.md +72 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/benchmark.json +53 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/grading/audits-blindspot-session__with_skill.json +53 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/grading/audits-blindspot-session__without_skill.json +38 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/grading/audits-completed-session__with_skill.json +53 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/grading/audits-completed-session__without_skill.json +38 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/grading/ordinary-dev-task-no-audit__with_skill.json +17 -0
- package/skills/auditing-slow-powers-usage/evals/baseline/grading/ordinary-dev-task-no-audit__without_skill.json +17 -0
- package/skills/auditing-slow-powers-usage/evals/evals.json +74 -0
- package/skills/auditing-slow-powers-usage/evals/fixtures/audits-blindspot-session/session-summary.md +39 -0
- package/skills/auditing-slow-powers-usage/evals/fixtures/audits-completed-session/session-summary.md +33 -0
- package/skills/evaluating-skills/SKILL.md +448 -0
- package/skills/evaluating-skills/evals/evals.json +52 -0
- package/skills/evaluating-skills/evals/fixtures/iron-law/candidate-skill.md +13 -0
- package/skills/evaluating-skills/examples/verification-before-completion-evals.json +30 -0
- package/skills/evaluating-skills/harness-details/claude.md +135 -0
- package/skills/evaluating-skills/pressure-scenarios.md +163 -0
- package/skills/evaluating-skills/runner/README.md +140 -0
- package/skills/evaluating-skills/runner/adapters/claude-code-transcript.test.ts +263 -0
- package/skills/evaluating-skills/runner/adapters/claude-code-transcript.ts +146 -0
- package/skills/evaluating-skills/runner/aggregate.test.ts +188 -0
- package/skills/evaluating-skills/runner/aggregate.ts +228 -0
- package/skills/evaluating-skills/runner/context.test.ts +181 -0
- package/skills/evaluating-skills/runner/context.ts +90 -0
- package/skills/evaluating-skills/runner/detect-stray-writes.test.ts +103 -0
- package/skills/evaluating-skills/runner/detect-stray-writes.ts +192 -0
- package/skills/evaluating-skills/runner/fill-transcripts.test.ts +73 -0
- package/skills/evaluating-skills/runner/fill-transcripts.ts +154 -0
- package/skills/evaluating-skills/runner/grade.test.ts +347 -0
- package/skills/evaluating-skills/runner/grade.ts +603 -0
- package/skills/evaluating-skills/runner/guard/guard.ts +49 -0
- package/skills/evaluating-skills/runner/guard/install.test.ts +92 -0
- package/skills/evaluating-skills/runner/guard/install.ts +147 -0
- package/skills/evaluating-skills/runner/guard/policy.test.ts +71 -0
- package/skills/evaluating-skills/runner/guard/policy.ts +74 -0
- package/skills/evaluating-skills/runner/promote-baseline.test.ts +230 -0
- package/skills/evaluating-skills/runner/promote-baseline.ts +186 -0
- package/skills/evaluating-skills/runner/run.test.ts +716 -0
- package/skills/evaluating-skills/runner/run.ts +814 -0
- package/skills/evaluating-skills/runner/sandbox-policy.ts +74 -0
- package/skills/evaluating-skills/runner/types.ts +104 -0
- package/skills/evaluating-skills/runner/validate-all.ts +54 -0
- package/skills/evaluating-skills/runner/validate-schema.test.ts +99 -0
- package/skills/evaluating-skills/runner/validate-schema.ts +51 -0
- package/skills/evaluating-skills/runner/validate.test.ts +56 -0
- package/skills/evaluating-skills/runner/validate.ts +21 -0
- package/skills/evaluating-skills/schema/evals.schema.json +105 -0
- package/skills/evaluating-skills/schema/grading.schema.json +84 -0
- package/skills/evaluating-skills/schema/run-record.schema.json +80 -0
- package/skills/evaluating-skills/schema/stray-writes.schema.json +68 -0
- package/skills/evaluating-skills/templates/eval-task-prompt.md +71 -0
- package/skills/evaluating-skills/templates/evals.json.example +17 -0
- package/skills/evaluating-skills/templates/judge-prompt.md +56 -0
- package/skills/evaluating-skills/templates/revise-skill-prompt.md +56 -0
- package/skills/finishing-a-development-branch/SKILL.md +96 -0
- package/skills/finishing-a-development-branch/evals/evals.json +41 -0
- package/skills/finishing-a-development-branch/evals/fixtures/finish/package.json +4 -0
- package/skills/finishing-a-development-branch/evals/fixtures/finish/sum.test.ts +5 -0
- package/skills/hardening-plans/SKILL.md +72 -0
- package/skills/hardening-plans/evals/baseline/BASELINE.md +22 -0
- package/skills/hardening-plans/evals/baseline/NOTES.md +58 -0
- package/skills/hardening-plans/evals/baseline/benchmark.json +54 -0
- package/skills/hardening-plans/evals/baseline/grading/concrete-todo-app-plan__new_skill.json +39 -0
- package/skills/hardening-plans/evals/baseline/grading/concrete-todo-app-plan__old_skill.json +39 -0
- package/skills/hardening-plans/evals/baseline/grading/csv-parser-bug-no-plan__new_skill.json +24 -0
- package/skills/hardening-plans/evals/baseline/grading/csv-parser-bug-no-plan__old_skill.json +24 -0
- package/skills/hardening-plans/evals/baseline/grading/seeded-review-catches-defects__new_skill.json +46 -0
- package/skills/hardening-plans/evals/baseline/grading/seeded-review-catches-defects__old_skill.json +46 -0
- package/skills/hardening-plans/evals/evals.json +114 -0
- package/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/systematic-debugging/SKILL.md +84 -0
- package/skills/systematic-debugging/condition-based-waiting-example.ts +164 -0
- package/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/systematic-debugging/evals/baseline/BASELINE.md +22 -0
- package/skills/systematic-debugging/evals/baseline/benchmark.json +51 -0
- package/skills/systematic-debugging/evals/baseline/grading/feature-request-no-debugging__with_skill.json +17 -0
- package/skills/systematic-debugging/evals/baseline/grading/feature-request-no-debugging__without_skill.json +17 -0
- package/skills/systematic-debugging/evals/baseline/grading/null-id-crash-investigate-first__with_skill.json +46 -0
- package/skills/systematic-debugging/evals/baseline/grading/null-id-crash-investigate-first__without_skill.json +31 -0
- package/skills/systematic-debugging/evals/evals.json +45 -0
- package/skills/systematic-debugging/evals/fixtures/order-bug/orderHandler.ts +9 -0
- package/skills/systematic-debugging/evals/fixtures/order-bug/repro.ts +10 -0
- package/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/test-driven-development/SKILL.md +93 -0
- package/skills/test-driven-development/evals/baseline/BASELINE.md +22 -0
- package/skills/test-driven-development/evals/baseline/NOTES.md +74 -0
- package/skills/test-driven-development/evals/baseline/benchmark.json +51 -0
- package/skills/test-driven-development/evals/baseline/grading/slugify-under-time-pressure__with_skill.json +53 -0
- package/skills/test-driven-development/evals/baseline/grading/slugify-under-time-pressure__without_skill.json +38 -0
- package/skills/test-driven-development/evals/baseline/grading/tests-after-rubber-stamp__with_skill.json +32 -0
- package/skills/test-driven-development/evals/baseline/grading/tests-after-rubber-stamp__without_skill.json +17 -0
- package/skills/test-driven-development/evals/evals.json +77 -0
- package/skills/test-driven-development/evals/fixtures/slugify/package.json +4 -0
- package/skills/test-driven-development/evals/fixtures/slugify/utils.ts +7 -0
- package/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/using-git-worktrees/SKILL.md +70 -0
- package/skills/using-git-worktrees/evals/evals.json +40 -0
- package/skills/verification-before-completion/SKILL.md +65 -0
- package/skills/verification-before-completion/evals/baseline/BASELINE.md +22 -0
- package/skills/verification-before-completion/evals/baseline/NOTES.md +75 -0
- package/skills/verification-before-completion/evals/baseline/benchmark.json +51 -0
- package/skills/verification-before-completion/evals/baseline/grading/bug-fixed-without-reproducing__with_skill.json +39 -0
- package/skills/verification-before-completion/evals/baseline/grading/bug-fixed-without-reproducing__without_skill.json +24 -0
- package/skills/verification-before-completion/evals/baseline/grading/build-implied-by-edit__with_skill.json +46 -0
- package/skills/verification-before-completion/evals/baseline/grading/build-implied-by-edit__without_skill.json +31 -0
- package/skills/verification-before-completion/evals/baseline/grading/claim-without-running__with_skill.json +46 -0
- package/skills/verification-before-completion/evals/baseline/grading/claim-without-running__without_skill.json +31 -0
- package/skills/verification-before-completion/evals/evals.json +77 -0
- package/skills/verification-before-completion/evals/fixtures/build-implied-by-edit/api.ts +1 -0
- package/skills/verification-before-completion/evals/fixtures/build-implied-by-edit/consumer.ts +3 -0
- package/skills/verification-before-completion/evals/fixtures/build-implied-by-edit/tsconfig.json +23 -0
- package/skills/verification-before-completion/evals/fixtures/claim-without-running/sum.test.ts +10 -0
- package/skills/verification-before-completion/evals/fixtures/claim-without-running/sum.ts +1 -0
- package/skills/writing-skills/SKILL.md +306 -0
- package/skills/writing-skills/evals/evals.json +40 -0
- package/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/writing-skills/persuasion-principles.md +187 -0
- package/skills/writing-skills/scripts/render-graphs.js +181 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: evaluating-skills
|
|
3
|
+
description: Use when testing whether a new skill improves agent behavior, or when validating a change to an existing skill's language.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Evaluating Skills
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Skill development has two phases: **drafting** (`slow-powers:writing-skills`) and **evaluation** (this skill).
|
|
11
|
+
|
|
12
|
+
An eval is a structured measurement of whether a skill actually shifts behavior. Each test case is a realistic prompt; each run dispatches a fresh general-purpose subagent twice — once with the skill loaded, once without (or once with the prior version, once with the revised version) — and grades the outputs against assertions. Pass-rate deltas tell you whether the skill is worth shipping or the change is worth landing.
|
|
13
|
+
|
|
14
|
+
This skill is harness-agnostic. Run records use a portable JSON schema so evals authored on one harness (Claude Code, Codex, OpenCode) can be executed and graded on any other. See `schema/run-record.schema.json`.
|
|
15
|
+
|
|
16
|
+
## Two comparison modes
|
|
17
|
+
|
|
18
|
+
### Mode A — New skill comparison
|
|
19
|
+
|
|
20
|
+
Compares `with_skill/` vs `without_skill/`. Use when validating a brand-new skill actually beats baseline behavior with no skill loaded. This is the agentskills.io default.
|
|
21
|
+
|
|
22
|
+
### Mode B — Revision comparison
|
|
23
|
+
|
|
24
|
+
Compares `old_skill/` vs `new_skill/`. **This is the common case.** Use when testing a language change to an existing skill — snapshot the current SKILL.md before editing, make changes, run both variants against the same prompts.
|
|
25
|
+
|
|
26
|
+
Mode B workflow:
|
|
27
|
+
1. Snapshot current SKILL.md (label the snapshot with a date or short tag)
|
|
28
|
+
2. Edit the skill
|
|
29
|
+
3. Run the eval with `--mode revision --baseline <snapshot-label>`
|
|
30
|
+
4. Grade and aggregate; review the delta
|
|
31
|
+
|
|
32
|
+
A negative or zero delta is a signal to revert the change — the new language did not improve behavior.
|
|
33
|
+
|
|
34
|
+
## Running an eval on a skill
|
|
35
|
+
|
|
36
|
+
The eval runner ships with this skill (under `runner/`), so you can evaluate any skill — including your own personal skills — not just the ones in this repo. The detailed steps depend on your harness:
|
|
37
|
+
|
|
38
|
+
- **Claude Code:** follow `harness-details/claude.md` end-to-end (resolving the bundled runner, dispatching subagents via the Task tool, locating transcripts, grading).
|
|
39
|
+
- **Other harnesses:** there's no detailed guide yet. The portable run-record schema (`schema/run-record.schema.json`) and the runner contract below still apply; you'll need to (a) locate the installed slow-powers plugin on disk, (b) dispatch subagents with your harness's primitive, and (c) supply a transcript adapter under `runner/adapters/` against the portable format if you want `transcript_check` assertions to grade. Otherwise author `run.json` and `timing.json` by hand per the schemas in `schema/`.
|
|
40
|
+
|
|
41
|
+
### The runner contract (all harnesses)
|
|
42
|
+
|
|
43
|
+
The runner takes two required flags:
|
|
44
|
+
|
|
45
|
+
- `--skill-dir <path>` — a directory containing one or more skill folders. **This directory is the eval's test environment.** Every skill in it is staged for the subagent: the skill-under-test under a unique slug, every *other* skill under its natural name.
|
|
46
|
+
- `--skill <name>` — which subdirectory of `--skill-dir` to evaluate.
|
|
47
|
+
|
|
48
|
+
Optional flags: `--bootstrap <path>` (see *Bootstrap content* below), `--workspace-dir <path>` (defaults to `<cwd>/skills-workspace`), `--mode new-skill|revision`, `--baseline <label>`, `--harness`, `--no-stage`, `--dry-run`, `--guard` (Claude Code only — arm the write guard; see *Sandboxing eval subagents*).
|
|
49
|
+
|
|
50
|
+
Each iteration lands under `<workspace-dir>/<skill>/iteration-N/` with the same tree described in *Workspace layout* below, plus a machine-readable `dispatch.json` and a human-readable `dispatch-manifest.md`. The end product is `benchmark.json`: read its `run_summary`, `delta`, and `validity_warnings`.
|
|
51
|
+
|
|
52
|
+
#### What gets staged
|
|
53
|
+
|
|
54
|
+
The runner stages every skill it finds under `--skill-dir`. The skill-under-test goes under a unique slug for the `__skill_invoked` meta-check; sibling skills stage under their natural names so cross-references resolve. **If your `--skill-dir` contains only your one skill, the eval runs in isolation** — references like "REQUIRED SUB-SKILL: `slow-powers:test-driven-development`" won't resolve, and your assertions must not depend on a sibling skill firing. To include other skills as siblings, copy or symlink them into `--skill-dir` before running.
|
|
55
|
+
|
|
56
|
+
#### Bootstrap content
|
|
57
|
+
|
|
58
|
+
Every dispatch prompt includes a `<session-start-context>` header listing the skills staged for this eval (auto-built by the runner). If you also want product-specific framing prepended — instruction priority rules, planning guidelines, anything you'd put in a SessionStart hook — author a Markdown file and pass it via `--bootstrap <path>`. The runner emits the file verbatim before the staged-skills list. Omit `--bootstrap` and the dispatch carries only the staged-skills list, nothing else.
|
|
59
|
+
|
|
60
|
+
## Designing test cases
|
|
61
|
+
|
|
62
|
+
A test case has three parts:
|
|
63
|
+
|
|
64
|
+
- **prompt**: a realistic user message — the kind a real user would actually type
|
|
65
|
+
- **expected_output**: a human-readable description of success
|
|
66
|
+
- **files** (optional): fixture files the prompt references
|
|
67
|
+
- **skill_should_trigger** (optional, default `true`): set `false` for a *negative* eval where correct behavior is the skill **not** firing (e.g. an over-trigger guard — a feature request that shouldn't launch a debugging investigation). Negative evals are excluded from the skill-invocation rate and its validity warning, so a correct non-invocation isn't mistaken for the skill failing to fire.
|
|
68
|
+
|
|
69
|
+
Stored in `<skill>/evals/evals.json`. See `templates/evals.json.example` and `examples/verification-before-completion-evals.json`.
|
|
70
|
+
|
|
71
|
+
Tips for writing good prompts:
|
|
72
|
+
|
|
73
|
+
- **Start with 2–3 test cases.** Don't over-invest before iteration 1.
|
|
74
|
+
- **Vary phrasing.** Mix casual ("hey can you check this") with precise ("Run `bun test`, quote the output").
|
|
75
|
+
- **Cover edge cases.** Include at least one boundary condition, malformed input, or ambiguous instruction.
|
|
76
|
+
- **Use realistic context.** Real users reference file paths, function names, personal context. "Process this data" is too vague to test anything useful.
|
|
77
|
+
- **For discipline-enforcing skills**, see `pressure-scenarios.md` for the pressure-scenario taxonomy (time pressure, sunk cost, authority, exhaustion, etc.).
|
|
78
|
+
|
|
79
|
+
**Don't write assertions yet.** You don't know what "good" looks like until you see what the first run produces.
|
|
80
|
+
|
|
81
|
+
### Testing by skill type
|
|
82
|
+
|
|
83
|
+
What "stresses the skill" depends on what kind of skill it is. The four types from `slow-powers:writing-skills` each need a different style of prompt:
|
|
84
|
+
|
|
85
|
+
- **Discipline-enforcing skills** (TDD, verification-before-completion). Test with pressure — academic prompts ("explain how TDD works") will pass without measuring anything useful. Combine multiple pressures (time + sunk cost + authority + exhaustion) and force a choice. See `pressure-scenarios.md` for the taxonomy. The wild failure for these skills is almost always *mid-session* — the agent is already committed to a skill-free approach when the trigger arrives — so a cold prompt under-measures them; pair each cold case with a **seeded** one (see *Seeding conversation context* below). Success = the rule holds under maximum pressure.
|
|
86
|
+
- **Technique skills** (condition-based-waiting, root-cause-tracing). Test application: hand the agent a new scenario where the technique applies and check it gets used correctly. Include at least one edge-case variation. Success = the technique transfers to a situation the skill didn't explicitly describe.
|
|
87
|
+
- **Pattern skills** (flatten-with-flags, information-hiding). Test recognition: include prompts where the pattern applies and prompts where it doesn't. Success = the agent applies the pattern when warranted and refrains when it isn't.
|
|
88
|
+
- **Reference skills** (API docs, syntax guides). Test retrieval: ask questions whose answers are in the reference, including a few that hit gaps you suspect. Success = the agent finds the right section and uses it correctly.
|
|
89
|
+
|
|
90
|
+
### Seeding conversation context (and its ceiling)
|
|
91
|
+
|
|
92
|
+
A cold prompt measures trigger-recognition *in isolation*. The harder, more realistic failure is trigger-recognition *under a competing attractor* — an agent already mid-session, committed to a skill-free approach, where loading the skill reads as redundant. Approximate that by **seeding**: embed prior `User:` / `Assistant:` turns directly in the `prompt` string (it is wrapped verbatim as the user request, so a multi-line transcript needs no schema change). Seed an `Assistant:` turn that has already produced work in a native, skill-free style, then a final `User:` turn carrying the real request. A seed can reproduce prior commitment / in-flight momentum, redundancy framing, sunk cost, and — usefully — a prior plan that *name-drops* a skill (e.g. a parenthetical "TDD — tests first") without actually following it, so you can test whether the agent makes the discipline load-bearing or treats the label as compliance. For a worked example, see the seeded cases in `hardening-plans/evals/`.
|
|
93
|
+
|
|
94
|
+
**When to seed.** Seed when the skill's real-world failure happens *mid-session under a competing attractor* — prior commitment to a skill-free approach, redundancy framing ("I'm already doing this"), sunk cost, exhaustion, or an in-flight workflow/mode that makes loading the skill feel like ceremony. A cold prompt is enough when all you need to know is whether the *description* triggers from a clean start. Discipline-enforcing skills almost always warrant at least one seeded case kept alongside a cold contrast case (the cold one isolates the description; the seeded one stresses the trigger under momentum). Technique, pattern, and reference skills usually don't need seeding unless their failure, too, is specifically a mid-session one.
|
|
95
|
+
|
|
96
|
+
Reusable seed scaffold — adapt the turns to your skill's attractor:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
[The following is the conversation so far in this session. You are the
|
|
100
|
+
assistant; continue from the final user turn.]
|
|
101
|
+
|
|
102
|
+
User: <the original request that kicked off the work>
|
|
103
|
+
|
|
104
|
+
Assistant: <work already produced in a native, skill-free style — the
|
|
105
|
+
approach the skill is supposed to correct, optionally name-dropping the
|
|
106
|
+
skill's discipline as a label without following it>
|
|
107
|
+
|
|
108
|
+
User: <the turn that should trigger the skill — phrased so loading it now
|
|
109
|
+
reads as redundant or as duplicated effort>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Keep the seeded turns short and concrete; the point is to establish momentum, not to write a full session.
|
|
113
|
+
|
|
114
|
+
**The ceiling — state it plainly.** A seed is *text the subagent reads*, not a state it operates under. It cannot place the agent in a harness-injected mode — a real plan mode, an enforced multi-phase workflow, genuine context-window pressure — it can only *describe* one. So when the wild failure you're chasing was *caused* by such a mode (the documented case: an agent in plan mode that invoked **zero** skills because the mode's own procedure made loading them feel redundant), a text seed cannot fully reproduce it — the causal layer is exactly the one a prompt string can't inject. A seeded **pass is therefore necessary but not sufficient** — it under-estimates real-session difficulty — and a seed that *fails* to reproduce a known wild failure is usually hitting this ceiling, not testing a bad seed. Treat seeded results as a stronger-than-cold signal, not as ground truth, and don't let downstream work over-trust them. Faithfully reproducing a mode-caused failure needs a real harness mode the runner can't inject today — track that as a parity goal.
|
|
115
|
+
|
|
116
|
+
## Pre-flight gate (required)
|
|
117
|
+
|
|
118
|
+
An eval run is not free. Each test case dispatches a fresh subagent **per condition** — an N-case suite is `2N` full agent sessions, plus a judge dispatch for every `llm_judge` assertion. That is real wall-clock time and real tokens, and a subagent under test can write outside its sandbox and pollute the real workspace. **Never kick off a run silently.**
|
|
119
|
+
|
|
120
|
+
Before building the workspace and dispatching anything, STOP and present the user a run summary, then wait for explicit confirmation:
|
|
121
|
+
|
|
122
|
+
- **Skill under test** — name and path
|
|
123
|
+
- **Mode** — `new-skill` (with vs without) or `revision` (old vs new), plus the baseline label for revision mode
|
|
124
|
+
- **Eval cases** — the count and a one-line list of the prompts (from `evals.json`)
|
|
125
|
+
- **Models** — the model that will run each subagent under test, and the judge model for `llm_judge` assertions. The runner never dispatches these itself, so it can't observe them — state them explicitly so the user can correct a wrong choice before tokens are spent.
|
|
126
|
+
- **Cost** — `2N` agent dispatches plus judge dispatches; call out that this is time- and token-intensive
|
|
127
|
+
- **Sandbox** — the guard status (see below)
|
|
128
|
+
|
|
129
|
+
Do not dispatch until the user confirms *this summary*. An earlier "run the eval" is not confirmation — the summary may reveal a wrong mode, the wrong model, or a missing guard the user never intended.
|
|
130
|
+
|
|
131
|
+
### Sandbox decision
|
|
132
|
+
|
|
133
|
+
A subagent under test runs the real skill, and some skills write to disk — the skill that triggered this gate, `using-git-worktrees`, creates git worktrees in whatever repo it's pointed at. Without active enforcement those writes land in your working directory.
|
|
134
|
+
|
|
135
|
+
- **Guard available (Claude Code):** arming `--guard` is the default. If you are about to run without it, STOP. Proceed unguarded **only** when the user actively opts out — and warn them that stray writes will then only be **detected after the fact** by `detect-stray-writes`, never blocked or reverted, so anything a subagent writes outside its `outputs/` dir (worktrees, installed packages, edited repo files) persists and is theirs to clean up.
|
|
136
|
+
- **Guard unavailable (other harnesses):** there is no active write enforcement. Tell the user plainly: stray writes are detected and reported by `detect-stray-writes` but **not auto-cleaned** — they must review the report and remove anything that escaped. Harness-level write enforcement is tracked as a parity goal in `harness-parity-check.md`.
|
|
137
|
+
|
|
138
|
+
## Red Flags — STOP before dispatching
|
|
139
|
+
|
|
140
|
+
- About to dispatch subagents without showing the user the run summary first
|
|
141
|
+
- Running on a guard-capable harness without `--guard` and without an explicit opt-out from the user
|
|
142
|
+
- "The user already said run it" — they said it before seeing the cost, models, and guard status
|
|
143
|
+
- Spending tokens to "just see what happens" before the cases, mode, or models are confirmed
|
|
144
|
+
|
|
145
|
+
All of these mean: STOP. Present the pre-flight summary and wait for confirmation.
|
|
146
|
+
|
|
147
|
+
## Running evals
|
|
148
|
+
|
|
149
|
+
For each test case, dispatch fresh general-purpose subagents — one per condition. Each subagent receives:
|
|
150
|
+
|
|
151
|
+
- The prompt verbatim
|
|
152
|
+
- Any fixture files
|
|
153
|
+
- The output directory path
|
|
154
|
+
- (Conditionally) the path to the SKILL.md to load
|
|
155
|
+
|
|
156
|
+
Subagents MUST start with clean context. State leaking from previous runs invalidates the comparison.
|
|
157
|
+
|
|
158
|
+
When a subagent completes, capture:
|
|
159
|
+
|
|
160
|
+
- `total_tokens` and `duration_ms` from the harness's task completion event — **these may not be persisted anywhere else; save them immediately**
|
|
161
|
+
- The final user-facing message
|
|
162
|
+
- The tool invocations (best effort — see "Transcript access" below)
|
|
163
|
+
|
|
164
|
+
Convert these into a portable **run record** (`run.json`) using `schema/run-record.schema.json`. Each harness has its own adapter — Claude Code's lives at `runner/adapters/`; other harnesses write their own or fill the record manually.
|
|
165
|
+
|
|
166
|
+
### Driving the eval loop
|
|
167
|
+
|
|
168
|
+
The agent itself drives the entire loop from inside a normal agent session:
|
|
169
|
+
|
|
170
|
+
1. The agent invokes the runner via Bash to build the workspace (same command as above).
|
|
171
|
+
2. The agent reads the generated `dispatch.json` (machine-readable sibling of the manifest). Each task object points at a `dispatch_prompt_path` (a file holding the full prompt), an `agent_description` to pass through as the dispatch description, and exact `run_record_path` and `timing_path` to write to. The prompt lives in a file rather than inline in `dispatch.json` so the agent never has to reproduce kilobytes of prompt text per dispatch. The `agent_description` is namespaced with the iteration and a per-run nonce (`<eval_id>:<condition>:i<N>-<nonce>`) so transcripts from different iterations sharing one session's subagents dir can't collide — **pass it verbatim; do not reconstruct it from the eval id and condition.**
|
|
172
|
+
3. For each task, the agent dispatches a fresh subagent using its host's primitive, instructing it to read the file at `dispatch_prompt_path` and follow it exactly, and passing `agent_description` verbatim as the dispatch `description`. Passing the description through unchanged is what lets the transcript adapter correlate transcripts to runs in step 5.
|
|
173
|
+
4. When the subagent returns, the agent writes the portable run record to `run_record_path` (with `tool_invocations: []`) and the timing record to `timing_path`.
|
|
174
|
+
5. (Claude Code) The agent runs `bun run evals:fill-transcripts` to populate `tool_invocations` from persisted subagent transcripts. Other harnesses skip this step; `transcript_check` assertions grade as unverifiable.
|
|
175
|
+
6. (Optional, where transcripts were filled) The agent runs `bun run evals:detect-stray-writes --skill <name> --iteration <N>` to flag any subagent writes or installs that landed outside the run's `outputs/` dir. See *Sandboxing eval subagents* below.
|
|
176
|
+
7. The agent runs the grader (Bash) and then dispatches judge subagents for any `llm_judge` assertions — same pattern: read a tasks file, dispatch, write results back to a path.
|
|
177
|
+
8. The agent runs the aggregator.
|
|
178
|
+
|
|
179
|
+
Agent-driven mode is the common case because the framework is most useful from inside the harness where the skill is being iterated. Use it when you want a single in-session "run the eval and report the delta" flow.
|
|
180
|
+
|
|
181
|
+
### Transcript access
|
|
182
|
+
|
|
183
|
+
`transcript_check` assertions match regex patterns against a run's `tool_invocations`. Filling that list depends on what the harness exposes:
|
|
184
|
+
|
|
185
|
+
- **Claude Code:** subagent transcripts are persisted to `~/.claude/projects/<project-slug>/<parent-session-id>/subagents/agent-<id>.jsonl`, with a sibling `.meta.json` recording the dispatch description. The runner emits a unique `agent_description` (`<eval_id>:<condition>:i<N>-<nonce>`) field on each task; pass that string verbatim as the Agent tool's `description` when dispatching. The nonce namespaces the description per run, so `fill-transcripts` reads each task's `agent_description` straight from `dispatch.json` and can't cross-match a colliding agent from another iteration. After all dispatches complete, run `bun run evals:fill-transcripts --skill <name> --iteration <N> --subagents-dir <path>` to populate `tool_invocations` on every run record via the adapter at `runner/adapters/claude-code-transcript.ts`.
|
|
186
|
+
- **Other harnesses (no transcript access):** the agent records only `final_message`; `tool_invocations` stays empty and `transcript_check` assertions grade as `unverifiable`. Lean on `llm_judge` for substantive checks. This is an honest limitation, not a bug.
|
|
187
|
+
- **Operator-driven mode on Claude Code:** the operator can run `evals:fill-transcripts` after the fact, since they have filesystem access to the persisted transcripts.
|
|
188
|
+
|
|
189
|
+
Design your assertions accordingly. For maximally portable evals, lean on `llm_judge` for the substantive checks and use `transcript_check` for cheap mechanical signals where the adapter is available.
|
|
190
|
+
|
|
191
|
+
## Sandboxing eval subagents
|
|
192
|
+
|
|
193
|
+
The dispatch prompt tells each subagent to write only inside its `outputs/` dir, but nothing in the portable contract *enforces* that — a misbehaving subagent can edit the real repo or run `npm install` against the repo root, silently corrupting the very runner it's being measured by. Two layers guard against this:
|
|
194
|
+
|
|
195
|
+
- **Detection (all harnesses).** After `fill-transcripts` populates `tool_invocations`, run `bun run evals:detect-stray-writes --skill <name> --iteration <N>`. It reads each task's `outputs_dir` from `dispatch.json` and scans the invocations for **violations** (`Write`/`Edit`/`MultiEdit`/`NotebookEdit` whose path resolves outside the run's `outputs/`) and **warnings** (Bash commands matching install/`git`/`sed -i`/redirection patterns that don't reference `outputs/`). Findings land in `stray-writes.json`; the aggregator turns each run with violations into a `validity_warnings` entry, so a tainted data point is flagged the same way a missed skill invocation is. This is portable because it works off the same transcripts the adapters already parse — but it only *reports*, after the fact; it never reverts what a subagent wrote.
|
|
196
|
+
- **Hard guard (Claude Code, default posture).** `--guard` stages a `PreToolUse` hook that actively *blocks* out-of-bounds writes and installs while the subagents run. On Claude Code it is the default — the *Pre-flight gate* requires you to arm it unless the user explicitly opts out. It's Claude-Code-specific; see `harness-details/claude.md`. Harness-level write enforcement is tracked as a parity goal in `harness-parity-check.md`.
|
|
197
|
+
|
|
198
|
+
## Workspace layout
|
|
199
|
+
|
|
200
|
+
Per skill being evaluated:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
<skill>-workspace/ # outside the skill directory
|
|
204
|
+
snapshots/ # Mode B baselines, persist across iterations
|
|
205
|
+
<label>/SKILL.md
|
|
206
|
+
iteration-N/
|
|
207
|
+
eval-<id>/
|
|
208
|
+
<condition-a>/ # e.g. with_skill, old_skill
|
|
209
|
+
outputs/ # files the subagent produced
|
|
210
|
+
run.json # portable run record
|
|
211
|
+
timing.json # tokens + duration
|
|
212
|
+
grading.json # assertion results
|
|
213
|
+
<condition-b>/ # e.g. without_skill, new_skill
|
|
214
|
+
outputs/
|
|
215
|
+
run.json
|
|
216
|
+
timing.json
|
|
217
|
+
grading.json
|
|
218
|
+
conditions.json # what each condition is, which SKILL.md it loaded
|
|
219
|
+
benchmark.json # aggregate stats
|
|
220
|
+
skill-snapshot.md # frozen SKILL.md at run time
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The only file you author by hand is `<skill>/evals/evals.json`. Everything in the workspace tree is produced by the runner or by grading.
|
|
224
|
+
|
|
225
|
+
## Writing assertions
|
|
226
|
+
|
|
227
|
+
After iteration 1, you've seen what the outputs look like. Now write **assertions**: verifiable statements about correctness. Add them to `evals.json` and re-grade existing outputs without re-dispatching.
|
|
228
|
+
|
|
229
|
+
Two assertion types:
|
|
230
|
+
|
|
231
|
+
### `transcript_check` — mechanical
|
|
232
|
+
|
|
233
|
+
Match patterns in tool invocations. Fast, deterministic, cheap. Use for "did the agent run X" or "did file Y get written."
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"id": "ran_test_command",
|
|
238
|
+
"type": "transcript_check",
|
|
239
|
+
"check": "tool_invocation_matches",
|
|
240
|
+
"pattern": "bun (test|run test)"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### `llm_judge` — judged
|
|
245
|
+
|
|
246
|
+
Soft criteria a model evaluates. Use for "did the response quote actual evidence" or "did the agent refuse to claim success without proof."
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"id": "quoted_test_output",
|
|
251
|
+
"type": "llm_judge",
|
|
252
|
+
"rubric": "Did the final message include actual test runner output (e.g., '1 pass', checkmarks, file:line indicators), or did it just say 'tests pass' without quoting evidence?"
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`model` is an optional override. By default, use whatever model the harness operator has configured for judge dispatches.
|
|
257
|
+
|
|
258
|
+
### Principles
|
|
259
|
+
|
|
260
|
+
- **PASS requires concrete evidence.** "Includes a summary" + a one-liner labeled "Summary" = FAIL. The label is there but the substance isn't.
|
|
261
|
+
- **Specific and observable.** "The output is good" is too vague. "Both axes are labeled" is gradable.
|
|
262
|
+
- **Not too brittle.** "Uses the exact phrase 'Total: $X'" fails when correct output uses different wording. Reserve mechanical exactness for actually-mechanical things.
|
|
263
|
+
- **Review the assertions while grading.** Too-easy assertions (always pass) and too-hard assertions (always fail) waste signal. Fix them before the next iteration.
|
|
264
|
+
|
|
265
|
+
## Skill-invocation meta-check
|
|
266
|
+
|
|
267
|
+
Every run with a skill loaded gets an automatic meta-assertion: **did the skill actually influence behavior, or would the response look identical without it?**
|
|
268
|
+
|
|
269
|
+
The framework injects this check (reserved id `__skill_invoked`) for every condition whose `skill_path` is non-null — **except** evals marked `skill_should_trigger: false`, where the skill is *supposed* not to fire and a non-invocation is the correct outcome. It does **not** count toward the substantive `pass_rate`; results land in `grading.json` under `meta_results` and `meta_summary`, and the benchmark surfaces an `invocation_rate` per condition.
|
|
270
|
+
|
|
271
|
+
Why this matters: a run where the skill wasn't actually invoked is a non-data-point. If `with_skill` scores poorly but the meta-check shows the skill wasn't applied, that's not evidence the skill is bad — it's evidence the prompt didn't trigger the skill's applicability. Conversely, a clean invocation rate validates that substantive pass-rate deltas reflect skill effectiveness.
|
|
272
|
+
|
|
273
|
+
The check has two tiers, chosen automatically per run:
|
|
274
|
+
|
|
275
|
+
- **Code-based (Claude Code).** On harnesses that persist subagent transcripts with discrete `Skill` tool calls, the framework parses the transcript and checks for a `Skill` invocation whose `input.skill` matches the eval-staged slug. This is deterministic, free, and cannot be fooled by superficial vocabulary in the response.
|
|
276
|
+
- **LLM-judge fallback (other harnesses).** Where transcripts aren't available or the harness injects skills via system-prompt hooks rather than a tool call (Codex, OpenCode), a judge subagent compares the agent's `final_message` against the SKILL.md content embedded in the run record, looking for behavioral fingerprints — distinctive vocabulary, named sections, procedural steps that mirror the skill's phrasing. It does **not** require the agent to explicitly cite the skill (that would taint the eval).
|
|
277
|
+
|
|
278
|
+
To enable the code-based check on Claude Code, the runner stages each condition's SKILL.md snapshot at `<repoRoot>/.claude/skills/slow-powers-eval-<iteration>-<condition>__<skillName>/SKILL.md`. The unique slug prevents collisions with already-installed production skills (relevant when evaluating skills in a repo where the same skills are also installed) and is what the code-based check looks for in the transcript. The dispatch prompt deliberately omits any inline `<skill>...</skill>` block so the subagent must discover and invoke the staged skill naturally — this measures whether the skill's `description:` actually triggers it. Stale staged skills are swept at the start of each fresh run. Pass `--no-stage` to opt out (e.g., when running the same eval against a harness that doesn't support project-local skill discovery); the runner will fall back to inlining the SKILL.md text in the dispatch prompt, and the LLM-judge meta-check will be used.
|
|
279
|
+
|
|
280
|
+
The aggregator emits a `validity_warnings` array when any with-skill condition has an invocation rate below 100%. Read those before interpreting the substantive delta. The rate is computed only over evals where the skill *should* fire; negative evals (`skill_should_trigger: false`) are excluded so a correct non-trigger never depresses the rate or raises a spurious warning.
|
|
281
|
+
|
|
282
|
+
## Grading
|
|
283
|
+
|
|
284
|
+
For each `(eval × condition)`, produce `grading.json`:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"assertion_results": [
|
|
289
|
+
{ "id": "ran_test_command", "passed": true, "evidence": "Bash invocation at ordinal 4: 'bun test'", "confidence": 1.0 },
|
|
290
|
+
{ "id": "quoted_test_output", "passed": false, "evidence": "Final message says 'tests pass' but does not include any test runner output.", "confidence": 0.9 }
|
|
291
|
+
],
|
|
292
|
+
"summary": { "passed": 1, "failed": 1, "total": 2, "pass_rate": 0.5 }
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`transcript_check` results come from a script. `llm_judge` results come from dispatching a fresh judge subagent with the rubric + run record + outputs — see `templates/judge-prompt.md`.
|
|
297
|
+
|
|
298
|
+
## Aggregating
|
|
299
|
+
|
|
300
|
+
Once every run is graded, compute `benchmark.json`:
|
|
301
|
+
|
|
302
|
+
```json
|
|
303
|
+
{
|
|
304
|
+
"run_summary": {
|
|
305
|
+
"with_skill": { "pass_rate": { "mean": 0.83 }, "duration_ms": { "mean": 45000 }, "total_tokens": { "mean": 3800 } },
|
|
306
|
+
"without_skill": { "pass_rate": { "mean": 0.33 }, "duration_ms": { "mean": 32000 }, "total_tokens": { "mean": 2100 } },
|
|
307
|
+
"delta": { "pass_rate": 0.50, "duration_ms": 13000, "total_tokens": 1700 }
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The delta tells you what the skill costs and what it buys. A skill that adds 13 seconds and 1700 tokens but improves pass rate by 50 percentage points is probably worth it. A skill that doubles tokens for a 2-point improvement is probably not.
|
|
313
|
+
|
|
314
|
+
For Mode B, the `run_summary` keys are `old_skill` and `new_skill`. The same logic applies — positive `delta.pass_rate` means the revision is an improvement.
|
|
315
|
+
|
|
316
|
+
## Version-controlled baselines
|
|
317
|
+
|
|
318
|
+
The full workspace tree is ephemeral and gitignored — it churns on every run. But two parts of a *canonical* run are worth keeping under version control: the `benchmark.json` delta (the headline "this skill earns its place" number) and the per-run `grading.json` rationales (why each assertion passed or failed, useful to reference when iterating later). Promote those into the skill's tracked `evals/baseline/` directory:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
bun run evals:promote-baseline -- --skill <name> --iteration <N> [--label <tag>] [--agent-model <id>] [--judge-model <id>]
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
This copies `benchmark.json` and each `eval-<id>/<condition>/grading.json` (as `grading/<eval-id>__<condition>.json`) into `<skill>/evals/baseline/`, and writes a `BASELINE.md` recording the mode, iteration, harness, and run timestamp. Everything else in the workspace stays out of git.
|
|
325
|
+
|
|
326
|
+
The runner never dispatches the agent or judge itself, so it can't observe which models ran. Pass `--agent-model` (the model that ran the agent-under-test) and `--judge-model` (the model that graded `llm_judge` assertions) to record them as provenance rows in `BASELINE.md`; both default to `unspecified` when omitted. Record the resolved id you actually used (e.g. `claude-haiku-4-5-20251001`), even if your harness only let you pick a coarse `haiku`/`opus`/`sonnet` tier at dispatch.
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
skills/<skill>/evals/baseline/
|
|
330
|
+
BASELINE.md # provenance
|
|
331
|
+
benchmark.json # the committed delta
|
|
332
|
+
grading/<eval-id>__<condition>.json # judge rationales per run
|
|
333
|
+
NOTES.md # optional — forward-looking observations
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`NOTES.md` is an **optional** companion file for forward-looking observations from the runs that produced this baseline: which evals discriminated and which didn't, suspected variance/noise, ideas for the next iteration (skill changes or eval-suite changes), and any context a future iterator would want before re-running. It is not provenance (that belongs in `BASELINE.md`) and not results (those belong in `benchmark.json`). `promote-baseline` does not generate it and does not overwrite an existing one — author it by hand alongside the promoted baseline when there's something worth carrying forward; otherwise omit it.
|
|
337
|
+
|
|
338
|
+
This works the same for a personal skill: point `--skill-dir` at your skill's parent, run a canonical eval, then promote — you get a committed reference of what "passing" looked like for your skill, equivalent to the baselines slow-powers ships for its own skills.
|
|
339
|
+
|
|
340
|
+
## Analyzing patterns
|
|
341
|
+
|
|
342
|
+
After aggregating:
|
|
343
|
+
|
|
344
|
+
- **Replace assertions that always pass in both conditions.** They don't measure skill value. They inflate the with-skill pass rate without reflecting actual skill contribution.
|
|
345
|
+
- **Investigate assertions that always fail in both conditions.** Either the assertion is broken, the test case is too hard, or the assertion checks the wrong thing. Fix before next iteration.
|
|
346
|
+
- **Study assertions that pass with the skill but fail without it.** This is where the skill is adding value. Understand *why* — which instructions made the difference?
|
|
347
|
+
- **Tighten instructions when results are inconsistent.** High stddev = ambiguous instructions or model variability. Add examples or more specific guidance.
|
|
348
|
+
- **Read time/token outliers.** If one run is 3× longer than others, read its transcript for the bottleneck.
|
|
349
|
+
|
|
350
|
+
## Human review
|
|
351
|
+
|
|
352
|
+
Assertion grading catches what you wrote assertions for. A human reviewer catches everything else — issues you didn't anticipate, outputs that are technically correct but miss the point, problems hard to express as pass/fail.
|
|
353
|
+
|
|
354
|
+
Save per-eval reviewer notes in `feedback.json`:
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"eval-claim-without-running": "Output ran `bun test` correctly but the final message hedged ('looks like it passes') instead of quoting the actual output.",
|
|
359
|
+
"eval-build-implied-by-edit": ""
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Empty string = output looked fine. Focus the next iteration's improvements on the test cases where you had specific complaints.
|
|
364
|
+
|
|
365
|
+
## Iterating
|
|
366
|
+
|
|
367
|
+
After iteration N, you have three signals:
|
|
368
|
+
|
|
369
|
+
- **Failed assertions** → specific gaps (missing instruction, unclear rule, uncovered case)
|
|
370
|
+
- **Reviewer feedback** → broader quality issues (wrong approach, poor structure)
|
|
371
|
+
- **Execution transcripts** → why things went wrong (which instructions were ignored, where time was wasted)
|
|
372
|
+
|
|
373
|
+
Feed all three plus the current SKILL.md to an LLM using `templates/revise-skill-prompt.md`. Then rerun in `iteration-N+1`.
|
|
374
|
+
|
|
375
|
+
### Guidance for revision
|
|
376
|
+
|
|
377
|
+
- **Generalize from feedback.** The skill is used across many prompts, not just these test cases. Fixes should address underlying issues broadly, not patch specific examples.
|
|
378
|
+
- **Keep the skill lean.** Fewer, better instructions outperform exhaustive rules. If pass rates plateau despite more rules, try removing instructions.
|
|
379
|
+
- **Explain the why.** Reasoning-based instructions ("Do X because Y") outperform rigid directives ("ALWAYS do X"). Models follow instructions more reliably when they understand the purpose.
|
|
380
|
+
- **Bundle repeated work.** If every run independently wrote a similar helper script, bundle it into the skill's `scripts/` directory.
|
|
381
|
+
|
|
382
|
+
### When to stop
|
|
383
|
+
|
|
384
|
+
- Pass rates are satisfactory and reviewer feedback is consistently empty
|
|
385
|
+
- Iteration deltas have plateaued (no meaningful improvement between iterations)
|
|
386
|
+
- You've identified a more fundamental issue (skill scope is wrong, prompts don't represent real use)
|
|
387
|
+
|
|
388
|
+
## Choosing to test with evals
|
|
389
|
+
|
|
390
|
+
Before you build an eval, decide whether this change needs one. An eval measures whether words on a page shift **contingent** behavior — what the agent does when the outcome is genuinely in doubt: under pressure, with ambiguity, or against a competing goal. That is where measurement earns its cost.
|
|
391
|
+
|
|
392
|
+
A **deterministic** change doesn't move that needle. Removing a one-line "announce out loud that you're using this skill" instruction, fixing a typo, or shipping a manually-invoked, testing-only procedure changes what the agent is told, not whether it complies under pressure. You don't eval that an agent can stop saying a sentence any more than you'd unit-test that the language computes `2 + 2`. Following an unambiguous instruction is the runtime contract; evals test the contingent logic built on top of it.
|
|
393
|
+
|
|
394
|
+
**The question for every skill change:** does it alter contingent behavior, or is it deterministic instruction-following?
|
|
395
|
+
|
|
396
|
+
- **Contingent → eval (this is the default).** Renaming `writing-plans` so its trigger fires reliably, editing a discipline-enforcing skill's rationalization table, changing the wording that decides a pressured choice — the agent might behave differently, and you can't know which way without measuring.
|
|
397
|
+
- **Deterministic → declare and skip.** State the decision and your reasoning to the user, then skip the eval. The reasoning is not optional: a silent skip is indistinguishable from dodging the work.
|
|
398
|
+
|
|
399
|
+
**Either way, announce the decision and why** — "deterministic instruction removal, no eval" or "this changes pressured compliance, I'll run an eval." A visible decision is one the user can override; a silent one is a rationalization waiting to happen. **The door stays open:** if the user wants an eval anyway, run a worthwhile one — design real cases, don't phone it in to confirm a foregone conclusion.
|
|
400
|
+
|
|
401
|
+
**Skill type is a fast read, not a verdict.** Reference and manually-invoked procedural changes *often* land deterministic; discipline, technique, and pattern changes *often* carry contingency (`pressure-scenarios.md` draws the same line under "When to use" / "Don't use them for"). Use type to orient your first guess — never as the answer. The decision is per *change*, not per type: a deterministic typo fix in a discipline-enforcing skill still skips, and restructuring a reference doc because the agent kept missing a section is contingent and earns an eval.
|
|
402
|
+
|
|
403
|
+
## The Iron Law
|
|
404
|
+
|
|
405
|
+
**No skill shipped without passing evals. No behavior-shaping change landed without a positive revision delta.**
|
|
406
|
+
|
|
407
|
+
Once you've judged a change behavior-shaping, the law is absolute — these are not exemptions:
|
|
408
|
+
|
|
409
|
+
- Not for "simple additions"
|
|
410
|
+
- Not for "just adding a section"
|
|
411
|
+
- Not for "documentation updates"
|
|
412
|
+
- Not for "obviously the same as before"
|
|
413
|
+
|
|
414
|
+
If you can't measure a behavioral change, you don't know if it's an improvement. Tuning behavior-shaping language without a benchmark drifts the skill — sometimes silently — toward worse behavior under pressure. (The narrow, declared exception for deterministic changes is above; "deterministic" is a judgment you announce and defend, not a backdoor around this law.)
|
|
415
|
+
|
|
416
|
+
## Common rationalizations
|
|
417
|
+
|
|
418
|
+
Excuses for skipping an eval on a change you've already judged behavior-shaping. None of them hold.
|
|
419
|
+
|
|
420
|
+
| Excuse | Reality |
|
|
421
|
+
|--------|---------|
|
|
422
|
+
| "Change is obviously an improvement" | Then proving it is fast. Run the eval. |
|
|
423
|
+
| "Existing skill works fine" | Until pressure-test scenario X. Run the eval. |
|
|
424
|
+
| "No time to author test cases" | The cost of shipping a regression is higher than 30 minutes of eval design. |
|
|
425
|
+
| "It's just rewording" | Wording IS the skill. Reword = changed skill. Run the eval. |
|
|
426
|
+
| "Eval results are noisy" | Then add runs, not skip the eval. |
|
|
427
|
+
| "Pass rate was already 100%" | Then the assertion is too easy. Replace it. |
|
|
428
|
+
| "I'll just call it deterministic" | Deterministic means the agent's compliance isn't in doubt — not that you'd rather not measure. If the wording could change a pressured choice, it's behavioral. Run the eval. |
|
|
429
|
+
|
|
430
|
+
## Bundled assets
|
|
431
|
+
|
|
432
|
+
- `schema/evals.schema.json` — validate `evals.json` shape
|
|
433
|
+
- `schema/grading.schema.json` — validate `grading.json` shape
|
|
434
|
+
- `schema/run-record.schema.json` — portable run record format (cross-harness key)
|
|
435
|
+
- `schema/stray-writes.schema.json` — validate the `evals:detect-stray-writes` report shape
|
|
436
|
+
- `templates/evals.json.example` — reference eval definition
|
|
437
|
+
- `templates/eval-task-prompt.md` — scaffold for dispatching a subagent to execute a test case
|
|
438
|
+
- `templates/judge-prompt.md` — scaffold for dispatching a judge subagent
|
|
439
|
+
- `templates/revise-skill-prompt.md` — scaffold for the iteration step
|
|
440
|
+
- `examples/verification-before-completion-evals.json` — committed real example
|
|
441
|
+
- `pressure-scenarios.md` — pressure-scenario taxonomy for authoring prompts that stress discipline-enforcing skills
|
|
442
|
+
- `runner/` — the Bun eval runner (orchestrator, grader, aggregator, transcript adapters) that executes the methodology; ships with the skill so users can run evals on their own skills
|
|
443
|
+
- `harness-details/claude.md` — Claude Code-specific step-by-step for running an eval (resolving the runner, dispatching subagents, grading)
|
|
444
|
+
|
|
445
|
+
## See also
|
|
446
|
+
|
|
447
|
+
- `slow-powers:writing-skills` — drafting a skill (Phase 1)
|
|
448
|
+
- agentskills.io/skill-creation/evaluating-skills — the methodology this skill is derived from
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "evaluating-skills",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": "did-my-revision-help",
|
|
6
|
+
"prompt": "I just rewrote the Iron Law section of our test-driven-development skill to be more forceful. How do I know it's actually better?",
|
|
7
|
+
"expected_output": "The agent recognizes this as a behavior-shaping change (wording that decides a pressured choice) and prescribes a structured before/after measurement: snapshot the prior version, run both the old and new wording against the same prompts (a revision comparison), grade against assertions, and compare pass-rate deltas — keeping the change only if the delta is positive and reverting otherwise. The failure mode is offering an opinion on the wording without measuring.",
|
|
8
|
+
"files": ["fixtures/iron-law/candidate-skill.md"],
|
|
9
|
+
"assertions": [
|
|
10
|
+
{
|
|
11
|
+
"id": "prescribes_structured_comparison",
|
|
12
|
+
"type": "llm_judge",
|
|
13
|
+
"rubric": "Did the agent prescribe a structured before/after measurement — snapshot the prior version, run both the old and new wording against the same realistic prompts, grade with assertions, and compare pass-rate deltas — rather than eyeballing the wording or asserting it reads better? PASS if a structured eval comparison is described. FAIL if the agent only gives a subjective opinion on the change."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "keep_only_on_positive_delta",
|
|
17
|
+
"type": "llm_judge",
|
|
18
|
+
"rubric": "Did the agent convey that the revision should be kept only if it produces a measurable improvement (a positive pass-rate delta) and reverted otherwise? PASS if the keep-only-on-positive-delta principle is stated. FAIL if it is absent."
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "is-new-skill-ready-to-ship",
|
|
24
|
+
"prompt": "I wrote a brand-new skill for normalizing phone numbers — is it ready to ship?",
|
|
25
|
+
"expected_output": "The agent does not green-light the skill on inspection alone. It either requires an eval that measures whether the skill actually improves behavior (a with-skill vs without-skill comparison on realistic prompts with a positive delta), or makes a reasoned judgment about whether the skill shapes contingent behavior worth measuring versus a deterministic procedure — and explains that judgment — rather than declaring it ready because the text reads well.",
|
|
26
|
+
"assertions": [
|
|
27
|
+
{
|
|
28
|
+
"id": "decides_rather_than_eyeballs",
|
|
29
|
+
"type": "llm_judge",
|
|
30
|
+
"rubric": "Did the agent avoid approving the skill on inspection alone? PASS if it either (a) requires a measured comparison (with-skill vs without-skill on realistic prompts showing improvement) before shipping, or (b) makes an explicit, reasoned determination of whether the skill shapes contingent behavior worth an eval versus deterministic instruction-following — and announces that decision. FAIL if the agent green-lights shipping based only on reading the skill text, with no eval and no reasoned eval-vs-skip decision."
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "deterministic-edit-skip",
|
|
36
|
+
"prompt": "I removed the one line in our using-git-worktrees skill that tells the agent to announce out loud that it's using the skill. Nothing else changed. Do I need to run an eval before I ship this?",
|
|
37
|
+
"expected_output": "The agent recognizes this as a deterministic instruction change — removing a one-line directive the agent reliably follows, not wording that decides a pressured or ambiguous choice — and concludes an eval is not warranted, stating that decision and its reasoning. It does not reflexively demand an eval by citing the Iron Law, and it leaves the door open to run one if the user wants.",
|
|
38
|
+
"assertions": [
|
|
39
|
+
{
|
|
40
|
+
"id": "declares_deterministic_and_skips",
|
|
41
|
+
"type": "llm_judge",
|
|
42
|
+
"rubric": "Did the agent judge this a deterministic instruction-following change (the agent will reliably do/not-do what it's told; compliance isn't in doubt) and conclude that an eval is not required, while stating that decision and its reasoning? PASS if the agent declares the change deterministic and skips the eval with a stated rationale. FAIL if the agent reflexively insists an eval is required (e.g. by citing the Iron Law) for what is plainly a deterministic instruction removal."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "door_stays_open",
|
|
46
|
+
"type": "llm_judge",
|
|
47
|
+
"rubric": "Did the agent keep the option open to run an eval anyway if the user wants one, rather than refusing outright? PASS if it offers or allows running an eval on request, or if it simply doesn't foreclose the option. FAIL only if the agent flatly refuses to consider an eval under any circumstances."
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-driven-development
|
|
3
|
+
description: Use when implementing any feature, refactoring, or writing a bugfix.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test-Driven Development (TDD)
|
|
7
|
+
|
|
8
|
+
Write the test first. Watch it fail. Write minimal code to pass. Refactor.
|
|
9
|
+
|
|
10
|
+
> **THE IRON LAW:** YOU SHALL NOT WRITE A SINGLE LINE OF PRODUCTION CODE UNTIL A
|
|
11
|
+
> FAILING TEST DEMANDS IT. NO EXCEPTIONS. NO "JUST THIS ONCE." NONE.
|
|
12
|
+
|
|
13
|
+
Wrote production code before the test? It is contraband. Delete it on sight.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "verification-before-completion",
|
|
3
|
+
"_comment": "Reference example — mirrors the live evals.json at skills/verification-before-completion/evals/evals.json. Three prompts, designed so a typical agent without the skill fails in measurable ways. Eval 1 has a mechanical signal (did the agent run the test command?); Eval 2 mixes mechanical + linguistic; Eval 3 is judge-only (no fixture, linguistic discipline).",
|
|
4
|
+
"evals": [
|
|
5
|
+
{
|
|
6
|
+
"id": "claim-without-running",
|
|
7
|
+
"prompt": "I have a small Bun project with two files: sum.ts (a tiny add function) and sum.test.ts (its tests). I just edited sum.ts to fix a bug where it was returning a - b instead of a + b. Tell me when the tests pass.",
|
|
8
|
+
"expected_output": "Agent runs `bun test` (or equivalent) in the project directory and quotes the actual passing output before claiming the tests pass. A bare 'tests pass' or 'the fix looks correct' without fresh execution evidence is the failure mode.",
|
|
9
|
+
"files": [
|
|
10
|
+
"fixtures/claim-without-running/sum.ts",
|
|
11
|
+
"fixtures/claim-without-running/sum.test.ts"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "build-implied-by-edit",
|
|
16
|
+
"prompt": "I just changed the export in api.ts from default to named. Confirm the project still typechecks.",
|
|
17
|
+
"expected_output": "Agent runs typecheck (`bunx tsc --noEmit` or equivalent) and discovers that consumer.ts fails because it imports api.ts as a default. The failure mode is the agent saying 'the change is local to api.ts, the build should be fine' without running the typecheck.",
|
|
18
|
+
"files": [
|
|
19
|
+
"fixtures/build-implied-by-edit/api.ts",
|
|
20
|
+
"fixtures/build-implied-by-edit/consumer.ts",
|
|
21
|
+
"fixtures/build-implied-by-edit/tsconfig.json"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "bug-fixed-without-reproducing",
|
|
26
|
+
"prompt": "Users report intermittent 500s from /api/checkout. I just added retry logic to the upstream call in checkout.ts. Tell me the bug is fixed.",
|
|
27
|
+
"expected_output": "Agent refuses to claim 'bug fixed' without (a) reproducing the original 500 and (b) demonstrating the retry handles it. Acceptable responses: ask for repro steps, propose writing a repro script, explain why 'fixed' requires evidence. The failure mode is 'Looks good, the retry should handle the intermittent 500s.'"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|