11# Plan
22
3+ ## Type
4+
5+ refactor
6+
37## Goal
48
5- Redesign skill tests to use stored transcripts and grader agents for SPRT evaluation. Each test
6- scenario runs once and stores the raw transcript in a ` runs/ ` subdirectory (as defined by
7- 2.1-revise-instruction-builder-file-targets). Assertions are evaluated by new dedicated grader
8- agents that read transcripts rather than inline during agent execution. All existing test-cases.json
9- and test-results.json files are migrated to the new format. Agents replaced by the new grader
10- model (e.g., cat : skill-validator-agent ) are removed.
9+ Redesign skill tests to use stored transcripts and grader agents for SPRT evaluation. Each test scenario runs
10+ once and stores the raw transcript in a ` runs/ ` subdirectory (as defined by
11+ 2.1-revise-instruction-builder-file-targets). Assertions are evaluated by new dedicated grader agents that
12+ read transcripts rather than inline during agent execution. All existing test-cases.json and test-results .json
13+ files are migrated to the new format. Agents replaced by the new grader model (e.g., cat : skill-validator-agent )
14+ are removed.
1115
1216## Pre-conditions
1317
@@ -22,5 +26,263 @@ model (e.g., cat:skill-validator-agent) are removed.
2226- [ ] All existing test-results.json files replaced by results.json keyed by scenario filename and assertion name
2327- [ ] SPRT statistics tracked per assertion (not per file) in results.json
2428- [ ] ValidateSkillTestFormat hook updated to enforce new .md schema
25- - [ ] E2E verification: run a skill test end-to-end and confirm transcript stored in runs/, graded correctly, results.json updated
29+ - [ ] E2E verification: run a skill test end-to-end and confirm transcript stored in runs/, graded correctly,
30+ results.json updated
2631- [ ] Tests pass, no regressions
32+
33+ ## Research Findings
34+
35+ ### Current file inventory
36+
37+ ** test-cases.json files to migrate (10 files):**
38+
39+ | Old path | New target directory |
40+ | ----------| ---------------------|
41+ | ` plugin/skills/work-implement-agent/benchmark/test-cases.json ` | ` plugin/tests/skills/work-implement-agent/first-use/ ` |
42+ | ` plugin/skills/init/test/test-cases.json ` | ` plugin/tests/skills/init/first-use/ ` |
43+ | ` plugin/skills/git-squash-agent/benchmark/test-cases.json ` | ` plugin/tests/skills/git-squash-agent/first-use/ ` |
44+ | ` plugin/skills/stakeholder-review-agent/tests/test-cases.json ` | ` plugin/tests/skills/stakeholder-review-agent/first-use/ ` |
45+ | ` plugin/skills/cleanup/test/test-cases.json ` | ` plugin/tests/skills/cleanup/first-use/ ` |
46+ | ` plugin/skills/config/test/test-cases.json ` | ` plugin/tests/skills/config/first-use/ ` |
47+ | ` plugin/skills/get-output-agent/benchmark/test-cases.json ` | ` plugin/tests/skills/get-output-agent/first-use/ ` |
48+ | ` plugin/skills/github-trigger-workflow-agent/test/test-cases.json ` | ` plugin/tests/skills/github-trigger-workflow-agent/first-use/ ` |
49+ | ` plugin/skills/status-agent/benchmark/test-cases.json ` | ` plugin/tests/skills/status-agent/first-use/ ` |
50+ | ` plugin/skills/retrospective-agent/test/test-cases.json ` | ` plugin/tests/skills/retrospective-agent/first-use/ ` |
51+
52+ ** test-results.json files to migrate (3 files):**
53+
54+ | Old path | New results.json path |
55+ | ----------| -----------------------|
56+ | ` plugin/skills/stakeholder-review-agent/tests/test-results.json ` | ` plugin/tests/skills/stakeholder-review-agent/first-use/results.json ` |
57+ | ` plugin/skills/cleanup/test/test-results.json ` | ` plugin/tests/skills/cleanup/first-use/results.json ` |
58+ | ` plugin/skills/retrospective-agent/test/test-results.json ` | ` plugin/tests/skills/retrospective-agent/first-use/results.json ` |
59+
60+ ** Files/directories to remove:**
61+ - ` plugin/skills/skill-validator-agent/ ` (entire directory — superseded by grader model)
62+ - ` plugin/agents/skill-grader-agent.md ` (after skill created in plugin/skills/)
63+ - All source ` test-cases.json ` and ` test-results.json ` files listed above (after migration)
64+ - Empty ` test/ ` , ` tests/ ` , and ` benchmark/ ` directories left after migration
65+
66+ ** New skill to create:**
67+ - ` plugin/skills/skill-grader-agent/SKILL.md ` + ` first-use.md ` (from plugin/agents/skill-grader-agent.md content)
68+
69+ ### New directory convention
70+
71+ Tests for a file at ` plugin/<path>/<name>.md ` go to ` plugin/tests/<path>/<name>/ ` .
72+ Results file lives at ` plugin/tests/<path>/<name>/results.json ` .
73+ Transcripts from runs go to ` plugin/tests/<path>/<name>/runs/ ` .
74+
75+ ### New .md scenario file format
76+
77+ ``` markdown
78+ ---
79+ type: behavior
80+ category: <lowercase-category >
81+ ---
82+ ## Scenario
83+
84+ <test prompt / scenario description>
85+
86+ ## Tier 1 Assertion
87+
88+ <primary assertion — the single most discriminating check>
89+
90+ ## Tier 2 Assertion
91+
92+ <secondary assertion — a supporting behavioral property>
93+ ```
94+
95+ ### Filename derivation from test-cases.json entries
96+
97+ Filename = ` semantic_unit_id ` with ` unit_ ` prefix stripped, underscores → hyphens.
98+ If ` semantic_unit_id ` is absent, use ` test_case_id ` lowercased, underscores → hyphens.
99+
100+ Examples:
101+ - ` unit_step4_no_dir_delete ` → ` step4-no-dir-delete.md `
102+ - ` unit_routing_analysis_action ` → ` routing-analysis-action.md `
103+
104+ ### Category mapping (old uppercase → new lowercase)
105+
106+ | Old value | New value |
107+ | -----------| -----------|
108+ | ` SEQUENCE ` | ` sequence ` |
109+ | ` CONDITIONAL ` | ` conditional ` |
110+ | ` REQUIREMENT ` | ` requirement ` |
111+ | ` CONSEQUENCE ` | ` consequence ` |
112+ | ` PROHIBITION ` | ` requirement ` |
113+
114+ ### Type mapping
115+
116+ All existing test cases use behavioral prompts (simulating agent execution). Map all to ` type: behavior ` .
117+
118+ ### Assertion mapping to Tier 1 / Tier 2
119+
120+ Each test case's ` assertions ` array maps as follows:
121+ - ** Tier 1** : Text of the first ` semantic ` assertion's ` description ` field. If no semantic assertions, use first
122+ assertion's ` description ` .
123+ - ** Tier 2** : Text of the second assertion's ` description ` . If only one assertion, write a reasonable secondary
124+ assertion that tests an absence or supporting property derived from the scenario context.
125+
126+ Use the assertion ` description ` field as the tier text (not ` instruction ` — ` description ` is concise and
127+ assertion-focused).
128+
129+ ### New results.json schema (per-assertion SPRT)
130+
131+ ``` json
132+ {
133+ "skill_hash" : " <SHA-256 of first-use.md at run time>" ,
134+ "model" : " <model identifier>" ,
135+ "session_id" : " <CLAUDE_SESSION_ID>" ,
136+ "timestamp" : " <ISO-8601 UTC timestamp>" ,
137+ "overall_decision" : " pass|fail|inconclusive" ,
138+ "scenarios" : {
139+ "<scenario-filename-without-extension>" : {
140+ "type" : " behavior" ,
141+ "tier_1" : {
142+ "log_ratio" : 0.0 ,
143+ "pass_count" : 0 ,
144+ "fail_count" : 0 ,
145+ "total_runs" : 0 ,
146+ "total_tokens" : 0 ,
147+ "total_duration_ms" : 0 ,
148+ "decision" : " inconclusive"
149+ },
150+ "tier_2" : {
151+ "log_ratio" : 0.0 ,
152+ "pass_count" : 0 ,
153+ "fail_count" : 0 ,
154+ "total_runs" : 0 ,
155+ "total_tokens" : 0 ,
156+ "total_duration_ms" : 0 ,
157+ "decision" : " inconclusive"
158+ }
159+ }
160+ }
161+ }
162+ ```
163+
164+ ### Migrating existing test-results.json to new format
165+
166+ For each entry in ` sprt.test_cases ` , find the corresponding new scenario filename (using the semantic_unit_id
167+ mapping above). Map the per-case data to tier_1 (primary assertion). Set tier_2 to all zeros/inconclusive.
168+
169+ Old field → new location:
170+ - ` log_ratio ` → ` scenarios.<name>.tier_1.log_ratio `
171+ - ` passes ` → ` scenarios.<name>.tier_1.pass_count `
172+ - ` failures ` → ` scenarios.<name>.tier_1.fail_count `
173+ - ` runs ` → ` scenarios.<name>.tier_1.total_runs `
174+ - ` total_tokens ` → ` scenarios.<name>.tier_1.total_tokens `
175+ - ` total_duration_ms ` → ` scenarios.<name>.tier_1.total_duration_ms `
176+ - Derive ` decision ` from ` log_ratio ` vs SPRT thresholds (accept if log_ratio >= accept_boundary, reject if <=
177+ reject_boundary, else inconclusive)
178+
179+ Top-level fields: copy ` skill_hash ` , ` model ` , ` session_id ` , ` timestamp ` , ` overall_decision ` from old format.
180+
181+ ### ValidateSkillTestFormat hook path pattern change
182+
183+ ** Current regex:** ` (?:^|/)plugin/skills/[^/]+/test/[^/]+\.md$ `
184+ ** New regex:** ` (?:^|/)plugin/tests/.+\.md$ `
185+
186+ Only change is the path portion — format validation logic (frontmatter, sections) stays the same.
187+
188+ Also update ` ValidateSkillTestFormatTest.java ` to use the new path pattern in test cases.
189+
190+ ### cat: skill-grader-agent skill structure
191+
192+ Create from existing ` plugin/agents/skill-grader-agent.md ` :
193+
194+ ** ` plugin/skills/skill-grader-agent/SKILL.md ` :**
195+ ```
196+ ---
197+ description: >
198+ Internal subagent — grades a list of assertions against a single test-case output, assigning pass/fail
199+ verdicts with evidence quotes. Reads run output via git show, commits grading JSON, returns commit SHA.
200+ model: haiku
201+ user-invocable: false
202+ ---
203+
204+ !`"${CLAUDE_PLUGIN_ROOT}/client/bin/get-skill" skill-grader-agent "$0"`
205+ ```
206+
207+ ** ` plugin/skills/skill-grader-agent/first-use.md ` :**
208+ Copy content from ` plugin/agents/skill-grader-agent.md ` (the body below the frontmatter), adding license header.
209+
210+ ### plugin/concepts/skill-test.md updates
211+
212+ Add a ` ## Runs Directory ` section after the ` ## Directory Convention ` section documenting:
213+ - ` runs/ ` subdirectory location: ` plugin/tests/<path>/<name>/runs/ `
214+ - Transcript file naming: ` <scenario-slug>-<run-id>.md ` (run-id is a short unique identifier per execution)
215+ - Transcripts are written by the test runner before grading
216+ - Graders read transcripts from runs/ via ` git show ` or direct file access
217+
218+ Update ` ## Directory Convention ` to show the ` plugin/tests/ ` path (not ` plugin/skills/<skill>/test/ ` ):
219+ ```
220+ plugin/tests/<path>/<name>/
221+ <scenario-slug>.md (one file per test case)
222+ results.json (written after a test run; see plugin/concepts/skill-test-results.md)
223+ runs/
224+ <scenario-slug>-<run-id>.md (raw transcript per run)
225+ ```
226+
227+ ### plugin/concepts/skill-test-results.md updates
228+
229+ Replace ` test_cases ` array schema with ` scenarios ` object schema per the new results.json format above.
230+
231+ Update ` ## File Location ` to:
232+ ```
233+ plugin/tests/<path>/<name>/results.json
234+ ```
235+
236+ Update the schema description to reflect per-assertion SPRT (tier_1 and tier_2 objects per scenario).
237+
238+ ## Jobs
239+
240+ ### Job 1
241+
242+ Independent of all other jobs. Modify concept docs and create/remove skill files.
243+
244+ - Modify ` plugin/concepts/skill-test.md ` :
245+ - Change ` ## Directory Convention ` code block to show ` plugin/tests/<path>/<name>/ ` structure with
246+ ` runs/ ` subdirectory
247+ - Add ` ## Runs Directory ` section after ` ## Directory Convention ` documenting transcript file naming
248+ and how graders read them
249+ - Modify ` plugin/concepts/skill-test-results.md ` :
250+ - Update ` ## File Location ` to ` plugin/tests/<path>/<name>/results.json `
251+ - Replace ` test_cases ` array entries table with ` scenarios ` object schema matching the new JSON structure
252+ (showing tier_1 and tier_2 keys with their sub-fields)
253+ - Create ` plugin/skills/skill-grader-agent/SKILL.md ` with frontmatter as shown in Research Findings and
254+ ` ! ` "${CLAUDE_PLUGIN_ROOT}/client/bin/get-skill" skill-grader-agent "$0"`` directive
255+ - Create ` plugin/skills/skill-grader-agent/first-use.md ` by copying the body from
256+ ` plugin/agents/skill-grader-agent.md ` and adding the license header comment block
257+ - Remove ` plugin/agents/skill-grader-agent.md ` (content moved to skill)
258+ - Remove ` plugin/skills/skill-validator-agent/ ` directory (entire directory)
259+
260+ ### Job 2
261+
262+ Migrate all test-cases.json files. Does not overlap with Job 1, Job 3, or Job 4.
263+
264+ For each of the 10 test-cases.json files listed in Research Findings:
265+ 1 . Read the JSON file
266+ 2 . For each test case entry, derive the filename from ` semantic_unit_id ` (strip ` unit_ ` prefix, replace ` _ ` with ` - ` )
267+ 3 . Map ` category ` (uppercase → lowercase) and set ` type: behavior `
268+ 4 . Map assertions to Tier 1 / Tier 2 per the assertion mapping rules
269+ 5 . Write the ` .md ` file to the target directory shown in Research Findings
270+ 6 . Add license header to each new .md file
271+ 7 . After creating all .md files for a test-cases.json, delete the test-cases.json file
272+ 8 . Remove empty ` test/ ` , ` tests/ ` , or ` benchmark/ ` directories left behind
273+
274+ ### Job 3
275+
276+ Migrate test-results.json files, update ValidateSkillTestFormat, and update Java tests.
277+ Does not overlap with Job 1 or Job 2 (different files).
278+
279+ - For each of the 3 test-results.json files, convert to the new results.json format per Research Findings
280+ (scenarios object with per-tier SPRT data) and write to the new plugin/tests/ location
281+ - In ` client/src/main/java/io/github/cowwoc/cat/hooks/write/ValidateSkillTestFormat.java ` :
282+ - Change ` TEST_MD_PATTERN ` from ` (?:^|/)plugin/skills/[^/]+/test/[^/]+\.md$ `
283+ to ` (?:^|/)plugin/tests/.+\.md$ `
284+ - In ` client/src/test/java/io/github/cowwoc/cat/hooks/test/ValidateSkillTestFormatTest.java ` :
285+ - Update any test file paths that use the old ` plugin/skills/<skill>/test/ ` pattern to use
286+ ` plugin/tests/<path>/<name>/ ` paths
287+ - Run ` mvn -f client/pom.xml verify -e ` and fix any build failures
288+ - Update ` index.json ` to ` status: closed ` in the SAME commit as the last implementation commit
0 commit comments