agent-skill-eval 0.2.1__tar.gz → 0.3.0__tar.gz

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.
Files changed (63) hide show
  1. agent_skill_eval-0.3.0/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  2. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/CHANGELOG.md +11 -0
  3. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/PKG-INFO +4 -2
  4. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/README.md +3 -1
  5. agent_skill_eval-0.3.0/examples/review-diff/evals/evals.json +45 -0
  6. agent_skill_eval-0.3.0/examples/review-diff/evals/files/changes.diff +21 -0
  7. agent_skill_eval-0.3.0/examples/review-diff/evals/files/orders.py +22 -0
  8. agent_skill_eval-0.3.0/examples/validate-config/evals/evals.json +57 -0
  9. agent_skill_eval-0.3.0/examples/validate-config/evals/files/app_config.json +10 -0
  10. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/pyproject.toml +1 -1
  11. agent_skill_eval-0.3.0/skills/review-diff/SKILL.md +85 -0
  12. agent_skill_eval-0.3.0/skills/validate-config/SKILL.md +91 -0
  13. agent_skill_eval-0.3.0/skills/validate-config/references/config-rules.md +33 -0
  14. agent_skill_eval-0.3.0/skills/validate-config/scripts/validate_config.py +68 -0
  15. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/__init__.py +2 -2
  16. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  17. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  18. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/.github/actions/setup/action.yml +0 -0
  19. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/.github/workflows/ci.yml +0 -0
  20. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/.github/workflows/publish.yml +0 -0
  21. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/.gitignore +0 -0
  22. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/AGENTS.md +0 -0
  23. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/CODE_OF_CONDUCT.md +0 -0
  24. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/CONTRIBUTING.md +0 -0
  25. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/LICENSE +0 -0
  26. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/TODO.md +0 -0
  27. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/commit-push-pr/evals/evals.json +0 -0
  28. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/commit-push-pr/evals/files/sample_change.py +0 -0
  29. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/fix-failing-tests/evals/evals.json +0 -0
  30. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/fix-failing-tests/evals/files/calculator.py +0 -0
  31. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/fix-failing-tests/evals/files/conftest.py +0 -0
  32. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/fix-failing-tests/evals/files/test_calculator.py +0 -0
  33. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/write-release-notes/evals/evals.json +0 -0
  34. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/examples/write-release-notes/evals/files/commits.txt +0 -0
  35. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/schemas/evals.schema.json +0 -0
  36. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/scripts/record-demo.sh +0 -0
  37. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/skills/commit-push-pr/SKILL.md +0 -0
  38. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/skills/fix-failing-tests/SKILL.md +0 -0
  39. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/skills/write-release-notes/SKILL.md +0 -0
  40. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/__main__.py +0 -0
  41. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/cli.py +0 -0
  42. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/git_state.py +0 -0
  43. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/graders/__init__.py +0 -0
  44. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/harnesses/__init__.py +0 -0
  45. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/models.py +0 -0
  46. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/runner.py +0 -0
  47. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/skills.py +0 -0
  48. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/src/skill_eval/workspace.py +0 -0
  49. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/__init__.py +0 -0
  50. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/conftest.py +0 -0
  51. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/fixtures/evals/format-json.json +0 -0
  52. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/fixtures/skills/format-json/SKILL.md +0 -0
  53. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_cli_smoke.py +0 -0
  54. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_fuzz_grader.py +0 -0
  55. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_git_state.py +0 -0
  56. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_graders.py +0 -0
  57. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_harnesses.py +0 -0
  58. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_launch_features.py +0 -0
  59. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_llm_grader.py +0 -0
  60. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_runner.py +0 -0
  61. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_skill_validation.py +0 -0
  62. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_skills.py +0 -0
  63. {agent_skill_eval-0.2.1 → agent_skill_eval-0.3.0}/tests/test_workspace.py +0 -0
@@ -0,0 +1,9 @@
1
+ ## What
2
+
3
+ ## Why
4
+
5
+ ## Testing
6
+
7
+ - [ ] `uv run --extra dev ruff check src/ tests/`
8
+ - [ ] `uv run --extra dev ruff format --check src/ tests/`
9
+ - [ ] `uv run --extra dev pytest -q`
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.3.0] - 2026-06-11
6
+
7
+ ### Added
8
+ - `validate-config` example skill + eval suite: exercises bundled resources (agent runs skill's `scripts/` and reads its `references/`); graded via command-ran + file + content checks; fully offline
9
+ - `review-diff` example skill + eval suite: read-only analysis (planted bugs found, documented decoy not flagged, nothing modified); graded via chat-output-only content + LLM rubric; fully offline
10
+
11
+ ## [0.2.1] - 2026-06-11
12
+
13
+ ### Changed
14
+ - Renamed package from `skill-eval` to `agent-skill-eval` (PyPI name collision)
15
+
5
16
  ## [0.2.0] - 2026-06-10
6
17
 
7
18
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-skill-eval
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Framework for evaluating agent skills across OpenCode, Claude Code, and Codex
5
5
  Project-URL: Homepage, https://github.com/tardigrde/skill-evals
6
6
  Project-URL: Repository, https://github.com/tardigrde/skill-evals
@@ -250,13 +250,15 @@ skill-eval init my-skill [--output ./examples]
250
250
 
251
251
  ## Example skills
252
252
 
253
- Three example skills ship with the repo, chosen to exercise different grading surfaces:
253
+ Five example skills ship with the repo, chosen to exercise different grading surfaces:
254
254
 
255
255
  | Skill | Tests | Grading surface |
256
256
  | --- | --- | --- |
257
257
  | [`commit-push-pr`](skills/commit-push-pr/SKILL.md) | git workflow automation | deterministic state-diff checks (branch/commit/push/PR); needs a `--source-repo` |
258
258
  | [`fix-failing-tests`](skills/fix-failing-tests/SKILL.md) | error recovery / iterative refinement | deterministic + file checks; fully offline |
259
259
  | [`write-release-notes`](skills/write-release-notes/SKILL.md) | subjective writing quality, anti-fabrication | LLM rubric grading; fully offline |
260
+ | [`validate-config`](skills/validate-config/SKILL.md) | bundled resources: does the agent run the skill's `scripts/` and read its `references/`? | command-ran + file + content checks; fully offline |
261
+ | [`review-diff`](skills/review-diff/SKILL.md) | read-only analysis: planted bugs found, documented decoy not flagged, nothing modified | chat-output-only grading (content + LLM rubric); fully offline |
260
262
 
261
263
  Each has a matching eval suite under [`examples/`](examples/). `skills/` holds the artifacts being evaluated; `examples/` holds the test cases — so you can test one skill against many suites or one suite against many skill versions.
262
264
 
@@ -219,13 +219,15 @@ skill-eval init my-skill [--output ./examples]
219
219
 
220
220
  ## Example skills
221
221
 
222
- Three example skills ship with the repo, chosen to exercise different grading surfaces:
222
+ Five example skills ship with the repo, chosen to exercise different grading surfaces:
223
223
 
224
224
  | Skill | Tests | Grading surface |
225
225
  | --- | --- | --- |
226
226
  | [`commit-push-pr`](skills/commit-push-pr/SKILL.md) | git workflow automation | deterministic state-diff checks (branch/commit/push/PR); needs a `--source-repo` |
227
227
  | [`fix-failing-tests`](skills/fix-failing-tests/SKILL.md) | error recovery / iterative refinement | deterministic + file checks; fully offline |
228
228
  | [`write-release-notes`](skills/write-release-notes/SKILL.md) | subjective writing quality, anti-fabrication | LLM rubric grading; fully offline |
229
+ | [`validate-config`](skills/validate-config/SKILL.md) | bundled resources: does the agent run the skill's `scripts/` and read its `references/`? | command-ran + file + content checks; fully offline |
230
+ | [`review-diff`](skills/review-diff/SKILL.md) | read-only analysis: planted bugs found, documented decoy not flagged, nothing modified | chat-output-only grading (content + LLM rubric); fully offline |
229
231
 
230
232
  Each has a matching eval suite under [`examples/`](examples/). `skills/` holds the artifacts being evaluated; `examples/` holds the test cases — so you can test one skill against many suites or one suite against many skill versions.
231
233
 
@@ -0,0 +1,45 @@
1
+ {
2
+ "skill_name": "review-diff",
3
+ "evals": [
4
+ {
5
+ "id": "explicit-invoke",
6
+ "prompt": "Use the $review-diff skill to review changes.diff against the current code.",
7
+ "expected_output": "The agent reads changes.diff and orders.py, identifies the two real bugs (the loop skips the last item due to range(len(prices) - 1), and the comparison uses > instead of >= so exactly `threshold` items get no discount despite the docstring saying 'threshold or more'), does not flag the documented cents-based summation, modifies nothing, and ends with VERDICT: REQUEST_CHANGES.",
8
+ "files": ["files/orders.py", "files/changes.diff"],
9
+ "force_skill_invocation": true,
10
+ "assertions": [
11
+ "The output contains \"VERDICT\"",
12
+ "The output contains \"REQUEST_CHANGES\"",
13
+ "The review identifies that the loop skips the last item (off-by-one in the range bound)",
14
+ "The review identifies that the threshold comparison uses > instead of >=, contradicting the docstring's 'threshold or more'",
15
+ "The review does not flag the cents-based summation, which is correct and documented as intentional",
16
+ "The agent did not modify orders.py and did not write any new files"
17
+ ]
18
+ },
19
+ {
20
+ "id": "implicit-invoke",
21
+ "prompt": "A teammate sent me changes.diff with a proposed addition to orders.py. Can you review it before I merge?",
22
+ "expected_output": "The agent automatically triggers the skill, reads both files, reports the two planted bugs with severities, leaves the workspace untouched, and ends with VERDICT: REQUEST_CHANGES.",
23
+ "files": ["files/orders.py", "files/changes.diff"],
24
+ "assertions": [
25
+ "The output contains \"VERDICT\"",
26
+ "The output contains \"REQUEST_CHANGES\"",
27
+ "The review identifies that the loop skips the last item (off-by-one in the range bound)",
28
+ "The review identifies that the threshold comparison uses > instead of >=, contradicting the docstring's 'threshold or more'",
29
+ "The agent did not modify orders.py and did not write any new files"
30
+ ]
31
+ },
32
+ {
33
+ "id": "negative-control",
34
+ "prompt": "How many functions does changes.diff add to orders.py?",
35
+ "expected_output": "The skill should NOT trigger. The agent should answer that the diff adds one function (bulk_discount_total) without producing a full code review or verdict.",
36
+ "files": ["files/orders.py", "files/changes.diff"],
37
+ "should_trigger": false,
38
+ "assertions": [
39
+ "The output contains \"bulk_discount_total\"",
40
+ "The agent states that exactly one function is added",
41
+ "The agent only answered the question without delivering a severity-tagged review or verdict"
42
+ ]
43
+ }
44
+ ]
45
+ }
@@ -0,0 +1,21 @@
1
+ --- a/orders.py
2
+ +++ b/orders.py
3
+ @@ -20,3 +20,18 @@ def format_receipt(items: list[tuple[str, float]]) -> str:
4
+ """Render one 'name: $price' line per item."""
5
+ lines = [f"{name}: ${price:.2f}" for name, price in items]
6
+ return "\n".join(lines)
7
+ +
8
+ +
9
+ +def bulk_discount_total(prices: list[float], threshold: int = 10) -> float:
10
+ + """Total with a 5% bulk discount when ordering `threshold` or more items.
11
+ +
12
+ + Prices are summed in integer cents (rounded per item) to avoid float
13
+ + accumulation error; this is intentional, see the team float-handling policy.
14
+ + """
15
+ + cents = 0
16
+ + for i in range(len(prices) - 1):
17
+ + cents += round(prices[i] * 100)
18
+ + total = cents / 100
19
+ + if len(prices) > threshold:
20
+ + return round(total * 0.95, 2)
21
+ + return round(total, 2)
@@ -0,0 +1,22 @@
1
+ """Order processing utilities."""
2
+
3
+
4
+ def order_total(prices: list[float]) -> float:
5
+ """Sum a list of item prices, rounded to cents."""
6
+ total = 0.0
7
+ for price in prices:
8
+ total += price
9
+ return round(total, 2)
10
+
11
+
12
+ def apply_discount(total: float, percent: float) -> float:
13
+ """Apply a percentage discount to a total."""
14
+ if percent < 0 or percent > 100:
15
+ raise ValueError(f"invalid discount percent: {percent}")
16
+ return round(total * (1 - percent / 100), 2)
17
+
18
+
19
+ def format_receipt(items: list[tuple[str, float]]) -> str:
20
+ """Render one 'name: $price' line per item."""
21
+ lines = [f"{name}: ${price:.2f}" for name, price in items]
22
+ return "\n".join(lines)
@@ -0,0 +1,57 @@
1
+ {
2
+ "skill_name": "validate-config",
3
+ "evals": [
4
+ {
5
+ "id": "explicit-invoke",
6
+ "prompt": "Use the $validate-config skill to validate app_config.json.",
7
+ "expected_output": "The agent runs the bundled structural validator script, reads the convention rules from the skill's references, writes CONFIG_REPORT.md flagging 5 violations (missing 'environment' key, service_name not svc- kebab-case, port 8080 outside 9000-9999, log_level 'verbose' not allowed, max_retries 10 above 5), and reports TOTAL VIOLATIONS: 5.",
8
+ "files": ["files/app_config.json"],
9
+ "force_skill_invocation": true,
10
+ "assertions": [
11
+ "The agent ran the bundled python validation script",
12
+ "The file `CONFIG_REPORT.md` exists",
13
+ "The output contains \"TOTAL VIOLATIONS\"",
14
+ "The report flags the port as outside the allowed 9000-9999 range",
15
+ "The report flags service_name for not being kebab-case with the svc- prefix",
16
+ "The report flags the missing 'environment' key as a structural issue",
17
+ "The report does not flag cache_ttl_seconds, which complies with the rules"
18
+ ]
19
+ },
20
+ {
21
+ "id": "implicit-invoke",
22
+ "prompt": "Can you check whether app_config.json follows our team's configuration conventions?",
23
+ "expected_output": "The agent automatically triggers the skill, runs the bundled validator, checks the reference rules, and writes CONFIG_REPORT.md listing all 5 violations.",
24
+ "files": ["files/app_config.json"],
25
+ "assertions": [
26
+ "The agent ran the bundled python validation script",
27
+ "The file `CONFIG_REPORT.md` exists",
28
+ "The output contains \"TOTAL VIOLATIONS\"",
29
+ "The report flags the port as outside the allowed 9000-9999 range",
30
+ "The report flags max_retries as exceeding the maximum of 5"
31
+ ]
32
+ },
33
+ {
34
+ "id": "reference-knowledge",
35
+ "prompt": "Is the port configured in app_config.json allowed by our team's port allocation policy?",
36
+ "expected_output": "The agent consults the skill's references/config-rules.md, finds the allocated range 9000-9999, and answers that port 8080 is NOT allowed. Without the skill the agent cannot know the policy and should say so or guess.",
37
+ "files": ["files/app_config.json"],
38
+ "assertions": [
39
+ "The output contains \"9000\"",
40
+ "The agent states that port 8080 is not allowed by the policy",
41
+ "The agent cites the team's allocated port range rather than guessing a generic answer"
42
+ ]
43
+ },
44
+ {
45
+ "id": "negative-control",
46
+ "prompt": "What is the value of max_retries in app_config.json?",
47
+ "expected_output": "The skill should NOT trigger. The agent should simply read the file and answer '10' without running the validator or writing a report.",
48
+ "files": ["files/app_config.json"],
49
+ "should_trigger": false,
50
+ "assertions": [
51
+ "The output contains \"10\"",
52
+ "The agent only answered the question without writing a report file",
53
+ "The validation script was not executed"
54
+ ]
55
+ }
56
+ ]
57
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "service_name": "DataPipeline",
3
+ "port": 8080,
4
+ "log_level": "verbose",
5
+ "max_retries": 10,
6
+ "features": {
7
+ "cache_enabled": true,
8
+ "cache_ttl_seconds": 600
9
+ }
10
+ }
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agent-skill-eval"
7
- version = "0.2.1"
7
+ version = "0.3.0"
8
8
  description = "Framework for evaluating agent skills across OpenCode, Claude Code, and Codex"
9
9
  requires-python = ">=3.11"
10
10
  license = "MIT"
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: review-diff
3
+ description: Review a proposed code change (a .diff/.patch file) against the current source code and deliver a structured, severity-tagged review verdict in chat, without modifying or creating any files.
4
+ license: MIT
5
+ compatibility: opencode, claude-code, codex
6
+ metadata:
7
+ workflow: read-analyze-report
8
+ ---
9
+
10
+ ## What I do
11
+
12
+ Review a proposed code change — provided as a unified diff file — against the current source code in the workspace. I read both, look for real defects, and deliver a structured review directly in my reply. I am strictly read-only: I never apply the diff, never edit source files, and never write report files.
13
+
14
+ ## When to use me
15
+
16
+ Use when the user asks to review a diff, patch, or proposed change before merging. Trigger on phrases like:
17
+ - "review this diff"
18
+ - "can you review changes.diff before I merge?"
19
+ - "is this patch safe to apply?"
20
+ - "look over this proposed change"
21
+
22
+ ## IMPORTANT: Read-only
23
+
24
+ Do NOT apply the diff. Do NOT modify any source file. Do NOT create any new files (no report files, no notes). The entire deliverable is the review text in your reply.
25
+
26
+ ## IMPORTANT: Precision over volume
27
+
28
+ Only flag real defects. Do not pad the review with style nits, speculation, or restatements of what the diff does. Code that looks unusual but is correct and documented is not a finding. A wrong finding is worse than a missing one.
29
+
30
+ ## Steps
31
+
32
+ ### Step 1: Read the diff
33
+
34
+ Read the diff file the user pointed at. Identify which files and functions it touches.
35
+
36
+ ### Step 2: Read the current source
37
+
38
+ Read the current version of every file the diff touches, so you understand the context the change lands in.
39
+
40
+ ### Step 3: Analyze the change
41
+
42
+ Mentally apply the diff and look for defects:
43
+ - Off-by-one errors in loops, ranges, slicing
44
+ - Wrong comparison operators (`>` vs `>=`, boundary conditions vs the documented behavior)
45
+ - Logic that contradicts the function's own docstring or naming
46
+ - Broken callers, changed return types, missed edge cases (empty input, zero, None)
47
+
48
+ Check claimed behavior against actual behavior: if the docstring says "threshold or more", the code must use `>=`.
49
+
50
+ ### Step 4: Deliver the review
51
+
52
+ Reply with this exact structure:
53
+
54
+ ```
55
+ ## Review: <diff file name>
56
+
57
+ ### Findings
58
+
59
+ - <file>:<approx line>: [HIGH|MEDIUM|LOW] <what is wrong and why>
60
+ - ...or "No findings."
61
+
62
+ VERDICT: APPROVE
63
+ ```
64
+
65
+ or, if there is at least one HIGH or MEDIUM finding:
66
+
67
+ ```
68
+ VERDICT: REQUEST_CHANGES
69
+ ```
70
+
71
+ The reply MUST end with exactly one `VERDICT:` line containing either `APPROVE` or `REQUEST_CHANGES`.
72
+
73
+ ## Definition of done
74
+
75
+ All of these must be true:
76
+ - The diff file and every touched source file were read
77
+ - Each finding names the file, approximate location, severity, and the concrete defect
78
+ - No source file was modified and no new file was created
79
+ - The reply ends with exactly one `VERDICT:` line
80
+
81
+ ## Error handling
82
+
83
+ - If the diff file does not exist, say which file was expected and stop
84
+ - If the diff does not apply cleanly to the current source (context mismatch), report that as a HIGH finding
85
+ - If the diff is empty, reply with "No findings." and `VERDICT: APPROVE`
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: validate-config
3
+ description: Validate a project configuration file against the team's structural requirements and naming/range conventions, then write a CONFIG_REPORT.md listing every violation.
4
+ license: MIT
5
+ compatibility: opencode, claude-code, codex
6
+ metadata:
7
+ workflow: validate-report
8
+ ---
9
+
10
+ ## What I do
11
+
12
+ Validate a JSON configuration file (e.g. `app_config.json`) in two passes:
13
+
14
+ 1. **Structural pass**: run the bundled validation script, which checks required keys and value types.
15
+ 2. **Convention pass**: check every value against the team's convention rules, which are documented in this skill's `references/config-rules.md`.
16
+
17
+ Then write a `CONFIG_REPORT.md` in the workspace root listing every violation found, and summarize the result to the user.
18
+
19
+ ## When to use me
20
+
21
+ Use when the user asks to validate, audit, lint, or check a configuration file, or asks whether a config follows team conventions or policy. Trigger on phrases like:
22
+ - "validate the config"
23
+ - "check app_config.json against our conventions"
24
+ - "does this config follow our policy?"
25
+ - "audit the configuration file"
26
+
27
+ ## IMPORTANT: Use the bundled resources
28
+
29
+ Do NOT guess the team's conventions — they are arbitrary house rules you cannot infer. You MUST:
30
+
31
+ - Run the bundled script `scripts/validate_config.py` (located in this skill's directory) for the structural pass. Do not re-implement its checks by hand.
32
+ - Read `references/config-rules.md` (located in this skill's directory) for the convention pass. Every rule in that file must be checked.
33
+
34
+ ## Steps
35
+
36
+ ### Step 1: Run the structural validator
37
+
38
+ The script lives in this skill's directory, next to this SKILL.md file. Run it with the config file as the argument:
39
+
40
+ ```bash
41
+ python <path-to-this-skill>/scripts/validate_config.py app_config.json
42
+ ```
43
+
44
+ (Use `python3` if `python` is not on the PATH.)
45
+
46
+ It prints one `STRUCTURAL: ...` line per problem, or `STRUCTURAL: OK` if the structure is valid.
47
+
48
+ ### Step 2: Read the convention rules
49
+
50
+ Read `references/config-rules.md` from this skill's directory. It defines numbered rules (RULE 1, RULE 2, ...) for naming, ranges, and allowed values.
51
+
52
+ ### Step 3: Check the config against every rule
53
+
54
+ Read the config file and evaluate each rule from the reference document against the actual values. Record each violation with its rule number.
55
+
56
+ ### Step 4: Write CONFIG_REPORT.md
57
+
58
+ Create `CONFIG_REPORT.md` in the workspace root with this structure:
59
+
60
+ ```markdown
61
+ # Config validation report: <config file name>
62
+
63
+ ## Structural issues
64
+
65
+ - <each STRUCTURAL line from the script, or "None">
66
+
67
+ ## Convention violations
68
+
69
+ - RULE <n>: <key> = <actual value> — <what the rule requires>
70
+ - ...or "None"
71
+
72
+ TOTAL VIOLATIONS: <count of structural issues + convention violations>
73
+ ```
74
+
75
+ ### Step 5: Report to the user
76
+
77
+ Summarize the findings in your reply and include the exact `TOTAL VIOLATIONS: <n>` line in your final message.
78
+
79
+ ## Definition of done
80
+
81
+ All of these must be true:
82
+ - The bundled `validate_config.py` script was executed (not re-implemented)
83
+ - `references/config-rules.md` was read and every rule in it was checked
84
+ - `CONFIG_REPORT.md` exists in the workspace root with both sections and the `TOTAL VIOLATIONS:` line
85
+ - The final reply to the user contains the `TOTAL VIOLATIONS: <n>` line
86
+
87
+ ## Error handling
88
+
89
+ - If the config file does not exist, report which file was expected and stop
90
+ - If the config is not valid JSON, report the parse error as a structural issue and skip the convention pass
91
+ - If the bundled script cannot be found, say so explicitly — do not silently substitute your own checks
@@ -0,0 +1,33 @@
1
+ # Team config conventions
2
+
3
+ House rules for service configuration files. These are conventions, not
4
+ structural requirements — the structural validator script does not check
5
+ them. Every rule below must be evaluated against the config.
6
+
7
+ ## RULE 1: service_name format
8
+
9
+ `service_name` must be kebab-case (lowercase, words separated by `-`) and
10
+ must start with the `svc-` prefix.
11
+
12
+ Valid: `svc-data-pipeline`, `svc-auth`. Invalid: `DataPipeline`, `data_pipeline`, `auth`.
13
+
14
+ ## RULE 2: port range
15
+
16
+ `port` must be in the team's allocated range **9000–9999** (inclusive).
17
+ Ports outside this range collide with other teams' allocations.
18
+
19
+ ## RULE 3: log_level allowed values
20
+
21
+ `log_level` must be exactly one of: `debug`, `info`, `warning`, `error`.
22
+ No other values (e.g. `verbose`, `trace`, `WARN`) are accepted.
23
+
24
+ ## RULE 4: max_retries bound
25
+
26
+ `max_retries` must be between **0 and 5** (inclusive). Higher values mask
27
+ persistent failures and overload downstream services.
28
+
29
+ ## RULE 5: cache TTL floor
30
+
31
+ When `features.cache_enabled` is `true`, `features.cache_ttl_seconds` must
32
+ be **at least 300**. Shorter TTLs cause cache stampedes. (If caching is
33
+ disabled, the TTL value is irrelevant.)
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env python3
2
+ """Structural validator for team service configs.
3
+
4
+ Checks required keys and value types only. Convention rules (naming,
5
+ ranges, allowed values) live in references/config-rules.md and are
6
+ checked by the agent, not by this script.
7
+
8
+ Usage: python validate_config.py <config.json>
9
+ Prints one "STRUCTURAL: ..." line per problem, or "STRUCTURAL: OK".
10
+ Exit code 0 if structure is valid, 1 otherwise.
11
+ """
12
+
13
+ import json
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ REQUIRED_KEYS = {
18
+ "service_name": str,
19
+ "port": int,
20
+ "log_level": str,
21
+ "max_retries": int,
22
+ "environment": str,
23
+ }
24
+
25
+
26
+ def validate(config_path: Path) -> list[str]:
27
+ problems: list[str] = []
28
+ try:
29
+ data = json.loads(config_path.read_text())
30
+ except FileNotFoundError:
31
+ return [f"STRUCTURAL: config file not found: {config_path}"]
32
+ except json.JSONDecodeError as e:
33
+ return [f"STRUCTURAL: config is not valid JSON: {e}"]
34
+
35
+ if not isinstance(data, dict):
36
+ return ["STRUCTURAL: top-level value must be a JSON object"]
37
+
38
+ for key, expected_type in REQUIRED_KEYS.items():
39
+ if key not in data:
40
+ problems.append(f"STRUCTURAL: missing required key '{key}'")
41
+ elif not isinstance(data[key], expected_type):
42
+ problems.append(
43
+ f"STRUCTURAL: key '{key}' must be {expected_type.__name__}, "
44
+ f"got {type(data[key]).__name__}"
45
+ )
46
+
47
+ features = data.get("features")
48
+ if features is not None and not isinstance(features, dict):
49
+ problems.append("STRUCTURAL: key 'features' must be an object")
50
+
51
+ return problems
52
+
53
+
54
+ def main() -> int:
55
+ if len(sys.argv) != 2:
56
+ print("usage: validate_config.py <config.json>", file=sys.stderr)
57
+ return 2
58
+ problems = validate(Path(sys.argv[1]))
59
+ if not problems:
60
+ print("STRUCTURAL: OK")
61
+ return 0
62
+ for line in problems:
63
+ print(line)
64
+ return 1
65
+
66
+
67
+ if __name__ == "__main__":
68
+ sys.exit(main())
@@ -3,6 +3,6 @@
3
3
  from importlib.metadata import PackageNotFoundError, version
4
4
 
5
5
  try:
6
- __version__ = version("skill-eval")
6
+ __version__ = version("agent-skill-eval")
7
7
  except PackageNotFoundError:
8
- __version__ = "0.1.0"
8
+ __version__ = "0.3.0"