Files
DeskcommCRM/evidence/passo4-capacidade-radar.png
Rafael MelgaçoandClaude Opus 5 02d9aceacf refactor(mcp): remove a description morta do catálogo — 51 cópias que ninguém lia
`lib/mcp/tools/catalogo/*.ts` declarava `description` em 51 capacidades; o tipo a
documentava como "Texto tecnico entregue ao MODELO" e sete cabeçalhos repetiam
"`description` fala com o modelo". Nenhum consumidor lia esse campo: a ponte do
turno (`lib/ai/runtime/tools.ts`) monta `def.description`, do HANDLER, e a rota
`/api/v1/mcp/tools` também. As duas fontes divergiam em 48 das 51.

## Por que remover, e não sincronizar

Fonte única mudaria o que 48 tools dizem ao modelo — risco alto, ganho zero. Um
gate de paridade obrigaria manter dois textos sincronizados para sempre, com a
duplicata seguindo lá para ser editada por engano. Remover mata a armadilha na
raiz: não dá para editar o lugar errado se o lugar não existe.

E a remoção SE AUTO-VERIFICA. Tirei o campo do tipo e o `tsc` apontou cada
leitura — prova mais forte que grep.

## O que o typecheck achou: a dívida não era teórica

Um leitor, e o pior possível:
`evidence/ia-360-w4/medicao-vazamento/remedir-com-operador.ts`, o script que mede
vazamento de vocabulário do agente. A função se chama `descreverFerramentas` e o
comentário diz "A ferramenta como o modelo a vê: nome + descrição, que é o que
pode vazar" — e lia `TOOL_CATALOG.description`, exatamente o texto que o modelo
NÃO vê.

NÃO MEDIDO: se isso muda o resultado daquela medição. O `name` é idêntico nas
duas fontes e é o vetor principal de vazamento, então o efeito pode ser nulo,
mas não rodei. Corrigi a fonte para `allTools` e deixei a ressalva no script. Não
reabri a medição arquivada de outra branch.

## O buraco que a remoção expôs

`tests/unit/catalogo-servido.test.ts` testava a junção com FIXTURES
(`description: "faz algo"`), nunca com o catálogo real — nenhum gate garantia que
uma capacidade servida tem descrição. Esvaziar a de um handler passaria calado: a
tela sem explicação e o modelo com uma ferramenta sem contrato.

Caso novo: toda capacidade servida tem descrição não-vazia E ela é IDÊNTICA à do
handler. A segunda metade é a que importa — se reaparecer uma cópia no catálogo e
a junção preferi-la, reprova.

Sabotagens: `description` de um handler vira "" → 1 → 1 (a primeira tentativa não
sabotou nada: escrevi `description: "" ||`, e `"" || "texto"` devolve o texto —
instrumento quebrado, não gate fraco); junção servindo outro texto → 1 → 2.

## Correção de um número que publiquei

A mensagem do commit c56416aa diz "1819 unitários". O real naquele SHA é 1818,
medido depois com `git stash` e a árvore limpa em c56416aa. Rodei a suíte com a
árvore ligeiramente diferente da commitada (antes do `rm` do protótipo e do stage
final) e publiquei como se fosse do commit. Régua daqui em diante: número que sai
em artefato público é medido DEPOIS do stage.

## Saldo

130 linhas removidas, 63 acrescentadas, 9 arquivos. Os 7 cabeçalhos falsos foram
reescritos com o que é verdade e POR QUE o campo não existe mais — para ninguém
"completar" o catálogo de volta. O BRIEFING-ia-360.md acompanhou.

Provado na tela: `/app/ai/agents/<mcp_agent>`, Supabase local (HTML de /login com
2× `127.0.0.1:54321`, 0× `*.supabase.co`), a capacidade segue com rótulo,
explicação, categoria e área — 6/6, zero erro de console.

typecheck 0 · lint 0 errors · 1819 unitários (1818 + 1 caso novo)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkS3mzwtXughmjVC5FCoNo
2026-08-07 12:48:53 -03:00

157 KiB
1280x900px