@dungle-scrubs/skillval 0.1.0 → 0.3.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/README.md +120 -12
- package/dist/cli.js +660 -115
- package/dist/cli.js.map +1 -1
- package/package.json +4 -3
- package/schemas/config.schema.json +1 -1
- package/schemas/skillval.schema.json +48 -2
package/README.md
CHANGED
|
@@ -6,6 +6,19 @@ agent behavior instead of merely checking whether the final answer looks accepta
|
|
|
6
6
|
baseline also passes, the rule is flagged as a no-op and a possible prune candidate. You can only
|
|
7
7
|
trust what you test.
|
|
8
8
|
|
|
9
|
+
## Capability and preference rules
|
|
10
|
+
|
|
11
|
+
A skill carries two kinds of rule. **Capability** rules teach a model something it does not yet
|
|
12
|
+
reliably do. **Preference** rules express a choice - style, convention, house taste - a model would
|
|
13
|
+
not reach on its own. Most skills mix both. The distinction has teeth because capabilities expire:
|
|
14
|
+
as models are trained on the same information, a capability rule stops changing behavior and turns
|
|
15
|
+
into dead weight. skillval finds those.
|
|
16
|
+
|
|
17
|
+
Each case runs with the skill and again without it (the baseline arm). Skill-pass with
|
|
18
|
+
baseline-fail means the rule is load-bearing. Skill-pass with baseline-pass means the model already
|
|
19
|
+
does this on its own - the rule is a prune candidate. Preferences stay; stale capabilities go.
|
|
20
|
+
Cases can record which kind they exercise with the `type` field (`capability` or `preference`).
|
|
21
|
+
|
|
9
22
|
## Install
|
|
10
23
|
|
|
11
24
|
```sh
|
|
@@ -64,6 +77,14 @@ to select one case, `--no-cache` to ignore cached arm results, `--skip-baseline`
|
|
|
64
77
|
arms, and `--json` for the complete report. The command exits with status 1 when any selected
|
|
65
78
|
case's skill arm fails.
|
|
66
79
|
|
|
80
|
+
Use `--model <model>` and `--effort <level>` to pin the executor's model and effort for the run,
|
|
81
|
+
so you can evaluate one skill under, for example, `--model sonnet --effort medium`. Both pass
|
|
82
|
+
through to the configured executor and are recorded in the report and the cache identity, so runs
|
|
83
|
+
at different levels are cached and compared separately. Effort levels are executor-specific and
|
|
84
|
+
validated before the run: `codex` accepts `none, minimal, low, medium, high, xhigh, max`; `claude`
|
|
85
|
+
accepts `low, medium, high, xhigh, max`; `pi` accepts `off, minimal, low, medium, high, xhigh`.
|
|
86
|
+
Model support for a given effort is a subset of these, enforced by the harness itself.
|
|
87
|
+
|
|
67
88
|
## Configuration
|
|
68
89
|
|
|
69
90
|
The configuration follows the [configuration JSON Schema](schemas/config.schema.json):
|
|
@@ -76,7 +97,7 @@ executor: codex
|
|
|
76
97
|
```
|
|
77
98
|
|
|
78
99
|
`roots` contains directories whose immediate children have the form `<skill>/SKILL.md`. Both `~`
|
|
79
|
-
and `$HOME` are expanded. Missing roots are skipped during `run`; `list` returns them in
|
|
100
|
+
and `$HOME` are expanded. `executor` selects the trial adapter: `codex`, `claude`, or `pi`. Missing roots are skipped during `run`; `list` returns them in
|
|
80
101
|
`missingRoots` with JSON output and prints each as `missing root: <path>` in human output.
|
|
81
102
|
|
|
82
103
|
Configuration path precedence is:
|
|
@@ -92,12 +113,31 @@ There is no legacy `~/.skillval` lookup. State uses `$XDG_STATE_HOME/skillval`,
|
|
|
92
113
|
- `cache/` stores arm results.
|
|
93
114
|
- `reports/` stores run reports named by a hash of the participating skills and their content
|
|
94
115
|
hashes. Each report also includes every participating skill's content hash and the executor's
|
|
95
|
-
name, version, and
|
|
116
|
+
name, version, model, thinking-level identity, and invocation-detection method.
|
|
96
117
|
|
|
97
118
|
`skillval list` returns the skill name, configured root, class, case count, whether `skillval.yml`
|
|
98
119
|
exists, and a `missing`, `invalid`, or `ready` status in JSON output. Invalid case files include a
|
|
99
120
|
validation error. Discovery only requires `SKILL.md`; evaluation requires a valid `skillval.yml`.
|
|
100
121
|
|
|
122
|
+
## Trust model
|
|
123
|
+
|
|
124
|
+
A `skillval.yml` is executable input, not passive configuration. Two fields run case-authored
|
|
125
|
+
shell commands directly on the machine that grades the suite:
|
|
126
|
+
|
|
127
|
+
- fixture `setup` commands, before the trial's agent runs;
|
|
128
|
+
- `assert.command_exit`, at grading time.
|
|
129
|
+
|
|
130
|
+
Both run with a minimal environment - only `PATH` is inherited, and `HOME` points at a throwaway
|
|
131
|
+
trial directory - and are killed on timeout, but that is scoping, not a sandbox: nothing prevents
|
|
132
|
+
a command from reading or writing anything your user account can reach. Evaluating a skill
|
|
133
|
+
therefore means trusting its `skillval.yml` exactly as you would trust running its Makefile or
|
|
134
|
+
npm scripts. Review the case file before running a suite from a repository you do not control.
|
|
135
|
+
|
|
136
|
+
The agent trials themselves are a separate boundary, sandboxed per executor (see
|
|
137
|
+
[Executors](#executors)): codex trials get an OS sandbox, claude trials get permission modes, and
|
|
138
|
+
pi generation trials have no sandbox at all and must be acknowledged with
|
|
139
|
+
`--allow-unsandboxed-pi`.
|
|
140
|
+
|
|
101
141
|
## Case files
|
|
102
142
|
|
|
103
143
|
Only a file named `skillval.yml` next to `SKILL.md` is recognized. There is no `evals.yml`
|
|
@@ -127,8 +167,21 @@ Case fields:
|
|
|
127
167
|
- `prompt`: the complete trial prompt.
|
|
128
168
|
- `assert.must_match`: JavaScript regular expressions that must match, with the `m` flag.
|
|
129
169
|
- `assert.must_not_match`: JavaScript regular expressions that must not match, with the `m` flag.
|
|
130
|
-
- `assert.graders`: deterministic graders. `tsc` is supported for generation cases.
|
|
131
|
-
graders and graders used with an unsupported mode are validation errors.
|
|
170
|
+
- `assert.graders`: parameterless deterministic graders. `tsc` is supported for generation cases.
|
|
171
|
+
Unknown graders and graders used with an unsupported mode are validation errors.
|
|
172
|
+
- `assert.json_schema`: validates a produced file against a JSON Schema (draft 2020-12), for
|
|
173
|
+
generation cases. Takes `file` (relative to the workspace) and `schema` (the JSON Schema, an
|
|
174
|
+
object or boolean). The file must exist inside the workspace, be a regular file, and parse as
|
|
175
|
+
JSON; a schema mismatch reports the failing instance path. Omit `$schema` or set it to 2020-12;
|
|
176
|
+
other declared dialects, an escaping `file` path, or a schema that does not compile are validation
|
|
177
|
+
errors.
|
|
178
|
+
- `assert.command_exit`: runs a shell command in the workspace and passes when it exits with the
|
|
179
|
+
expected code, for generation cases. Takes `command` and optional `expect` (default `0`). The
|
|
180
|
+
command is case-authored arbitrary shell, the same trust level as fixture `setup` (see
|
|
181
|
+
[Trust model](#trust-model)); it runs with a minimal environment and is killed after
|
|
182
|
+
120 seconds. This is the language-agnostic grader: run a
|
|
183
|
+
compiler, test runner, or validator over produced files in any language. Used in a non-generation
|
|
184
|
+
case it is a validation error.
|
|
132
185
|
- `trials`: an integer from 1 through 5. Results use a strict majority. If configured trials
|
|
133
186
|
disagree, the arm escalates to 5 trials.
|
|
134
187
|
- `fixture`: optional workspace fixture for this case. It replaces the suite-level `fixture`
|
|
@@ -150,8 +203,9 @@ two fields, and at least one is required:
|
|
|
150
203
|
directory, and it may not contain symbolic links (create links with `setup` commands instead);
|
|
151
204
|
anything else is a validation error at load time.
|
|
152
205
|
- `setup`: shell commands run sequentially inside the workspace after the copy, with a minimal
|
|
153
|
-
environment (`PATH` plus a throwaway `HOME`).
|
|
154
|
-
|
|
206
|
+
environment (`PATH` plus a throwaway `HOME`). These are case-authored arbitrary shell commands
|
|
207
|
+
executed on the grading machine (see [Trust model](#trust-model)). A non-zero exit fails the
|
|
208
|
+
trial with a `fixture-setup` error before the agent runs; it is never a grading failure. Each command's
|
|
155
209
|
stdout and stderr are captured into the trial record.
|
|
156
210
|
|
|
157
211
|
A suite-level `fixture` applies to every case; a case-level `fixture` replaces it entirely.
|
|
@@ -197,7 +251,7 @@ the trial as a fixture-setup error - here the conflict is the point.
|
|
|
197
251
|
Executors are adapters with three responsibilities: report stable metadata for cache keys, prepare
|
|
198
252
|
provider-specific skill and environment state, and run one trial request to return a normalized
|
|
199
253
|
`Trace`. The runner owns temporary workspace lifecycle, grading, caching, majority voting, and
|
|
200
|
-
reports. `codex`
|
|
254
|
+
reports. Three adapters exist: `codex`, `claude`, and `pi`.
|
|
201
255
|
|
|
202
256
|
The Codex adapter runs:
|
|
203
257
|
|
|
@@ -212,16 +266,70 @@ command contains `<skill>/SKILL.md`. The adapter also gives the skill arm a work
|
|
|
212
266
|
|
|
213
267
|
Baseline arms are not seeded. Their `HOME` points to an empty temporary directory so globally
|
|
214
268
|
installed skills are invisible, while `CODEX_HOME` still points to the user's real `~/.codex` for
|
|
215
|
-
authentication and model configuration.
|
|
269
|
+
authentication and model configuration.
|
|
270
|
+
|
|
271
|
+
The Claude adapter runs Claude Code headlessly:
|
|
272
|
+
|
|
273
|
+
```text
|
|
274
|
+
claude -p <prompt> --output-format stream-json --verbose --no-session-persistence <permissions>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
with the workspace as the working directory. Trigger cases run
|
|
278
|
+
`--permission-mode dontAsk --allowedTools "Read,Glob,Grep,Skill"` - read-only, but the Skill tool
|
|
279
|
+
must be allowed or invocation would be blocked before it can be observed. Generation cases run
|
|
280
|
+
`--permission-mode acceptEdits`. Invocation is detected from `Skill` tool_use blocks in the
|
|
281
|
+
stream-json trace that name the evaluated skill. The skill arm seeds a workspace-local
|
|
282
|
+
`.claude/skills/<name>` symlink; the baseline arm points `CLAUDE_CONFIG_DIR` at an empty
|
|
283
|
+
temporary directory so user-level skills are invisible (on macOS credentials live in the
|
|
284
|
+
Keychain, so authentication survives; elsewhere the credentials file is copied across). The
|
|
285
|
+
reported model comes from the real configuration's `settings.json`, or `default`.
|
|
286
|
+
|
|
287
|
+
The pi adapter runs [pi](https://github.com/badlogic/pi-mono) headlessly:
|
|
288
|
+
|
|
289
|
+
```text
|
|
290
|
+
pi -p --mode json --no-session <arm flags> <tool flags> <prompt>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
with the workspace as the working directory. pi has first-class arm switches: the skill arm
|
|
294
|
+
passes `--skill <directory>` so the evaluated skill is discoverable alongside the user's normal
|
|
295
|
+
library, and the baseline arm passes `--no-skills` - no HOME or config redirection is involved.
|
|
296
|
+
Trigger cases restrict tools with `-t read` (read also loads SKILL.md, so invocation stays
|
|
297
|
+
observable); generation cases keep pi's default tool set. pi implements the Agent Skills
|
|
298
|
+
progressive-disclosure standard by having the model `read` a listed skill's SKILL.md, so
|
|
299
|
+
invocation is detected from `read` toolCalls targeting `<skill>/SKILL.md` in the transcript.
|
|
300
|
+
The reported model is `defaultProvider/defaultModel` from `~/.pi/settings.json`. pi resolves
|
|
301
|
+
provider API keys from its auth file or environment variables (e.g. `ZAI_API_KEY`) - the key
|
|
302
|
+
must be available in the environment running skillval.
|
|
303
|
+
|
|
304
|
+
Unlike codex (which gets a read-only or `workspace-write` sandbox) and claude (permission modes),
|
|
305
|
+
pi has no OS sandbox: generation trials rely on the temporary-workspace convention alone, with no
|
|
306
|
+
enforced isolation, so an agent's writes are only conventionally scoped to the workspace. Because
|
|
307
|
+
of this, skillval refuses to run pi generation cases unless you pass `--allow-unsandboxed-pi` to
|
|
308
|
+
acknowledge the missing sandbox. Trigger cases are read-only and unaffected. Prefer codex or claude
|
|
309
|
+
for untrusted generation cases.
|
|
310
|
+
|
|
311
|
+
Each adapter reports its detection method as `invocationDetection` in report metadata. The
|
|
312
|
+
`invoked` signal has asymmetric confidence: claude detects invocation from a structured `Skill`
|
|
313
|
+
tool_use block, while codex and pi string-match trace text for `<skill>/SKILL.md`. Trigger rates
|
|
314
|
+
should not be compared across executors as if they measured the same thing.
|
|
315
|
+
|
|
316
|
+
By default, executors do not set a model or thinking/effort level; trials inherit the harness
|
|
317
|
+
defaults the user has configured, and each adapter captures both into its identity so results are
|
|
318
|
+
always associated with what actually ran: codex reads `model` and `model_reasoning_effort` from
|
|
319
|
+
`~/.codex/config.toml`, claude reads `model` and `effort` from `settings.json`, and pi reads
|
|
320
|
+
`defaultProvider/defaultModel` and `defaultThinkingLevel` from `~/.pi/settings.json`. A missing
|
|
321
|
+
value is recorded as `default` (the provider's own default applies). Passing `--model`/`--effort`
|
|
322
|
+
overrides the default for the run, and the override is what gets captured. Changing any of these -
|
|
323
|
+
in the provider configuration or via the flags - therefore keys distinct cached results.
|
|
216
324
|
|
|
217
325
|
Cached arm results are keyed by runner version, skill content hash, serialized case, arm, executor
|
|
218
|
-
name, executor version, and configured
|
|
219
|
-
buffer.
|
|
326
|
+
name, executor version, configured model, and configured thinking level. A trial has a 15-minute
|
|
327
|
+
timeout and a 64 MB output buffer.
|
|
220
328
|
|
|
221
329
|
## Roadmap
|
|
222
330
|
|
|
223
|
-
-
|
|
224
|
-
|
|
331
|
+
- Support multi-executor runs through the same normalized trace interface, now that `codex` and
|
|
332
|
+
`claude` adapters share it.
|
|
225
333
|
- Run multiple models and emit per-model reports. A passing binding or trigger result on a weaker
|
|
226
334
|
tier is a conservative bound for stronger tiers. Baseline no-op results remain model-specific,
|
|
227
335
|
and a rule is a prune candidate only when every model in normal use passes at baseline.
|