bkit Evals — Skill Quality Evaluation Runner
v2.1.11 Sprint β FR-β2. Wraps evals/runner.js with input validation,
result persistence, and structured reporting. Replaces the bare node evals/runner.js <skill> invocation that previously required users to
remember argv structure and ignored timeout / sandbox concerns.
Arguments
| Argument |
Description |
Example |
run <skill> |
Execute the eval suite for one skill |
/bkit-evals run gap-detector |
list |
List all skills that have an eval.yaml definition |
/bkit-evals list |
If no argument is provided, render the same output as list.
Behavior
run <skill>
- Validate
skill against /^[a-z][a-z0-9-]{0,63}$/. Reject anything else
(no shell metacharacters, no slashes, no spaces) — see Security below.
- Spawn
node evals/runner.js --skill <skill> via child_process.spawnSync
(argv form, no shell). Default timeout 30 s, max 120 s. The --skill flag
form is mandated by the runner CLI and locked by L3 contract test.
- Capture stdout / stderr. Parse the trailing JSON block via
balanced-brace fallback (string-aware).
- Apply fail-closed defense: if
parsed === null and stdout includes
Usage:, return reason: 'argv_format_mismatch'; if parsed === null
otherwise, return reason: 'parsed_null'. Exit code 0 alone NEVER
implies success — the parsed JSON must be present.
- Persist the structured result to
.bkit/runtime/evals-{skill}-{ISO timestamp}.json with stdout/stderr
tails (2000 chars each), parsed payload, and reason field.
- Render a one-line summary in the chat:
- exit code
- parsed pass/fail counts (if available)
- path of the persisted result file
list
- Read
evals/config.json to enumerate skill classifications.
- For each classification (
workflow, capability, hybrid),
list skills that have evals/{classification}/{skill}/eval.yaml.
- Render a category-grouped table with skill name + a one-line note from
the eval YAML (
description field if present).
Security
- Skill name regex prevents argument injection. Anything outside
[a-z][a-z0-9-]{0,63} is rejected with reason: invalid_skill_name.
- argv-array spawn (no shell). No template-string concatenation into
command lines.
- Result file path is composed from a hardcoded base + sanitized skill
name + timestamp; no traversal possible.
- Subprocess timeout enforced (default 30 s, hard cap 120 s) so a buggy
eval cannot block the session indefinitely.
Module Dependencies
| Module |
Function |
Usage |
lib/evals/runner-wrapper.js |
invokeEvals(skill, opts) |
Validate + spawn + persist |
lib/evals/runner-wrapper.js |
isValidSkillName(name) |
Regex pre-check shared with list |
evals/runner.js |
(subprocess) |
Existing eval execution engine |
Result Schema
.bkit/runtime/evals-{skill}-{timestamp}.json:
{
"skill": "gap-detector",
"invokedAt": "<ISO 8601>",
"exitCode": 0,
"timedOut": false,
"stdoutTail": "...",
"stderrTail": "...",
"parsed": { /* whatever runner.js prints as JSON, or null */ }
}
Examples
# Single eval
/bkit-evals run gap-detector
# Discovery
/bkit-evals list
Related
/control trust — eval results contribute to trust score
/code-review — uses eval data when assessing skills
/bkit explore (FR-β1) — explore evals as a category
ARGUMENTS:
1---2name: bkit-evals3description: Run skill evals via evals/runner.js — wrapper validates skill names, captures stdout/stderr, persists JSON results. Triggers: bkit evals, evals run, skill quality, eval runner4---56# bkit Evals — Skill Quality Evaluation Runner78> v2.1.11 Sprint β FR-β2. Wraps `evals/runner.js` with input validation,9> result persistence, and structured reporting. Replaces the bare `node10> evals/runner.js <skill>` invocation that previously required users to11> remember argv structure and ignored timeout / sandbox concerns.1213## Arguments1415| Argument | Description | Example |16|----------|-------------|---------|17| `run <skill>` | Execute the eval suite for one skill | `/bkit-evals run gap-detector` |18| `list` | List all skills that have an `eval.yaml` definition | `/bkit-evals list` |1920If no argument is provided, render the same output as `list`.2122## Behavior2324### `run <skill>`25261. Validate `skill` against `/^[a-z][a-z0-9-]{0,63}$/`. Reject anything else27 (no shell metacharacters, no slashes, no spaces) — see Security below.282. Spawn `node evals/runner.js --skill <skill>` via `child_process.spawnSync`29 (argv form, no shell). Default timeout 30 s, max 120 s. The `--skill` flag30 form is mandated by the runner CLI and locked by L3 contract test.313. Capture stdout / stderr. Parse the trailing JSON block via32 balanced-brace fallback (string-aware).334. Apply fail-closed defense: if `parsed === null` and stdout includes34 `Usage:`, return `reason: 'argv_format_mismatch'`; if `parsed === null`35 otherwise, return `reason: 'parsed_null'`. Exit code 0 alone NEVER36 implies success — the parsed JSON must be present.375. Persist the structured result to38 `.bkit/runtime/evals-{skill}-{ISO timestamp}.json` with stdout/stderr39 tails (2000 chars each), `parsed` payload, and `reason` field.405. Render a one-line summary in the chat:41 - exit code42 - parsed pass/fail counts (if available)43 - path of the persisted result file4445### `list`46471. Read `evals/config.json` to enumerate skill classifications.482. For each classification (`workflow`, `capability`, `hybrid`),49 list skills that have `evals/{classification}/{skill}/eval.yaml`.503. Render a category-grouped table with skill name + a one-line note from51 the eval YAML (`description` field if present).5253## Security5455- Skill name regex prevents argument injection. Anything outside56 `[a-z][a-z0-9-]{0,63}` is rejected with `reason: invalid_skill_name`.57- argv-array spawn (no shell). No template-string concatenation into58 command lines.59- Result file path is composed from a hardcoded base + sanitized skill60 name + timestamp; no traversal possible.61- Subprocess timeout enforced (default 30 s, hard cap 120 s) so a buggy62 eval cannot block the session indefinitely.6364## Module Dependencies6566| Module | Function | Usage |67|--------|----------|-------|68| `lib/evals/runner-wrapper.js` | `invokeEvals(skill, opts)` | Validate + spawn + persist |69| `lib/evals/runner-wrapper.js` | `isValidSkillName(name)` | Regex pre-check shared with `list` |70| `evals/runner.js` | (subprocess) | Existing eval execution engine |7172## Result Schema7374`.bkit/runtime/evals-{skill}-{timestamp}.json`:7576```json77{78 "skill": "gap-detector",79 "invokedAt": "<ISO 8601>",80 "exitCode": 0,81 "timedOut": false,82 "stdoutTail": "...",83 "stderrTail": "...",84 "parsed": { /* whatever runner.js prints as JSON, or null */ }85}86```8788## Examples8990```bash91# Single eval92/bkit-evals run gap-detector9394# Discovery95/bkit-evals list96```9798## Related99100- `/control trust` — eval results contribute to trust score101- `/code-review` — uses eval data when assessing skills102- `/bkit explore` (FR-β1) — explore evals as a category103104ARGUMENTS: