Fix Runner
A surgical corrector. When the evaluator returns FAIL, it dispatches fix-runner with a structured error report. fix-runner fixes only the reported problem, with the smallest footprint, then hands control back to the evaluator to re-confirm. It is deliberately smaller and more focused than implement-feature: it corrects, it does not build.
Base docs:
https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Skill_Fix_Runner.md(full rationale),https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Contrato_de_Feature.md(contract criteria),https://github.com/dayvisonassis/sdd-skills/blob/main/docs/Fluxo_SDD_e_Implementacao_das_Skills.md(flow, handoff, progress.json). Report schema:../evaluator/references/evaluation-report-schema.md.
This skill is stateless about the loop. The evaluator owns the attempt counter, the limit N, and the ABORTED decision. fix-runner performs one correction per invocation.
Scope — code, not tests. fix-runner corrects production code for failures the evaluator
classifies as kind: gate or kind: observable-criterion. Test failures (kind: test) are
NOT its job — the evaluator routes those to the matching test-writer (confirmed by the matching
test-validator). If a report handed to fix-runner contains only kind: test failures, return
"not resolved — test failures must be routed to a test-writer" without touching code.
INPUT
- Target feature (ID) and the path to
evaluation-report.json— passed by theevaluator(on FAIL) or byqa-preflight(after the feature is done). Both write the same schema, and the on-disk report is the source of truth either way. - If no report path is given, auto-discover the feature's
evaluation-report.jsonindocs/<feature-id>-<kebab>/andlastEvaluationReportinprogress.json. - Auto-discovers: the feature's
contract.mdandprogress.json. Readsspec.md/plan.mdlazily, only for the context the fix needs.
If no error report can be found, abort: "fix-runner requires an evaluation-report from the evaluator or from qa-preflight."
OUTPUT
- A minimal code correction for the reported failure(s).
- One commit per correction —
fix(F<ID>): <error summary>(or the repo's style). Stage only the touched files. - A light update to
progress.jsonrecording that a correction was applied for the currentattempt(does NOT decide the state). - A signal back to the evaluator: "correction applied — re-evaluate F" or "not resolved — ".
- The fix-runner does not emit a CLEAN/FAIL verdict — the evaluator re-confirms.
EXECUTION STEPS
Step 1: Resolve Input
- Resolve the target feature and locate the
evaluation-report.json(from the passed path, or auto-discover viaprogress.json'slastEvaluationReport). No report → abort.
Step 2: Load Minimal Context
- Read the
evaluation-report.json: eachfailures[]entry —kind,ref,message,location,evidence. - For each failure, re-read only the violated criterion/gate (
ref) incontract.md, so the fix conforms to the contract rather than guessing. - Read
progress.jsonto know the feature and the currentattempt. - Read
spec.md/plan.mdlazily, only for the slice needed to understand the fix. No broad codebase sweep.
Step 3: Apply the Minimal Correction
- Fix exclusively the reported cause(s), with the smallest footprint. No opportunistic refactoring, no "while I'm here" improvements, no scope beyond the report.
- If the report has multiple
failures[], address them together only if they are part of the same root cause; otherwise fix what the report lists, in order. - Record what was changed and why (for the commit message and the handoff signal).
Step 4: Local Revalidation
- Run specifically the gate/test that failed (from the report's
ref/location), not the whole suite — a cheap smoke check to avoid returning an obviously broken fix. - If it still fails, adjust within the same error's scope and re-run. Do not expand scope to unrelated problems.
- This local check does NOT replace the evaluator — the evaluator re-confirms against the whole contract afterward.
Step 5: Commit
- One commit, staging only the files touched by this correction. Message:
fix(F<ID>): <error summary>(or match the repo's recent commit style). - No
git add -A/git add .. Commit on the current branch — never create or switch branches. Do not skip hooks.
Step 6: Return to the Evaluator
- Update
progress.json: record that a correction was applied for the currentattempt(e.g. a note/timestamp). Do not set CLEAN/FAIL/PENDING/ABORTED and do not incrementattempt— those belong to the evaluator. - Signal back:
- "correction applied — re-evaluate F" when the local revalidation passed and the commit was made, OR
- "not resolved — " when the fix could not be made within scope (no commit).
RULES
Always:
- Act only from an evaluation-report — produced by the
evaluatoror byqa-preflight. - Re-read the violated criterion/gate (
ref) incontract.mdbefore fixing, so the fix conforms. - Fix only the reported cause, with the smallest possible footprint.
- Locally revalidate the failing gate/test before returning.
- Make one commit per invocation, staging only the touched files.
- Hand control back to the evaluator for re-confirmation.
Never:
- Implement new features or widen the contract's scope.
- Do opportunistic refactoring outside the reported error.
- Count attempts, decide ABORTED, or emit a CLEAN/FAIL verdict (the evaluator's job).
- Write CLEAN/FAIL/PENDING/ABORTED into
progress.jsonor incrementattempt. - Use
git add -A/git add .; skip hooks; create or switch branches. - Read the codebase with a broad upfront sweep — read lazily, scoped to the fix.
Edge Cases
No evaluation-report found: abort — the fix-runner needs the evaluator's report to act.
Report references a ref not found in contract.md: fix from the report's message/location/evidence, and note the missing contract reference in the return signal so the evaluator/spec-writer can reconcile.
kind: qa-finding: this is the expected shape, not an anomaly — the finding is a real defect that no contract criterion covered, and its ref is the QA case id that found it (e.g. CT-ACESSIBILIDADE-002). Fix from message/location/evidence and do not report a missing contract reference. Everything else is unchanged: minimal footprint, local revalidation, one commit.
Fix can't be made within scope (the real cause is outside the reported failure, or requires a spec/contract change): do not stretch scope. Return "not resolved — " without committing, so the evaluator can decide (re-evaluate, PENDING, or eventually ABORTED).
Local revalidation can't run in this environment (missing toolchain/browser): make the minimal fix the report implies, commit, and return "correction applied (local revalidation skipped: ) — re-evaluate F" so the evaluator runs the authoritative check.
Multiple unrelated failures in one report: fix each within its own scope; if one is unresolvable, fix the others, commit, and return a combined signal noting which remain.
Working tree has unrelated changes: stage only the files this correction touched; leave everything else untouched.
Commit-message style inconsistent in recent history: fall back to fix(F<ID>): <error summary>.