sdlc-eval
Invoked explicitly by /eval. For AI features, builds or runs the golden-set evaluation harness for a prompt version, records the quality metrics and cost as a baseline, and compares against the previo
Files1
View on GitHubSKILL.md.claude/skills/sdlc-eval/SKILL.md
GitHub/eval — prompt evaluation
The only command in the framework that deliberately spends money. Everything else reads the results this one records.
Protocol (shared — do not restate here)
Framework assets — .agent/framework.yaml, .agent/steps/, .agent/rules/,
.agent/gates/, .agent/templates/, .agent/modules/ — resolve project-first: use
the repository's copy when it exists, otherwise ${CLAUDE_PLUGIN_ROOT}/.agent/…. That is
how one repo can override a single template or rule without forking the framework.
.agent/project-context.yaml and .agent/state/ are always project-local — never
read or write them under the plugin root.
- If
.agent/state/context-cache.mdexists and itsframework_versionmatches.agent/framework.yaml, Read ONLY that file. Otherwise Read.agent/steps/context-loader.mdand execute it, then write the digest back to the cache. - Read
.agent/steps/gate.mdand execute it with the phase inputs below. - On completion, format output per
.agent/steps/report-footer.md. Never copy the contents of those files into this skill.
Phase inputs — feeds gate G5b. Inputs {{paths.eval_spec_dir}}/EVAL-{name}.md,
{{paths.eval_data_dir}}/{name}/{version}/cases.jsonl, the registry version file.
Outputs .agent/state/evals/{name}@{version}.json and the updated prompt metadata.
This command costs money, so gate Step 4 (CHECKPOINT) always applies and must state the
case count and the estimated spend.
Step 1 — Input check
There is no input_contracts entry for /eval — it is not a phase — so check these
directly. Stop with a clear message if any is missing, rather than evaluating something
undefined. List what is missing; do not ask for it.
- the feature's
ai_classisnone→ this command does not apply - no
EVAL-{name}.md→ run/techdocfirst; thresholds are a design decision - no
cases.jsonlfor this version → run/gen-testcasefirst - the registry version file does not exist → run
/gen-codefirst
Read the eval spec's metrics table, its thresholds, its judge rubric, and the baselines table.
Step 2 — Resolve the version and the baseline
Default to the newest version in the registry for that prompt. The baseline is the
previous version's recorded run in .agent/state/evals/, or the version named in the
spec's baselines table.
If no baseline exists, this run becomes the baseline. Say so explicitly — a first run cannot detect regression, and reporting it as "no regression" would be misleading.
Step 3 — Run the suite
Run quality_commands.evals for this prompt and version.
Per the oracle ladder in .agent/modules/ai-llm/stack-profile.yaml:
- Tier 1 structural — schema validity, required fields, token and length caps, banned strings, refusal detection, latency. These assert hard, per case, even inside an eval. A case with no structural expectation cannot fail deterministically and therefore protects nothing.
- Tier 2 reference — exact match for classification and extraction; similarity
against
expect.referenceotherwise. Recorded per case, gated on the suite pass rate. - Tier 3 judge — rubric scored 1–5 against the written anchors. Recorded per case, gated only on regression versus baseline.
Run judge and similarity cases n=3 and take the median, recording the variance. A case whose variance exceeds the spec's threshold is flagged as a bad eval case, not a bad prompt — an unstable case adds noise to every future comparison, so it is fixed or retired rather than tolerated.
Capture per case: outcome, scores, tokens in and out, latency, cost.
Step 4 — Aggregate and compare
{
"prompt": "summarize@1.2.0",
"at": "<ISO8601>",
"cases": { "total": 42, "passed": 39, "failed": 3 },
"pass_rate": 0.929,
"by_origin": { "golden": 0.95, "adversarial": 0.83, "regression": 1.0 },
"judge": { "faithfulness": 4.3, "coverage": 3.9, "variance_flags": ["EV-summarize-017"] },
"cost": { "p50_input_tokens": 1840, "p95_output_tokens": 190,
"cost_per_call_usd": 0.0031, "p95_latency_ms": 3120, "total_run_usd": 0.13 },
"baseline": "summarize@1.1.0",
"delta": { "pass_rate": 0.01, "judge_faithfulness": -0.1 },
"verdict": "pass | regression | below_threshold"
}
Verdict rules from the spec, defaults from ai_conventions.oracles:
- below_threshold — suite pass rate under the spec's minimum
- regression — judge mean dropped more than the tolerance (default 0.3), or pass rate dropped more than the tolerance (default 5 percentage points)
- pass — neither
Regression on by_origin.regression is always a blocker, whatever the aggregate
says. Those cases exist because something already failed once; letting one of them break
while the average holds is exactly how a fixed bug comes back.
Step 5 — Record
Write .agent/state/evals/{name}@{version}.json, add a row to the spec's baselines
table, add a row to the prompt spec's version-history table, and update the registry
file's metadata — eval_set, eval_score, eval_run.
metadata is the only part of a published version file that may be written after
publication, because it describes the file rather than changing its behaviour. The
template and rules do not change.
With --baseline, also mark this run as the comparison point for the next version.
Step 6 — Report
State pass rate, judge means, cost per call, p95 latency, the delta versus baseline, and the verdict. List the failed cases with their ids and what they expected — an aggregate number with no failing examples is not actionable.
On regression or below_threshold, write a finding routed to prompt (quality
dropped, contract unchanged) or techdoc (the contract or the model must change).
Boundaries
- Do not edit the prompt to make the eval pass. A prompt change is a new version,
decided in
/techdocand written by/gen-code. Tuning a prompt against its own eval inside the eval command is overfitting with extra steps. - Do not add or remove eval cases here.
/gen-testcaseauthors them. Deleting the case that fails is the most direct way to make an eval suite meaningless. - Do not renumber case ids — the baseline comparison depends on them being stable.
- Do not lower a threshold to pass. That is a design decision with an ADR.
- Do not run automatically as part of another command. The cost must always be a deliberate choice.