fix-runner · git:20260701.2525c6d · 2026-07-01 · sha256 99f16a2e88bb8a3b

fix-runner git:20260701.2525c6dA

Immutable. This exact content never changes and is served at /api/v1/blob/99f16a2e88bb8a3b.

---
name: fix-runner
description: Specialized, lightweight corrector dispatched by the evaluator on FAIL. Reads the structured evaluation-report.json, applies the minimal fix for the reported problem, locally revalidates the failing gate/test, commits (one commit per correction), records the attempt in progress.json, and returns control to the evaluator for re-confirmation. Does not implement new features, does not orchestrate the attempt loop, and does not decide ABORTED.
---

# 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 the evaluator). The on-disk report is the source of truth.
- If no report path is given, auto-discover the feature's `evaluation-report.json` in `docs/<feature-id>-<kebab>/` and `lastEvaluationReport` in `progress.json`.
- Auto-discovers: the feature's `contract.md` and `progress.json`. Reads `spec.md`/`plan.md` lazily, only for the context the fix needs.

If no error report can be found, abort: "fix-runner requires an evaluation-report from the evaluator."

## 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.json` recording that a correction was applied for the current `attempt` (does NOT decide the state).
- A signal back to the evaluator: **"correction applied — re-evaluate F<ID>"** or **"not resolved — <reason>"**.
- 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 via `progress.json`'s `lastEvaluationReport`). No report → abort.

### Step 2: Load Minimal Context

- Read the `evaluation-report.json`: each `failures[]` entry — `kind`, `ref`, `message`, `location`, `evidence`.
- For each failure, re-read **only** the violated criterion/gate (`ref`) in `contract.md`, so the fix conforms to the contract rather than guessing.
- Read `progress.json` to know the feature and the current `attempt`.
- Read `spec.md`/`plan.md` lazily, 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 current `attempt` (e.g. a note/timestamp). Do **not** set CLEAN/FAIL/PENDING/ABORTED and do **not** increment `attempt` — those belong to the evaluator.
- Signal back:
  - **"correction applied — re-evaluate F<ID>"** when the local revalidation passed and the commit was made, OR
  - **"not resolved — <reason>"** when the fix could not be made within scope (no commit).

---

## RULES

**Always:**
- Act only from an evaluation-report produced by the evaluator.
- Re-read the violated criterion/gate (`ref`) in `contract.md` before 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.json` or increment `attempt`.
- 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.

**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 — <reason>" 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: <reason>) — re-evaluate F<ID>" 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>`.