# Prova: Media Claims

> Escopo: Gate 3 (avaliador independente), **somente** em feature que gera mídia. Não é dimensão
> do score composto — o composto continua `arquitetura + contratos + qualidade + testes`. Esta prova
> impõe **teto** em Arquitetura (tabela em `evals/rubrics/architecture.md`, seção "Tetos de prova de
> mídia").

## O que esta prova responde

Uma feature que gera imagem ou vídeo faz **quatro afirmações** que custam caro quando são falsas, e
as quatro são fáceis de escrever e difíceis de provar: *controlamos o custo*, *tem revisão humana*,
*a mídia é marcada*, *o hot path não bloqueia*.

A pergunta desta rubrica é sempre a mesma: **onde está o código que torna a afirmação verdadeira,
e o que acontece se alguém tentar burlá-la?**

Ela vive em Arquitetura, e não em Cobertura, porque as quatro são **propriedades estruturais** —
onde a chamada mora, que estados existem, o que é persistido, o que é publicado. Uma feature que
gera mídia dentro do turno de WhatsApp está arquiteturalmente errada, não mal testada; contá-la como
falha de teste diria a coisa errada a quem lê o relatório.

As quatro afirmações, com o que conta e o que não conta:

| Afirmação | Evidência que conta | Evidência que **não** conta |
|---|---|---|
| **Custo** | Custo estimado gravado **antes** da chamada, com a tarifa e a data da tabela de preços; teto por tenant/dia exercitado em teste, com o caso que ESTOURA | "usamos o modelo mais barato"; uma constante de preço sem fonte datada |
| **Revisão humana** | Teste que prova que a publicação falha a partir de **todo** estado que não seja aprovado, e que aprovar carimba quem e quando | Um comentário dizendo que há revisão; um campo booleano `approved` |
| **Marcação** | A publicação **recusa** quando a finalidade exige rótulo e ele não veio — com texto e superfície nomeados, gravados junto do artefato — mais a proveniência persistida e o C2PA preservado sem re-encode. No exemplar: `MediaJob.Publish(MediaDisclosure?, at)`, terceira porta | "o SynthID vem embutido" — vem, e cai no primeiro re-encode ou screenshot; um campo de rótulo que a publicação aceita vazio; a obrigação escrita só no comentário do estado `Published` |
| **Hot path não bloqueado** | O tipo que produz bytes é **inacessível** do handler (teste de acessibilidade por reflexão) **e** o handler de mensagem devolve id de job | "o handler chama o serviço assíncrono"; um `Task.Run` |

O conhecimento por trás das quatro está em `.morph/framework/standards/ai-agents/media-pipeline.md`.
Esta rubrica não o repete: ela cobra a prova.

## Bandas 0-10

| 0-3 | 4-6 | 7-8 | 9-10 |
|---|---|---|---|
| Nenhuma das quatro afirmações tem código apontado. Ou a geração acontece dentro do turno de conversa, ou a publicação é alcançável sem aprovação humana — qualquer um dos dois é **bloqueante**, independentemente do resto | As quatro estão implementadas, mas a prova é leitura de código: nenhum teste exercita o caminho de recusa. Teto de custo declarado e nunca estourado em teste; marcação prometida na prosa e ausente no ponto de publicação | As quatro provadas por teste, com o caminho de recusa exercitado ao menos uma vez cada. Falta a mutação: nenhuma delas foi vista MORDENDO, ou a proveniência grava menos campos do que o standard nomeia | As quatro provadas, cada caminho de recusa com teste próprio, **e a acessibilidade do tipo que produz bytes asserida por reflexão com a mutação rodada** (tornar público, ver o teste falhar nomeando o defeito, restaurar por bytes e conferir por md5). Custo estimado antes da chamada, com tarifa datada; teto exercitado no caso que estoura. A marcação é **parâmetro da transição de publicação**, não campo opcional: existe o teste que publica sem rótulo e vê a recusa |

## Anti-padrões literais

- **Prender a regra dura só na prosa.** "O standard diz para não gerar no turno" não impede nada. O
  que impede é a acessibilidade do tipo: se um handler consegue **nomear** o tipo que produz bytes,
  a regra não está presa — mesmo que ninguém a tenha violado ainda. (memória: é a diferença entre
  "o standard diz para não fazer" e "o compilador recusa")
- **Testar a fachada em vez do dono do estado.** Se `Review.Publish(job)` reimplementa a regra em
  vez de delegar a `job.Publish()`, o teste da fachada mede uma **cópia** do objeto — e as duas
  divergem no primeiro conserto. Exercite as duas contra o MESMO job e compare.
- **Confundir a chave de idempotência do PEDIDO com o hash do CONTEÚDO.** Uma dedupe chamada paga, a
  outra dedupe storage. Um teste que só confere "o hash é o mesmo" **não distingue dedupe de
  regravação**: as duas produzem o mesmo hash. Conte as gravações.
- **Aceitar `estimatedCostUsd` gravado DEPOIS da chamada.** Isso é contabilidade, não controle: o
  dinheiro já saiu. A prova é a ordem, e a ordem é verificável.
- **Preço em código sem fonte datada.** Uma constante `0.211m` sem a página e a data de consulta é
  folclore com força de orçamento. A tarifa vem da tabela do standard da modalidade.
- **Tratar bloqueio de moderação como erro técnico.** Se o estado de bloqueio for retryable, um laço
  de retry vira uma fatura — e no provedor que cobra o bloqueio, cobra duas vezes. Exija dois
  estados distintos e um teste por estado.
- **`not_detected` lido como "feito por humano".** Ausência de marca é ausência de informação. Um
  verificador de proveniência que devolve "não detectado" não prova origem humana.
- **Marcação delegada ao provedor.** C2PA e SynthID vêm embutidos e **caem** com screenshot ou
  re-encode. A marcação visível é obrigação separada, e ela mora no ponto de publicação — não no de
  geração.
- **Marcação presa só por prosa.** É o anti-padrão mais fácil de cometer porque parece resolvido: o
  comentário do estado `Published` diz "com a marcação aplicada", a finalidade `Marketing` diz
  "marcação visível obrigatória", e um `grep` por rótulo, watermark ou disclosure no código volta
  **vazio**. Se as outras três afirmações estão presas por tipo ou por estado e a marcação — que é a
  obrigação legal — está presa por nada, a assimetria é o achado. Prova mínima: a publicação recusa
  sem rótulo.
- **Ler "o tipo prende" como "nada mais é possível".** O `internal` fecha *obter bytes* e *nomear o
  produtor*; ele **não** fecha *aguardar inline*. O gatilho do worker (`IMediaJobRunner.RunAsync` no
  exemplar) é público por necessidade — quem o dispara é o `BackgroundService` do projeto — e um
  `await runner.RunAsync(id)` dentro de um handler de turno **compila** e paga a latência inteira.
  Procure por esse `await`: é o que o compilador não procura. Uma prova que só cita a acessibilidade
  e não olha os call sites do runner cobriu metade da afirmação.
- **Teste que toca API paga.** Uma chave de API num teste é uma fatura por execução de CI e um teste
  que fica vermelho quando o provedor tem incidente. `grep` por chave, host do provedor e leitura de
  ambiente nos arquivos de teste tem de voltar vazio.
- **Aceitar o número de testes sem a suíte inteira.** "Derruba 4" medido sobre um arquivo e "derruba
  4 de 203" são afirmações diferentes. Só a suíte inteira autoriza a frase.
- **Declarar as quatro afirmações numa feature que não gera mídia.** A isenção é automática: se
  nenhuma task pina um standard de mídia, a seção não é exigida. Escrever a seção "por precaução"
  gasta o Gate 3 com cerimônia vazia, que é como uma regra vira ruído e depois é desligada.

## Como registrar no relatório

Na seção `## Mídia gerada` do `evaluator-report.md`, uma linha por afirmação, com os quatro campos:

```markdown
## Mídia gerada

| Afirmação | Onde está presa (arquivo:linha) | Teste que a prova | Rodada de mutação |
|---|---|---|---|
| Custo | src/…/MediaGenerator.cs:88 (reserva ANTES da chamada) | MediaGeneratorTests.ABudgetOverrun… | — |
| Revisão humana | src/…/MediaJob.cs:… (Publish só de Approved) | MediaJobStateTests.PublishFails… (6 estados) | — |
| Marcação | src/…/MediaJob.cs:… (3ª porta de Publish) + MediaDisclosure.cs | MediaDisclosureTests.PublishFailsWithoutADisclosure… | porta removida → 2 de 220 caem, motivo lido, md5 restaurado |
| Hot path | src/…/IMediaProvider.cs (internal) | MediaAccessibilityTests.TheTypeThat… | público → 4 de 220 caem, motivo lido, md5 restaurado |
```

E a linha machine-readable, no mesmo estilo do `**Composite-Score:**`, junto das demais provas:

```
**Proof-Scores:** mutation=9 verde=8 fiacao=9 guard=7 media=9
```

**A chave `media` só entra quando a feature gera mídia.** Feature que não pina nenhum standard de
mídia não declara a seção e não declara a chave — e o gate não a cobra. Feature que gera mídia **sem
a seção** pausa o Gate 3 por conta própria (`src/lib/risk-detector.js`), antes de qualquer discussão
de nota.
