TDD com Playwright MCP e CI: pipeline Claude Code em 47 min
O ciclo Red-Green-Refactor sempre foi vendido como disciplina, quase moral. Você escreve o teste, vê o vermelho, faz o verde, refatora. Em papel bonito. Na prática, meu loop de feedback ficava em 12 minutos por causa do CI, e eu passava metade da tarde alternando entre a aba do editor e a aba do Actions esperando o “amarelinho ficar verde”.
Este post é o desenho de um pipeline em que o Claude Code escreve o teste Playwright via MCP e o CI roda em headless com @playwright/mcp 0.0.78 (Microsoft, Apache-2.0). Os arquivos de configuração aqui são um exemplo montado para o texto, e os tempos e custos são estimativa, não medição de produção. O que eu rodo de verdade é a parte de cima, a separação de fases em subagentes, e é dela que vem o resto.
Se você já leu meu texto sobre as 24 horas em que meu agente de IA gerou 8 incidentes de segurança, pode pular direto para a seção “Permissões no runner”. O resto do artigo assume que você ainda não passou por essa dor.
Por que TDD com Claude Code, e não Claude Code sozinho
O erro que eu já cometi, e que motiva o desenho todo, é deixar o Claude Code escrever teste e implementação na mesma sessão. O CI fica verde e a sensação é boa. O problema aparece depois: o teste sabe demais sobre a implementação.
Existe um termo para isso: contaminação de contexto. Quando o mesmo agente que implementa também escreve o teste, o teste não verifica correção. Ele carimba a implementação. É o equivalente digital de corrigir a própria prova de casa. Você acerta 100 %, mas não aprende nada.
A solução que virou padrão para mim é separar as fases em subagentes independentes. Um subagente escreve o teste sem enxergar código de produção. Outro implementa sem ver o raciocínio do primeiro. O terceiro refatora. Cada um tem contexto isolado. O teste não pode trapacear porque literalmente não viu o gabarito.

Setup: Playwright MCP em 3 comandos
Playwright MCP é um servidor MCP mantido pela Microsoft que expõe o navegador via árvore de acessibilidade em vez de screenshots. Isso muda tudo em CI headless: você não precisa de OCR nem de modelo visão para o agente entender a tela.
Instalação:
npx @playwright/mcp@latest --headless
Configuração no .mcp.json da raiz do projeto:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--headless"]
}
}
}
Requer Node.js 18+. Sem plano pago, sem métrica de uso, sem rate limit. Roda 100 % na sua máquina (ou no runner do GitHub Actions, no nosso caso). Ao rodar uma tool, o servidor devolve um snapshot estruturado da página: elementos, roles, texto. É o mesmo mecanismo que o Claude Code usa para clicar em botões sem “ver” a tela.
O passo que decide a spec: a matriz de cobertura
Antes de acionar qualquer subagente eu monto uma matriz de cobertura junto com os requisitos. Ela lista o que precisa ser verificado, por perspectiva, e é ela que vai para o tdd-test-writer no lugar de uma frase solta.
| Alvo | Caminho feliz | Valores limite | Casos de erro | Dependência de estado | Efeitos colaterais |
|---|---|---|---|---|---|
| Botão de export CSV | exporta com filtro de data ativo | intervalo vazio, um único dia | download falha, sessão expirada | filtro ligado e desligado | padrão do nome do arquivo |
O nome do campo, o nome do botão e a convenção do nome do arquivo entram aqui, escritos por mim. É neste ponto que a regra de negócio fica fixada, e por isso essa etapa não desce para subagente nenhum: ela é decisão, não execução.
Pular esse passo é o que faz o fluxo desandar. Se o subagente de teste recebe apenas “adiciona um botão de export CSV”, ele preenche as lacunas por conta própria, e o subagente de implementação persegue essa invenção só para o CI ficar verde. O isolamento de contexto impede que o teste seja derivado da implementação. Ele não resolve ambiguidade de especificação, e não é para isso que ele serve.
O agente que escreve o teste (subagente 1)
O subagente tdd-test-writer recebe os requisitos e a matriz de cobertura. Nunca vê src/. O prompt do agente:
# .claude/agents/tdd-test-writer.md
Você é um escritor de testes. Sua ÚNICA função é escrever testes que falham.
Regras:
- Você NÃO PODE ler nem referenciar arquivos de implementação (src/**)
- Escreva testes baseados APENAS na descrição da feature
- Cubra caminho feliz e casos limite (input vazio, mobile 375px, etc.)
- Use Playwright MCP para inspecionar a UI real quando existir
- Rode os testes e confirme que FALHAM antes de terminar
Recebendo a matriz, o agente abre o servidor local via mcp__playwright__browser_navigate, confirma que o botão ainda não existe, e escreve o teste a partir do que a matriz fixou, e não do que ele imagina que a implementação vai ser.
O teste sai assim:
test('exporta CSV com filtro de data ativo', async ({ page }) => {
await page.goto('http://localhost:3000/dashboard');
await page.getByLabel('De').fill('2026-07-01');
await page.getByRole('button', { name: 'Export CSV' }).click();
const download = await page.waitForEvent('download');
expect(download.suggestedFilename()).toMatch(/^dashboard-2026-07-01/);
});
Repare no getByRole('button', { name: 'Export CSV' }). Isso é Playwright MCP guiando o agente para seletores estáveis, em vez daquele .btn-primary.mt-4 frágil que quebra sempre que alguém troca de Tailwind.
O CI que fecha o loop em 47 segundos
Aqui está o workflow inteiro (removi comentários para caber):
# .github/workflows/tdd-loop.yml
name: TDD Loop
on:
pull_request:
types: [opened, synchronize]
jobs:
tdd:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npx playwright install chromium --with-deps
- run: npm run dev &
- run: npx wait-on http://localhost:3000
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--mcp-config '{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--headless"]
}
}
}'
--model claude-sonnet-4-6
--allowed-tools "Read,Edit,Bash(npm run test),mcp__playwright__*"
prompt: |
Rode o teste falhando em tests/. Se um subagente
tdd-test-writer já adicionou tests/*.spec.ts nesta PR,
invoque o subagente tdd-implementer para escrever a
implementação mínima em src/ que faça o teste passar.
NÃO modifique arquivos em tests/.
Os pontos de atenção que quebraram na minha primeira tentativa:
--allowed-toolsrestringe o subagente ao mínimo. Sem isso ele tenta rodargit pushno meio da PR e o Actions bloqueia (bem que devia).--headlessno MCP é obrigatório. Sem ele o Chromium tenta abrir X server no runner e trava por 30 s antes de morrer.timeout-minutes: 5é um teto psicológico. Se o loop passar de 5 minutos, o desenho está errado, não a máquina.
A estimativa de tempo por etapa, para dimensionar o desenho (não é medição de produção):
| Etapa | Antes | Depois |
|---|---|---|
| Escrever teste (local) | 3 min | 12 s (subagente) |
| Push + CI cold start | 4 min | 1 min (cache) |
| Rodar teste no CI | 3 min | 25 s (headless + a11y) |
| Ler resultado + iterar | 2 min | 10 s (comment no PR) |
| Total | 12 min | 47 s |
Permissões no runner: o campo minado
Quando o Claude Code roda no CI, ele herda o GITHUB_TOKEN. Se você deixar permissions: write-all (o default de muitos templates), o agente pode fechar issues, deletar branches, e criar releases.
O mínimo viável para essa pipeline:
permissions:
contents: read # ler o código
pull-requests: write # comentar na PR
Nada de issues: write, nada de actions: write, nada de packages: write. Se o agente tentar algo fora desse escopo, o GitHub bloqueia e você vê no log. É a diferença entre “meu agente é bem comportado” e “meu agente é estruturalmente incapaz de fazer besteira”.
O detalhe cruel: --allowed-tools restringe o Claude Code, mas não impede o subagente que ele invoca via MCP de fazer qualquer coisa que o token permita. Por isso a camada de permissions: no workflow é mais importante do que a whitelist de tools.
Quanto custa isso em BRL
Estimando o consumo de uma execução da pipeline (test-writer + implementer + refactorer), na ordem de:
- 45 000 tokens de input (contexto do repositório + resposta do MCP)
- 4 000 tokens de output (código + explicação)
Com Claude Sonnet 4.6 a US$ 3 / M input e US$ 15 / M output:
input: 45 000 × $3 / 1 000 000 = $0.135
output: 4 000 × $15 / 1 000 000 = $0.060
total: $0.195 por execução ≈ R$ 1,07 (câmbio 5,5)
Com Sonnet 5 (lançado em 30 de junho de 2026 a US$ 2 / US$ 10 até 31 de agosto), o mesmo loop cai para US$ 0.13 ≈ R$ 0,72. Um dev que roda 40 PRs por dia gasta R$ 40 / dia, ou seja, menos do que uma dose de café especial. E o CI para uma dose de café.
Se você comparar com um freelancer QA júnior a R$ 60 / hora rodando teste manual, o breakeven é uma execução por hora.
Onde eu ainda tropeço
Duas coisas que ainda me incomodam nessa arquitetura:
1. Debug de teste flaky é pior. Quando o teste passa localmente e falha no CI, o subagente tdd-implementer não tem contexto do que o tdd-test-writer “queria dizer”. Ele fica adivinhando. Solução parcial: um README.md em tests/ que descreve invariantes de negócio (nada de detalhes técnicos), e que ambos os subagentes leem.
2. Playwright MCP em CI paralelo compete por porta. Se o workflow roda 3 shards, os 3 tentam abrir Chromium na 9222. Solução feia mas funcional: PLAYWRIGHT_MCP_PORT=$((9222 + RANDOM % 100)).
O que fica
TDD não é sobre disciplina moral. É sobre isolamento de contexto. Quando o agente que escreve o teste não vê o gabarito, o teste vira uma spec real. E quando a spec é executável no CI em 47 segundos, ela deixa de ser “aquela coisa que a gente devia fazer” e vira o loop de feedback padrão.
Pela estimativa acima, o custo por PR fica na casa de R$ 1, abaixo do que vale o tempo que eu passaria olhando o Actions. Quem for montar isso, meça no próprio repositório antes de tratar o número como seu.
Se você já se queimou com agente sem permissions: explícito, o outro texto que eu recomendo é as 24 horas em que meu agente gerou 8 incidentes de segurança — os padrões de escape de sandbox que aparecem lá são exatamente os que essa pipeline previne.
ken imoto · WebRTC & Voice AI engineer · kenimoto.dev · TabNews
Livro relacionado Practical Claude Code Tutorial de Claude Code | padrões de CLAUDE.md, design de Plan Mode e workflows de equipe Ver a página do livro → Este artigo foi útil?