# Prova: Eval de Modelo

> Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
> Cobertura de Testes (`evals/rubrics/test-coverage.md`, seção "Tetos de prova").

## O que esta prova responde

Prompt e modelo são a única parte do produto cujo comportamento **nem o build nem o teste unitário
leem**: os dois continuam verdes com o prompt inteiro trocado. O eval existe para cobrir
exatamente essa superfície — e é por isso que ele é o artefato que mais facilmente vira teatro.

A pergunta NÃO é "o eval rodou?", nem "quantos cenários passaram?". É:

**o que este eval reprovaria, que hoje ele aprova?**

Um eval que não sabe responder isso mede **execução**, não **detecção** — e a diferença entre as
duas é o defeito inteiro que esta classe de rubrica persegue.

## Bandas 0-10

| 0-3 | 4-6 | 7-8 | 9-10 |
|---|---|---|---|
| Superfície de prompt/modelo mexida sem eval nenhum. Ou eval cujo verde significa "executou": conta cenários e não julga resposta. Miss de cache lido como `skip` | O eval julga a resposta, mas toda asserção passa com qualquer resposta plausível (`Contains` de palavra que o próprio prompt já carrega, "não vazio", "é JSON"). Nenhuma resposta degradada foi injetada — o eval nunca foi visto vermelho | Resposta degradada injetada e o eval ficou vermelho, mas num cenário só; ou quem mordeu foi a asserção de FORMA (o JSON tem a chave) e não a de CONTEÚDO; ou a árvore de cache não tem canário provando que o git a enxerga | Degradação injetada **por cláusula de asserção**, cada uma derrubando o cenário correspondente e **nomeando-o**; contagem de cenários pinada contra encolhimento silencioso; a cache provada legível pelo git; miss é `fail` nomeado, jamais `skip` |

## Anti-padrões literais

- **`Invocations = 0` como prova de qualidade.** Ele prova que **nenhuma chamada paga aconteceu** —
  não que o eval reprovaria um modelo pior. Contador de chamadas mede execução; detecção só se mede
  **degradando a resposta** e exigindo o vermelho. Medido nesta epic: uma suíte com
  `Invocations = 0`, cache quente e 4 de 4 cenários verdes não dizia uma palavra sobre morder.
- **Miss de cache lido como `skip`.** `skip` é lido como "não se aplica" e soma verde no agregado.
  Cenário não avaliado é **`fail` nomeado**, com o nome do cenário e o comando que regrava.
- **Asserção que qualquer resposta plausível satisfaz** — `Contains("orçamento")` num cenário cujo
  prompt já contém a palavra. A régua tem de separar a resposta boa da ruim, não a resposta da
  ausência de resposta. Teste a régua contra a resposta ERRADA, não só contra a certa.
- **O guarda que não enxerga o objeto** (cross-ref `guard-cego.md`). Caso real desta base: o
  `git diff --exit-code` sobre a árvore de cache reportava **limpo** — não porque a árvore
  estivesse limpa, mas porque os caminhos estouravam o `MAX_PATH` e o git não conseguia **abrir**
  os diretórios. Ele avisou no stderr, commitou **1 de 9** arquivos e saiu com código 0. Antes de
  acreditar em qualquer canário sobre a cache, prove que ele **enxerga** os arquivos.
- **Dataset que encolhe em silêncio.** Uma suíte que passa a avaliar menos cenários é a mesma classe
  de defeito que uma baseline de teste caindo — e ninguém percebe, porque o número que aparece é o
  de aprovados.
- **Juiz é o modelo avaliado.** Auto-avaliação do mesmo modelo mede concordância consigo, não
  qualidade. Vale para o avaliador LLM da suíte tanto quanto vale para o Gate 3.
- **Regravar a cache para o eval passar.** Regravar é a resposta certa quando **o pin mudou**, e a
  errada quando **a asserção começou a reprovar**. As duas situações produzem o mesmo comando e
  vereditos opostos: diga qual das duas era, e como sabe.

## Insumo: 3-implement/evidencias/

Task cujo `doneCriteria` julga saída de LLM grava `.morph/features/{feature}/3-implement/evidencias/{taskId}.md`
(`morph-plan` §5): critério, nó `evals` e cenário, modelo, versão do prompt, entrada, saída e
veredito. Não é o `3-implement/reports/{taskId}.md` — aquele é o que o sub-agente diz que fez. É
insumo, não veredito: leia antes de pontuar e confronte o veredito com a saída registrada.

- **Evidência atada a um nó `evals`** (`runner: "evals"`): as bandas acima valem como estão, e a
  degradação desta rubrica usa **o cenário que a evidência aponta** — é ele que tem de cair, pelo
  nome.
- **Evidência sem nó** ("nenhum nó — execução manual"): prova que o prompt rodou **uma vez**, não que
  algo pegaria a regressão — não há cenário a degradar. Banda **4-6** no máximo, por melhor que a
  saída registrada pareça.
- **Evidência devida e ausente** (o path está em `outputs` e o arquivo não existe): superfície de
  prompt/modelo mexida sem eval nenhum — banda 0-3.

## Como registrar no relatório

Uma linha por cláusula degradada, dizendo o que foi degradado e qual cenário caiu **pelo nome**:

```markdown
- `triagem.acme.a-saudacao` — degradei a resposta EM CACHE trocando a saudação por texto vazio:
  `Contains` derrubou o cenário nomeando-o (`eval-fail: triagem.acme.a-saudacao`). Restaurado por
  cópia de bytes feita antes; md5 confere.
- `orcamento.acme.a-resposta-estruturada` — removi a chave `total` do payload: `JsonHasKey` mordeu;
  `NoPii` continuou verde, como esperado (cláusulas independentes).
- Cache: 9 de 9 arquivos endereçáveis por `git ls-files` (canário `eval-cache-portability`), pior
  caminho relativo 145 contra teto 160.
```

E no `**Proof-Scores:**`, a chave `eval`.
