Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a4a5e63
feat(plugin): add plugin evaluation support across all tiers
rng1995 Jul 25, 2026
81dbf56
fix(quality): avoid substring false positives
chrisknvidia Aug 4, 2026
57c970d
fix(quality): close keyword boundary review gaps
chrisknvidia Aug 4, 2026
2f928af
fix(quality): harden contextual edge cases
chrisknvidia Aug 4, 2026
70c7f6c
fix(quality): handle real skill context
chrisknvidia Aug 4, 2026
74316a1
fix(quality): ignore negative and hidden references
rng1995 Aug 4, 2026
ec09c4b
fix(tier3): isolate per-case staged inputs
rng1995 Aug 4, 2026
e9c0617
fix(tier3): harden partial reward preservation
rng1995 Aug 4, 2026
970daf2
fix(cli): stabilize Tier 3 progress rendering
rng1995 Aug 4, 2026
249e56d
fix(tier2): harden public input traversal
rng1995 Aug 4, 2026
15ebdae
fix(tier2): complete no-follow hardening
rng1995 Aug 4, 2026
0463a7e
fix(tier2): stabilize Windows file identity checks
rng1995 Aug 4, 2026
601275a
fix(tier2): align Windows native file operations
rng1995 Aug 4, 2026
898b183
docs: cover ported evaluation behavior
rng1995 Aug 4, 2026
3965061
style: remove trailing blank lines
rng1995 Aug 5, 2026
facf3d7
Merge remote-tracking branch 'origin/main' into naren/plugin-evaluati…
rng1995 Aug 12, 2026
5826791
Merge remote-tracking branch 'origin/main' into naren/plugin-evaluati…
rng1995 Aug 12, 2026
a3fd9bc
fix: preserve alias target case on Windows
rng1995 Aug 12, 2026
ae2c5eb
Merge remote-tracking branch 'origin/main' into naren/plugin-evaluati…
rng1995 Aug 12, 2026
f59870d
fix: share evaluator snapshot across baseline arms
rng1995 Aug 12, 2026
f52a473
Merge remote-tracking branch 'origin/main' into naren/plugin-evaluati…
rng1995 Aug 18, 2026
39e0372
Merge remote-tracking branch 'origin/main' into naren/plugin-evaluati…
rng1995 Aug 18, 2026
f3aefb9
Merge main and address plugin evaluation review
rng1995 Sep 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ jobs:
tests/test_results_location_legacy_compatibility.py
tests/test_tier3_compare.py
tests/test_tier3_progress.py
tests/utils/test_secure_fs_platform.py
tests/utils/test_secure_fs_selected_reads.py
tests/test_oss_packaging.py

Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,29 @@ All notable changes to SkillEvaluator are documented in this file.
- SARIF 2.1.0 reporter (`-r sarif`) for GitHub Code Scanning and other SARIF
consumers. Findings map to rule IDs, severity levels, and file locations from
Tier 1 validation results.
- Added public plugin evaluation across all tiers: static schema and MCP checks,
advisory offline dependency/context deduplication, and Harbor-backed live
evaluation with effectiveness and optional sum-of-parts Integration arms.

### Security

- Hardened plugin input handling with descriptor-anchored, no-follow discovery
and reads so linked, hard-linked, reparse-point, escaping, and special files
are rejected before provider calls or sandbox staging.

### Fixed

- Kept Tier 3's interactive progress frame at a stable height, bounded visible
stage history, serialized terminal redraws, and safely disabled a reporter
when initialization or background refresh fails.
- Made the `evaluate` workflow visible in top-level CLI help while retaining
the documented `skillevaluator tier3 evaluate` spelling.
- Tier 3 preserves completed rewards from partially errored jobs only when each
aggregate error maps to a concrete failed trial; explicit failed statuses and
non-zero aggregate exit codes still suppress ambiguous scores.
- Plugin manifest discovery is now root-bounded across all tiers, and Integration
evaluation requires explicit cross-component dataset evidence instead of
reporting unsupported composition claims.
- Malformed, non-UTF-8, or unreadable bundled and custom policy files now
produce path-specific CLI errors instead of leaking raw parser or I/O errors
([#128](https://github.com/NVIDIA/SkillEvaluator/issues/128)).
Expand Down
33 changes: 30 additions & 3 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
# Third-Party Notices

This file lists the direct third-party Python dependencies declared by this
distribution. `pyproject.toml` defines the dependency groups, and `uv.lock`
records the exact resolved dependency set used for this release.
This file lists direct third-party Python dependencies and vendored test
fixtures in this distribution. `pyproject.toml` defines the dependency groups,
and `uv.lock` records the exact resolved dependency set used for this release.

| Dependency group | Packages and licenses |
| --- | --- |
| Base | Click (BSD-3-Clause), IDNA (BSD-3-Clause), Jinja2 (BSD-3-Clause), Markdown-It-Py (MIT), Pydantic (MIT), PyYAML (MIT), Rich (MIT) |
| LLM | Anthropic (MIT), Boto3 (Apache-2.0), LiteLLM (MIT), OpenAI (Apache-2.0) |
| Tier 3 | Harbor (Apache-2.0) |
| Security | Bandit (Apache-2.0), pip-audit (Apache-2.0) |

## OpenClaw agent-skills test fixture

`tests/fixtures/openclaw-autoreview/` contains pinned files from
`openclaw/agent-skills`, commit `2a409d348a4bcf6f15e41e9a20efd0b298a32528`,
path `skills/autoreview`. The source repository is licensed under the MIT
License:

Copyright (c) 2026 openclaw

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
63 changes: 57 additions & 6 deletions docs/cli-reference.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "CLI Reference"
description: "Every SkillEvaluator command, flag, and default — validate, quality-check, similarity-check, tier3 evaluate, and the tier1/tier2/tier3 expert groups."
description: "Every SkillEvaluator command, flag, and default — validate, quality-check, similarity-check, tier3 evaluate, tier3 evaluate-plugin, and the tier1/tier2/tier3 expert groups."
layout: reference
---

Expand All @@ -27,6 +27,7 @@ commands referenced below.
| [dedup-scan](#dedup-scan) | Alias of `context-optimization-check` | Embeddings + LLM provider key |
| [similarity-check](#similarity-check) | Detect duplicate content across a collection | Embeddings provider key |
| [tier3 evaluate](#tier3-evaluate) | Run Tier 3 live agent evaluation | Provider key plus a credential the selected agents can use (one `NVIDIA_API_KEY` covers both on NVIDIA Build), backend (Docker by default) |
| [tier3 evaluate-plugin](#tier3-evaluate-plugin) | Run public plugin effectiveness or Integration evaluation | The same Tier 3 runtime requirements as `tier3 evaluate` |
| [create-eval-dataset](#create-eval-dataset) | Generate synthetic eval datasets | LLM provider key (none with `--no-llm`) |
| [init-custom-grader](#init-custom-grader) | Scaffold a bring-your-own-grader starter | Nothing |
| [init-harbor-task](#init-harbor-task) | Scaffold a bring-your-own-task Harbor starter | Nothing |
Expand Down Expand Up @@ -68,9 +69,9 @@ Most commands are also reachable under a tier-prefixed group — `skillevaluator
| --- | --- |
| `tier1` | `validate`, `quality-check`, `rubric-eval`, `security-scan`, `pii-scan`, `lint-scripts` |
| `tier2` | `context-optimization-check`, `dedup-scan`, `similarity-check` |
| `tier3` | `evaluate`, `create-eval-dataset`, `init-custom-grader`, `init-harbor-task`, `doctor`, `view`, `compare`, `validate`, `harbor-view` |
| `tier3` | `evaluate`, `evaluate-plugin`, `create-eval-dataset`, `init-custom-grader`, `init-harbor-task`, `doctor`, `view`, `compare`, `validate`, `harbor-view` |

The exceptions run in both directions. `health-check` and [models](#models) are top-level only, with no tier-prefixed twin. And two commands are advertised **only** inside the `tier3` group: [tier3 evaluate](#tier3-evaluate), documented with its full flag set below, and `tier3 validate`:
The exceptions run in both directions. `health-check` and [models](#models) are top-level only, with no tier-prefixed twin. [tier3 evaluate](#tier3-evaluate) is also discoverable as top-level `evaluate`, while [tier3 evaluate-plugin](#tier3-evaluate-plugin) and `tier3 validate` are advertised only inside the `tier3` group:

### tier3 validate

Expand Down Expand Up @@ -102,6 +103,8 @@ skillevaluator validate ./my-skill --tier3 -a codex \
--block-on-agent-eval # gate on Tier 3 too
skillevaluator validate ./my-skill --autopilot # Tier 3, generating evals if missing
skillevaluator validate ./my-skill --full -a codex # everything, one shot
skillevaluator validate ./my-plugin --type plugin --tier3 \
--lift-mode both # plugin effectiveness + Integration
skillevaluator validate ./my-skill --tiers 1,3 # explicit tier selection
skillevaluator validate ./skills-folder --full # whole catalog, serially
skillevaluator validate ./my-skill --tier3 -a codex,claude-code \
Expand Down Expand Up @@ -165,6 +168,7 @@ The following flags are forwarded to the live-eval engine **only when Tier 3 is
| `--autopilot` | off | Generate an evaluation source automatically when missing, then run Tier 3 (implies `--tier3`). |
| `-a, --agents TEXT` | `codex` | Comma-separated Harbor agents to evaluate. |
| `--env-mode` | `docker` | Harbor environment backend (full list under [tier3 evaluate](#tier3-evaluate)). |
| `--lift-mode [effectiveness\|integration\|both]` | `effectiveness` | Plugin only: compare the coordinated plugin with no plugin, its member skills staged individually, or both. Integration requires explicit cross-component dataset evidence. |
| `--skip-baseline` | off | Skip the without-skill baseline (no lift analysis, faster). |
| `--n-concurrent INTEGER` | unset | Concurrent eval cases per agent. |
| `--max-agents INTEGER` | unset | Maximum agents to run in parallel. |
Expand Down Expand Up @@ -298,9 +302,8 @@ Run Tier 3 live agent evaluation: real agents attempt your eval cases with and w
skillevaluator tier3 evaluate ./my-skill --agents codex --env-mode docker
```

<Note>
The old top-level spelling `skillevaluator evaluate` still works for existing scripts, but no longer appears in `--help`; `tier3 evaluate` is the advertised name.
</Note>
The focused workflow is also discoverable as `skillevaluator evaluate`; both
spellings share the same flags and behavior.

| Flag | Default | Effect |
| --- | --- | --- |
Expand Down Expand Up @@ -333,6 +336,54 @@ Without a `--model`/`--agent-model` override, each agent uses the selected provi

Flags marked "unset" fall back to their matching keys in `evals/config.yml` where one exists, then to the built-in defaults. The exception is `--results-dir`, which resolves independently of `evals/config.yml`.

## tier3 evaluate-plugin

Run Tier 3 against a public plugin without fetching remote components. The
command accepts a bundle-reference `agent_plugin.yaml`/`.yml` target or a
contained `.claude-plugin/plugin.json` target, stages the locally evaluable
skills, rules, and MCP declarations into a temporary wrapper, and records any
unresolved remote references in plugin provenance.

```bash title="Evaluate plugin effectiveness and Integration"
skillevaluator tier3 evaluate-plugin ./my-plugin --lift-mode both \
--evals-source ./evals/evals.json --agents codex
```

| Flag | Default | Effect |
| --- | --- | --- |
| `--evals-source PATH` | auto-detect | Dataset file, `evals/` directory, or skill/plugin directory containing `evals/`. |
| `-a, --agents TEXT` | `codex` | Comma-separated Harbor agents. |
| `--env-mode` | `docker` | Harbor environment backend; accepts the same values as [tier3 evaluate](#tier3-evaluate). |
| `--skip-baseline` | off | Skip the no-plugin baseline. Invalid with `integration` or `both`, which require a baseline. |
| `--lift-mode [effectiveness\|integration\|both]` | `effectiveness` | Compare the coordinated plugin with no plugin, its member skills staged individually, or both. `both` falls back to effectiveness when composition evidence is missing. |
| `--n-attempts INTEGER` | unset | Attempts per eval case (pass@k). |
| `--pass-threshold FLOAT` | unset | Score threshold (0.0–1.0) for a case to count as passed. |
| `--stop-on-pass / --no-stop-on-pass` | unset | Stop a case's remaining attempts once one passes. |
| `--n-concurrent INTEGER` | unset | Concurrent eval cases per agent. |
| `--max-agents INTEGER` | unset | Maximum agents to run in parallel. |
| `--model TEXT` | unset | Global agent model override. |
| `--agent-model TEXT` | unset | Per-agent model override, `AGENT=MODEL` (repeatable). |
| `--custom-dockerfile-mode [preserve\|rebase]` | unset | Apply the dataset's custom Dockerfile using the same modes as `tier3 evaluate`. |
| `--include-skills PATH` | none | Additional local member skills to stage (repeatable). Plugin evaluation always uses a group workspace. |
| `--repo-root DIRECTORY` | auto-detect | Clone-root override used to resolve same-repository component references deterministically. |
| `--copy-repo` | off | Copy the surrounding repository into the eval environment. |
| `--grading-mode [default\|default_plus_custom\|custom_only]` | unset | Reward/grading mode — see [Custom Graders & Tasks](custom-graders.mdx). |
| `--results-dir DIRECTORY` | unset | Directory for live-eval results. |
| `--harbor-keep-jobs` | off | Retain Harbor job directories and artifacts for inspection. |
| `--agent-runtime-preflight / --no-agent-runtime-preflight` | enabled | Run one bounded agent smoke task before the full evaluation matrix. |
| `--timeout-multiplier FLOAT` | unset | Scale Harbor step timeouts. |
| `--override-cpus INTEGER` | unset | Override eval-environment CPU allocation. |
| `--override-memory-mb INTEGER` | unset | Override eval-environment memory in MB. |
| `--override-storage-mb INTEGER` | unset | Override eval-environment storage in MB. |
| `--progress [auto\|rich\|plain\|off]` | `auto` | Progress display for the run. |

Integration requires at least one dataset case with `cross_component: true`
and two or more distinct `expected_skills`. An explicit `integration` run is
inconclusive without that evidence; `both` still runs the valid effectiveness
comparison and records why Integration was skipped. See [Plugin
evaluation](tier3-live-evaluation.mdx#plugin-evaluation) for component support,
security restrictions, and result semantics.

## create-eval-dataset

Create synthetic eval datasets for agent skill evaluation, written to `evals/evals.json`. Uses the configured LLM provider unless you pass `--no-llm`. Dataset anatomy and the four case buckets are documented in [Eval Datasets](eval-datasets.mdx).
Expand Down
10 changes: 9 additions & 1 deletion docs/eval-datasets.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Only the dataset is required. Everything else is optional and additive:
| `evals/evals.json` | The eval dataset. Without it, live evaluation cannot run. | Yes — required |
| `evals/config.yml` | Run policy: attempts, thresholds, runtime env, agent models, grading mode. | Yes |
| `evals/EVAL.md` | Developer guidance that steers dataset generation. | Yes |
| `evals/files/` | Input fixtures, staged into `/workspace/input/` in the container. | Yes |
| `evals/files/` | Input fixtures. Legacy cases receive the full directory; cases with a `files` field receive only their declared fixtures. | Yes |
| `evals/environment/` | Custom container environment: Dockerfile, sidecars, MCP servers. | Yes |
| `evals/harbor/` | Bring-your-own-task native Harbor tasks — see [Custom Graders & Tasks](custom-graders.mdx). | Yes |
| `evals/grader.py` or `evals/grader.sh` | Bring-your-own-grader script — see [Custom Graders & Tasks](custom-graders.mdx). | Yes |
Expand Down Expand Up @@ -86,13 +86,21 @@ top-level object with `skill_name` and an `evals` array.
| `prompt` | Yes | The user request the agent receives. |
| `expected_output` | Yes | Outcome-oriented description of what a successful agent produced. |
| `assertions` | No | Observable agent behaviors to verify, one per entry. |
| `files` | No | Relative fixture path or list of paths under `evals/`. When present, only these fixtures are staged into `/workspace/input/`; an empty or `null` value stages none. Omit the field to stage all of `evals/files/` for legacy datasets. |
| `expected_skill` | No | Skill the agent should route to; `null` for negative cases. |
| `expected_script` | No | Script the agent should run; `null` if none applies. |
| `acceptable_skills` | No | Closely related skills that earn partial routing credit (legacy alias: `acceptable_alternates`). |

The dataset file can be named `evals.json`, `evals.jsonl`, `evals.yaml`, or
`evals.yml` — `tier3 evaluate` accepts all four.

<Note>
Declare `files` on new cases so each trial receives only the fixtures it needs.
This prevents negative or unrelated cases from seeing other cases' inputs.
Paths are resolved within `evals/`; absolute paths, URI schemes, and traversal
outside the directory are rejected before the trial is staged.
</Note>

<AccordionGroup>
<Accordion title="Legacy flat format">
Older datasets are a flat array of entries with `question` instead of
Expand Down
42 changes: 33 additions & 9 deletions docs/tier2-deduplication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,27 @@ Catalogs stay local unless you explicitly share them. Because they contain
embeddings and other data derived from skill content, review them before
sharing like any other generated project artifact.

## Plugin deduplication

`validate` also accepts bundle-reference and contained plugins:

```bash title="Run offline and contextual checks for a plugin"
skillevaluator validate ./my-plugin --type plugin
```

Plugin Tier 2 is advisory and public/offline by design. It always checks for
duplicate skill and rule references in `agent_plugin.yaml`. When an embeddings
provider is available, it also runs context deduplication independently over
each safely discovered skill under `skills/`. Missing optional embedding access
skips only that contextual check; the offline reference check still runs.
Ordinary duplicate findings remain advisory, but unsafe linked, hard-linked, or
special plugin inputs are refused as blocking execution failures because the
requested check cannot run safely.

The public implementation does not connect to a remote vector database, fetch
remote component catalogs, or compare a plugin against a private plugin index.
It preserves the external repository's local-catalog-only Tier 2 contract.

## Inside validate

`validate` runs the intra-skill deduplication pass by default as part of a full
Expand Down Expand Up @@ -224,17 +245,20 @@ you intend to skip Tier 2 entirely.

Tier 2 treats skill content and catalog files as untrusted input. Before any
provider call, it rejects linked roots, linked directories and manifests,
hard-linked selected files, non-regular files, paths outside the verified scan
root, and all file redirects except the exact contained
`CLAUDE.md -> AGENTS.md` compatibility alias. That alias is validated but never
followed; its exactly named, independently discovered, single-link regular
sibling target is scanned once. Explicit limits bound
hard-linked selected files, reparse points, non-regular files, and paths outside
the verified scan root. The only redirect exception is the exact, contained
`CLAUDE.md -> AGENTS.md` compatibility alias used by public agent-skill layouts;
the alias is validated but never followed, and its independently discovered,
single-link regular sibling target is scanned once. Native descriptor- or
handle-anchored reads on POSIX and Windows verify that a selected file did not
change between discovery and use. Explicit limits bound
discovered paths, file counts, per-file and total input bytes, content chunks,
candidate clusters, catalog size and entry count, vector length, returned
matches, and pairwise scalar work. Catalog loading also rejects duplicate JSON
keys, unexpected fields, invalid identities, non-finite vectors, and
incompatible provider metadata. If a collection exceeds a limit, split it into
intentional batches rather than scanning an unbounded directory tree.
matches, pairwise scalar work, structured-data depth and scalar volume, and LLM
prompt/response budgets. Manifest and catalog loading rejects duplicate keys,
unexpected fields, invalid identities, non-finite values, and incompatible
provider metadata. If a collection exceeds a limit, split it into intentional
batches rather than scanning an unbounded directory tree.

### What leaves your machine

Expand Down
Loading
Loading