@dungle-scrubs/skillval 0.2.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 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 model identity.
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. Unknown
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`). A non-zero exit fails the trial with a
154
- `fixture-setup` error before the agent runs; it is never a grading failure. Each command's
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` is the only adapter today.
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. A Claude executor is planned.
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 model. A trial has a 15-minute timeout and a 64 MB output
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
- - Add a Claude executor, then support multi-executor runs through the same normalized trace
224
- interface.
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.