Evaluator
A second, independent layer of validation. After implement-feature finishes a feature, the evaluator looks at it from the outside and confirms — against contract.md — whether the delivery conforms. It finds deviations the implementer's self-check missed. It does not fix code; it evaluates and orchestrates the correction loop, dispatching fix-runner on failure.
Base docs: https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Skill_Evaluator.md (full rationale),
https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Contrato_de_Feature.md (contract structure),
https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Como_criar_gates.md (gates),
https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Fluxo_SDD_e_Implementacao_das_Skills.md (flow, states, progress.json schema).
Report schema: references/evaluation-report-schema.md.
The evaluator complements automated tests — it does not replace them.
INPUT
Free-form. The skill needs:
- The target feature (ID/name) — explicit and required. Abort if absent or ambiguous (list candidates).
- Auto-discovers:
progress.json (root of docs/) and the feature's contract.md (in docs/<feature-id>-<kebab>/).
- Optional free-form overrides at the end — e.g. "no screenshots", "gates only", "max 5 attempts" (overrides
maxFixAttempts for this run).
If the feature has no contract.md, abort: "No contract.md for F — generate it with spec-writer first."
OUTPUT
- Screenshots — visual evidence of observable UI criteria (when applicable).
- Report — consolidated ✓ / ✗ / — per contract criterion and gate.
- Findings in chat — textual summary for the user.
- State in
progress.json — CLEAN | FAIL | PENDING | ABORTED for the feature.
- On FAIL: a structured
evaluation-report.json in the feature folder, with each failure classified by kind (consumed by fix-runner for code, or a test-writer for tests).
- The evaluator does not edit code or tests itself. Code corrections →
fix-runner; test corrections → the matching test-writer (confirmed by the matching test-validator).
EXECUTION STEPS
Step 1: Resolve Input
- Resolve the target feature (ID/name). Abort if absent or ambiguous, listing candidates.
- Locate
progress.json (root of docs/) and the feature's contract.md. If contract.md is missing → abort ("generate the contract first with spec-writer"). If progress.json is missing, create it with config.maxFixAttempts default 3.
- Parse any overrides (e.g.
max N attempts, gates only, no screenshots) and record them for the report.
Step 2: Load Context
- Read
contract.md: Environment Contract, Quality Gates (with ids), Coverage Manifest, Surfaces & Behaviors, Observable Criteria (with ids).
- Read
progress.json: this feature's state, attempt, and config.maxFixAttempts (N).
- The contract is the single source of acceptance criteria. Do NOT invent criteria outside it.
Step 3: Verify Environment Contract
- Check each environment prerequisite (runtime up, services reachable, tools available — e.g. dev server serving
/, Playwright CLI present).
- If the environment is not satisfied → do NOT proceed to evaluation. Set
state: PENDING (human/environment intervention needed), explain that this is an environment problem (not an implementation failure), and stop. Environment-invalid ≠ implementation-wrong.
Step 4: Run Quality Gates
- Execute each gate declared in the contract (typecheck, lint, build, tests, arch...). Use the exact commands from the contract.
- Any gate failing is a contract violation. Collect the command + log as evidence.
- Classify each failure by cause (this drives the correction routing in Step 7):
- The failure is a code failure (
kind: gate or observable-criterion) when production code is wrong — a type error, lint/build/arch violation, or a missing observable behavior.
- The failure is a test failure (
kind: test) when the test itself is broken/non-conforming — e.g. the tests gate fails and the cause is the test file (missing/incorrect mock, wrong pattern, a test that no longer matches correct behavior), not the production code. For a kind: test failure, fill the routing fields (testSuite, testFile, targetFile) per references/evaluation-report-schema.md.
- When ambiguous (a failing test that might reflect a real code bug), prefer
kind: gate/observable-criterion and let fix-runner handle the code; only route to a test-writer when the test is clearly the thing that is wrong.
- (If
gates only override is set, skip Step 5 and go to Step 6 with just gate results.)
Step 5: Validate Surfaces & Observable Criteria
- For each surface in the Coverage Manifest, start from its declared initial state and exercise the concrete behaviors (e.g. navigate the route via Playwright CLI, capturing screenshots).
- For each Observable Criterion, collect verifiable evidence (e.g. CTA present, login in top nav, redirect behavior, visual identity). A criterion with no observable evidence is a failure (
kind: observable-criterion, ref: <crit-id>).
- Map every PRD-derived acceptance back to a contract criterion/gate (the contract already did this traceability; honor it).
Step 6: Decide State
Determine the feature's state strictly from contract adherence — never from "looks ok":
- CLEAN — no failures: all gates pass, all observable criteria met. → record state, proceed to Step 9.
- FAIL — at least one correctable failure (gate/test/observable). → go to Step 7 (loop).
- PENDING — something the evaluator cannot test by itself (needs a human, or an environment it cannot bring up). → record state with a note, proceed to Step 9.
- ABORTED — decided in Step 7 when attempts are exhausted.
List exactly which gates/criteria failed.
Step 7: Correction Loop (when FAIL) — route by failure kind
- Read
attempt and maxFixAttempts (N) from progress.json.
- If
attempt >= N → set state: ABORTED; stop and report (the loop tried N times without converging). Proceed to Step 9.
- Else:
- Write/refresh
evaluation-report.json in the feature folder (schema in references/evaluation-report-schema.md) with the current attempt and the classified failures[].
- Set
state: FAIL in progress.json and persist the report path in lastEvaluationReport.
- Route each failure by
kind:
kind: gate / observable-criterion (code) → dispatch fix-runner, passing the feature ID and the report path. Unchanged behavior — the on-disk report is the source of truth.
kind: test → run the test-correction sub-flow (Step 8) for that failure. Never send test failures to fix-runner.
- When the dispatched correction returns:
- Correction applied → increment
attempt in progress.json, then re-evaluate: go back to Step 3.
- "not resolved — " → still increment
attempt; if attempt >= N set ABORTED, else re-evaluate. Do not loop without incrementing.
The evaluator owns the counter, the limit N, and the ABORTED decision. Correction skills are stateless and never decide when to stop.
Step 8: Test-correction sub-flow (kind: test)
For a test failure, correcting the code is the wrong move — fix the test, then re-confirm it
conforms. Select the suite from testSuite/testFile (deterministic rule in the schema):
unit → unit-test-*, integration → integration-test-*, monorepo → monorepo-unit-test-*.
- Fix the test — dispatch the matching test-writer in correction mode (autonomous),
passing the feature ID, the
evaluation-report.json path, testFile, and targetFile. It
fixes only the flagged test (smallest footprint), never production code.
- Confirm conformance — dispatch the matching test-validator on the corrected
testFile.
- Verdict PASS (or PASS WITH WARNINGS) → the test now conforms; resume the evaluation
where it left off (re-run Step 3+ / the failing gate) and continue.
- Verdict FAIL → the correction did not conform. Treat this round as a spent attempt:
increment
attempt; if attempt >= N → ABORTED; else loop (dispatch the test-writer again
with the validator's findings, then re-validate).
- Only after the test-validator returns PASS does the evaluator continue its own evaluation.
The test-writer/validator pair is a sub-loop inside the evaluator's main loop. It still
consumes the single attempt/N budget — never iterate the sub-loop without incrementing.
Step 9: Persist State & Report
- Write the final
state (CLEAN | PENDING | ABORTED) and updatedAt for the feature in progress.json. Preserve config and other features' entries (merge, don't replace).
- On CLEAN, you may clear or keep
lastEvaluationReport (a stale FAIL report should not imply a current failure — prefer clearing it).
- Output the report to chat:
Feature F<ID> — <name>
State: CLEAN | FAIL→(looped) | PENDING | ABORTED
Attempts used: <attempt> / <maxFixAttempts>
Quality gates:
✓ <gate-id> passed
✗ <gate-id> failed: <message> (<command>)
Observable criteria:
✓ <crit-id> — <evidence / screenshot path>
✗ <crit-id> — <what was missing>
Surfaces evaluated:
- <surface-id>: <result>
Environment:
✓ contract met | ✗ not met → PENDING (<which prerequisite>)
Findings:
- <key deviations the evaluation found>
Next:
- CLEAN → feature ready for the next stage
- PENDING → needs human intervention: <what>
- ABORTED → tried <N> fixes without converging; see last evaluation-report.json
RULES
Always:
- Treat
contract.md as the single source of acceptance criteria.
- Abort the evaluation if the Environment Contract is not met, and say it is an environment problem (→ PENDING).
- Run the contract's Quality Gates as objective checks before/with functional inspection.
- Derive the state from contract adherence, with evidence per criterion.
- Own the loop: keep
attempt/maxFixAttempts in progress.json and decide CLEAN/FAIL/PENDING/ABORTED.
- Classify each failure by kind and route it:
gate/observable-criterion → fix-runner; test → the matching test-writer (then confirmed by the matching test-validator).
- After a
kind: test correction, require the test-validator PASS before resuming the evaluation.
- Re-evaluate after each correction until CLEAN, PENDING, or ABORTED.
- Increment
attempt once per correction round (code or test); never loop without incrementing.
Never:
- Alter production code or "fix" the feature yourself — code corrections are the
fix-runner's job, test corrections are the test-writers' job.
- Send a
kind: test failure to fix-runner, or a kind: gate/observable-criterion failure to a test-writer.
- Approve based on visual perception without running the objective gates.
- Evaluate a feature without a
contract.md.
- Invent criteria not present in the contract.
- Exceed
maxFixAttempts without marking ABORTED (the test sub-loop shares the same budget).
- Write
PENDING_EVALUATION into progress.json — that is the implementer's pre-state; the evaluator writes CLEAN/FAIL/PENDING/ABORTED.
- Confuse an environment failure (PENDING) with an implementation failure (FAIL).
Edge Cases
No contract.md for the feature: abort and direct the user to spec-writer.
progress.json missing: create it with config.maxFixAttempts = 3 and this feature's entry.
Environment Contract not met: state = PENDING, clearly labeled as environment, no fix-runner dispatch.
Gate command not runnable in this environment (e.g. missing toolchain): treat as PENDING for that gate (needs environment), not FAIL — do not send the fix-runner after an environment gap. Note it in the report.
maxFixAttempts reached: state = ABORTED; keep the last evaluation-report.json for inspection.
fix-runner reports "not resolved": increment attempt; abort to ABORTED if the limit is hit, otherwise re-evaluate.
Override max N attempts: use N for maxFixAttempts this run (and persist it to config if the user intends it to stick — otherwise apply for the session only and note it).
Observable criterion is runtime-only and the runtime can't be exercised: PENDING for that criterion (human/environment), not FAIL.
Feature already CLEAN in progress.json: re-running is allowed (idempotent re-check); report the result and refresh state.
Cross-feature criteria: if the contract references behavior provided by another feature, evaluate only this feature's surface; note cross-feature dependencies in findings rather than failing on another feature's gap.
1---2name: evaluator3description: Evaluator4---56# Evaluator78A second, independent layer of validation. After `implement-feature` finishes a feature, the `evaluator` looks at it **from the outside** and confirms — against `contract.md` — whether the delivery conforms. It finds deviations the implementer's self-check missed. It **does not fix code**; it evaluates and **orchestrates the correction loop**, dispatching `fix-runner` on failure.910> Base docs: `https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Skill_Evaluator.md` (full rationale),11> `https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Contrato_de_Feature.md` (contract structure),12> `https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Como_criar_gates.md` (gates),13> `https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Fluxo_SDD_e_Implementacao_das_Skills.md` (flow, states, progress.json schema).14> Report schema: `references/evaluation-report-schema.md`.1516The evaluator complements automated tests — it does not replace them.1718## INPUT1920Free-form. The skill needs:2122- **The target feature** (ID/name) — explicit and required. Abort if absent or ambiguous (list candidates).23- Auto-discovers: `progress.json` (root of `docs/`) and the feature's `contract.md` (in `docs/<feature-id>-<kebab>/`).24- Optional free-form overrides at the end — e.g. "no screenshots", "gates only", "max 5 attempts" (overrides `maxFixAttempts` for this run).2526If the feature has no `contract.md`, abort: "No contract.md for F<ID> — generate it with `spec-writer` first."2728## OUTPUT2930- **Screenshots** — visual evidence of observable UI criteria (when applicable).31- **Report** — consolidated ✓ / ✗ / — per contract criterion and gate.32- **Findings in chat** — textual summary for the user.33- **State in `progress.json`** — CLEAN | FAIL | PENDING | ABORTED for the feature.34- **On FAIL:** a structured `evaluation-report.json` in the feature folder, with each failure classified by `kind` (consumed by `fix-runner` for code, or a test-writer for tests).35- The evaluator does **not** edit code or tests itself. Code corrections → `fix-runner`; test corrections → the matching test-writer (confirmed by the matching test-validator).3637---3839## EXECUTION STEPS4041### Step 1: Resolve Input4243- Resolve the target feature (ID/name). Abort if absent or ambiguous, listing candidates.44- Locate `progress.json` (root of `docs/`) and the feature's `contract.md`. If `contract.md` is missing → abort ("generate the contract first with `spec-writer`"). If `progress.json` is missing, create it with `config.maxFixAttempts` default 3.45- Parse any overrides (e.g. `max N attempts`, `gates only`, `no screenshots`) and record them for the report.4647### Step 2: Load Context4849- Read `contract.md`: Environment Contract, Quality Gates (with `id`s), Coverage Manifest, Surfaces & Behaviors, Observable Criteria (with `id`s).50- Read `progress.json`: this feature's `state`, `attempt`, and `config.maxFixAttempts` (N).51- The contract is the **single source of acceptance criteria**. Do NOT invent criteria outside it.5253### Step 3: Verify Environment Contract5455- Check each environment prerequisite (runtime up, services reachable, tools available — e.g. dev server serving `/`, Playwright CLI present).56- If the environment is **not** satisfied → do NOT proceed to evaluation. Set `state: PENDING` (human/environment intervention needed), explain that this is an **environment** problem (not an implementation failure), and stop. Environment-invalid ≠ implementation-wrong.5758### Step 4: Run Quality Gates5960- Execute each gate declared in the contract (typecheck, lint, build, tests, arch...). Use the exact commands from the contract.61- Any gate failing is a contract violation. Collect the command + log as evidence.62- **Classify each failure by cause** (this drives the correction routing in Step 7):63 - The failure is a **code failure** (`kind: gate` or `observable-criterion`) when production code is wrong — a type error, lint/build/arch violation, or a missing observable behavior.64 - The failure is a **test failure** (`kind: test`) when the test itself is broken/non-conforming — e.g. the `tests` gate fails and the cause is the test file (missing/incorrect mock, wrong pattern, a test that no longer matches correct behavior), not the production code. For a `kind: test` failure, fill the routing fields (`testSuite`, `testFile`, `targetFile`) per `references/evaluation-report-schema.md`.65 - When ambiguous (a failing test that might reflect a real code bug), prefer `kind: gate`/`observable-criterion` and let `fix-runner` handle the code; only route to a test-writer when the test is clearly the thing that is wrong.66- (If `gates only` override is set, skip Step 5 and go to Step 6 with just gate results.)6768### Step 5: Validate Surfaces & Observable Criteria6970- For each surface in the Coverage Manifest, start from its declared initial state and exercise the concrete behaviors (e.g. navigate the route via Playwright CLI, capturing screenshots).71- For each Observable Criterion, collect verifiable evidence (e.g. CTA present, login in top nav, redirect behavior, visual identity). A criterion with no observable evidence is a failure (`kind: observable-criterion`, `ref: <crit-id>`).72- Map every PRD-derived acceptance back to a contract criterion/gate (the contract already did this traceability; honor it).7374### Step 6: Decide State7576Determine the feature's state strictly from contract adherence — never from "looks ok":7778- **CLEAN** — no failures: all gates pass, all observable criteria met. → record state, proceed to Step 9.79- **FAIL** — at least one **correctable** failure (gate/test/observable). → go to Step 7 (loop).80- **PENDING** — something the evaluator **cannot test by itself** (needs a human, or an environment it cannot bring up). → record state with a note, proceed to Step 9.81- **ABORTED** — decided in Step 7 when attempts are exhausted.8283List exactly which gates/criteria failed.8485### Step 7: Correction Loop (when FAIL) — route by failure kind8687- Read `attempt` and `maxFixAttempts` (N) from `progress.json`.88- **If `attempt >= N`** → set `state: ABORTED`; stop and report (the loop tried N times without converging). Proceed to Step 9.89- **Else:**90 1. Write/refresh `evaluation-report.json` in the feature folder (schema in `references/evaluation-report-schema.md`) with the current `attempt` and the classified `failures[]`.91 2. Set `state: FAIL` in `progress.json` and persist the report path in `lastEvaluationReport`.92 3. **Route each failure by `kind`:**93 - **`kind: gate` / `observable-criterion` (code)** → **dispatch `fix-runner`**, passing the feature ID and the report path. Unchanged behavior — the on-disk report is the source of truth.94 - **`kind: test`** → run the **test-correction sub-flow (Step 8)** for that failure. Never send test failures to `fix-runner`.95 4. When the dispatched correction returns:96 - Correction applied → **increment `attempt`** in `progress.json`, then **re-evaluate**: go back to Step 3.97 - "not resolved — <reason>" → still increment `attempt`; if `attempt >= N` set `ABORTED`, else re-evaluate. Do not loop without incrementing.9899The evaluator **owns** the counter, the limit N, and the ABORTED decision. Correction skills are stateless and never decide when to stop.100101### Step 8: Test-correction sub-flow (`kind: test`)102103For a test failure, correcting the code is the wrong move — fix the test, then re-confirm it104conforms. Select the suite from `testSuite`/`testFile` (deterministic rule in the schema):105`unit` → `unit-test-*`, `integration` → `integration-test-*`, `monorepo` → `monorepo-unit-test-*`.1061071. **Fix the test** — dispatch the matching **test-writer** in **correction mode** (autonomous),108 passing the feature ID, the `evaluation-report.json` path, `testFile`, and `targetFile`. It109 fixes only the flagged test (smallest footprint), never production code.1102. **Confirm conformance** — dispatch the matching **test-validator** on the corrected `testFile`.111 - Verdict **PASS** (or PASS WITH WARNINGS) → the test now conforms; **resume the evaluation112 where it left off** (re-run Step 3+ / the failing gate) and continue.113 - Verdict **FAIL** → the correction did not conform. Treat this round as a spent attempt:114 increment `attempt`; if `attempt >= N` → `ABORTED`; else loop (dispatch the test-writer again115 with the validator's findings, then re-validate).1163. Only after the test-validator returns PASS does the evaluator continue its own evaluation.117118> The test-writer/validator pair is a **sub-loop inside** the evaluator's main loop. It still119> consumes the single `attempt`/N budget — never iterate the sub-loop without incrementing.120121### Step 9: Persist State & Report122123- Write the final `state` (CLEAN | PENDING | ABORTED) and `updatedAt` for the feature in `progress.json`. Preserve `config` and other features' entries (merge, don't replace).124- On CLEAN, you may clear or keep `lastEvaluationReport` (a stale FAIL report should not imply a current failure — prefer clearing it).125- Output the report to chat:126127```128Feature F<ID> — <name>129130State: CLEAN | FAIL→(looped) | PENDING | ABORTED131Attempts used: <attempt> / <maxFixAttempts>132133Quality gates:134✓ <gate-id> passed135✗ <gate-id> failed: <message> (<command>)136137Observable criteria:138✓ <crit-id> — <evidence / screenshot path>139✗ <crit-id> — <what was missing>140141Surfaces evaluated:142- <surface-id>: <result>143144Environment:145✓ contract met | ✗ not met → PENDING (<which prerequisite>)146147Findings:148- <key deviations the evaluation found>149150Next:151- CLEAN → feature ready for the next stage152- PENDING → needs human intervention: <what>153- ABORTED → tried <N> fixes without converging; see last evaluation-report.json154```155156---157158## RULES159160**Always:**161- Treat `contract.md` as the single source of acceptance criteria.162- Abort the evaluation if the Environment Contract is not met, and say it is an environment problem (→ PENDING).163- Run the contract's Quality Gates as objective checks before/with functional inspection.164- Derive the state from contract adherence, with evidence per criterion.165- Own the loop: keep `attempt`/`maxFixAttempts` in `progress.json` and decide CLEAN/FAIL/PENDING/ABORTED.166- Classify each failure by kind and route it: `gate`/`observable-criterion` → `fix-runner`; `test` → the matching test-writer (then confirmed by the matching test-validator).167- After a `kind: test` correction, require the **test-validator PASS** before resuming the evaluation.168- Re-evaluate after each correction until CLEAN, PENDING, or ABORTED.169- Increment `attempt` once per correction round (code or test); never loop without incrementing.170171**Never:**172- Alter production code or "fix" the feature yourself — code corrections are the `fix-runner`'s job, test corrections are the test-writers' job.173- Send a `kind: test` failure to `fix-runner`, or a `kind: gate`/`observable-criterion` failure to a test-writer.174- Approve based on visual perception without running the objective gates.175- Evaluate a feature without a `contract.md`.176- Invent criteria not present in the contract.177- Exceed `maxFixAttempts` without marking ABORTED (the test sub-loop shares the same budget).178- Write `PENDING_EVALUATION` into `progress.json` — that is the implementer's pre-state; the evaluator writes CLEAN/FAIL/PENDING/ABORTED.179- Confuse an environment failure (PENDING) with an implementation failure (FAIL).180181---182183## Edge Cases184185**No contract.md for the feature**: abort and direct the user to `spec-writer`.186187**progress.json missing**: create it with `config.maxFixAttempts` = 3 and this feature's entry.188189**Environment Contract not met**: state = PENDING, clearly labeled as environment, no fix-runner dispatch.190191**Gate command not runnable in this environment** (e.g. missing toolchain): treat as PENDING for that gate (needs environment), not FAIL — do not send the fix-runner after an environment gap. Note it in the report.192193**maxFixAttempts reached**: state = ABORTED; keep the last `evaluation-report.json` for inspection.194195**fix-runner reports "not resolved"**: increment `attempt`; abort to ABORTED if the limit is hit, otherwise re-evaluate.196197**Override `max N attempts`**: use N for `maxFixAttempts` this run (and persist it to `config` if the user intends it to stick — otherwise apply for the session only and note it).198199**Observable criterion is runtime-only and the runtime can't be exercised**: PENDING for that criterion (human/environment), not FAIL.200201**Feature already CLEAN in progress.json**: re-running is allowed (idempotent re-check); report the result and refresh state.202203**Cross-feature criteria**: if the contract references behavior provided by another feature, evaluate only this feature's surface; note cross-feature dependencies in findings rather than failing on another feature's gap.