Como se trabalha
09 · Mudanças com OpenSpec e TDD
Toda mudança de comportamento é uma change do OpenSpec. Toda linha de código de uma change nasce de um teste que falhou antes. As duas regras se encaixam: a spec diz o que deve acontecer, o teste prova que acontece, o código faz acontecer.
O ciclo de uma change
O que cada artefato da change contém
| Artefato | Conteúdo | Regras do modelo (openspec/config.yaml) |
|---|---|---|
proposal.md | Conteúdo: por quê, o que muda, o que fica de fora, impacto | Regras do modelo (openspec/config.yaml): aponta as capabilities afetadas |
design.md | Conteúdo: como: decisões numeradas (D1, D2…), alternativas, riscos | Regras do modelo (openspec/config.yaml): aponta módulos e migrations afetados; decisão com alternativa relevante → ADR |
specs/<capability>/spec.md | Conteúdo: requisitos delta (ADDED/MODIFIED/REMOVED), cada um com cenários WHEN/THEN | Regras do modelo (openspec/config.yaml): cenários concretos; cada requisito em até 500 caracteres, porque o openspec validate --all --strict da CI recusa o mais longo; distingue regra da aplicação de regra do Keycloak |
tasks.md | Conteúdo: tarefas numeradas, agrupadas por módulo | Regras do modelo (openspec/config.yaml): teste antes do código; cada cenário mapeado para um teste; tarefa de E2E; tarefa de documentação; tarefa de verificação final |
A forma do tasks.md
## 1. Domínio (<modulo-comum>)
- [ ] 1.1 Teste de unidade: conceder crédito negativo é recusado (cenário "valor inválido")
- [ ] 1.2 Código: a carteira recusa valor ≤ 0
- [ ] 1.3 Teste de integração, com o banco real: o extrato é append-only
- [ ] 1.4 Código: a migration que torna o lançamento imutável
## 2. Aplicação de gestão
- [ ] 2.1 Caso E2E `carteira/concessao/recusa-valor-negativo` (cenário "valor inválido"): falha, a tela ainda não existe
- [ ] 2.2 Caso E2E `carteira/concessao/credito-aparece-no-extrato`: falha
- [ ] 2.3 Teste de rota: conceder sem `carteira:conceder` → 403
- [ ] 2.4 Código: a rota e a tela; o teste de rota e os dois casos passam
## 3. Documentação e verificação
- [ ] 3.1 `docs/acesso/gestao.md`: permissão `carteira:conceder` no perfil gestor
- [ ] 3.2 `ARCHITECTURE.md`: parágrafo da capability
- [ ] 3.3 Build verde + verificador + `eng/e2e.sh` (regressão do zero) com 0 falha
- [ ] 3.4 Capturas só das telas afetadas: `gestao-carteira` (se a stack tem acervo)O ciclo TDD dentro de cada tarefa
O que "teste primeiro" significa na prática
- O teste nasce do cenário da spec. O nome do teste diz o cenário; o comentário de
cabeçalho cita a spec (
spec: carteira-moeda-hibex › Concessão › valor inválido); - O agente roda o teste e o vê falhar antes de escrever o código — e confere que a falha é a esperada (uma asserção, não um erro de compilação de outra coisa);
- Código mínimo para passar. Nada de "já que estou aqui";
- Refatoração com a suíte verde;
- Relato: ao marcar a tarefa, o agente diz em uma linha "vermelho por X → verde". Esse relato é o que permite ao mantenedor confiar no TDD sem assistir à sessão.
Em que camada testar
Regra de proporção: a maior parte dos cenários é coberta por unidade e teste de rota; o E2E cobre o caminho do usuário de cada requisito, não cada combinação. Um cenário de spec pode ser coberto em mais de uma camada, mas pelo menos em uma.
Como o TDD chega ao OpenSpec
O OpenSpec não sabe de TDD; o modelo o ensina por três caminhos, e um script confere:
| Onde | O que leva | Quando chega ao agente |
|---|---|---|
openspec/config.yaml, rules.tasks | O que leva: teste e código em tarefas separadas, o teste antes em cada grupo, o tipo no começo de cada tarefa, o caso E2E antes da tela | Quando chega ao agente: ao gerar o tasks.md (/opsx:propose), como regra obrigatória |
openspec/config.yaml, context | O que leva: rodar o teste e vê-lo falhar antes do código; o relato "vermelho por X → verde" | Quando chega ao agente: em todo artefato e também no /opsx:apply |
AGENTS.md e o guia fluxo-openspec.md | O que leva: as mesmas regras, com o modelo de tasks.md | Quando chega ao agente: sempre |
eng/tdd-order.sh | O que leva: confere, em cada grupo de cada tasks.md aberto, que nenhuma tarefa Código: vem antes de uma tarefa de teste | Quando chega ao agente: o agente roda antes de pedir a revisão; a CI roda em todo push |
Cada tarefa começa pelo tipo — Teste de unidade:, Teste de integração:, Teste de rota:,
Caso E2E …: ou Código: —, e é isso que torna a ordem conferível. Código que não muda
comportamento (criar a pasta de um módulo, renomear) leva Código (sem teste: <razão>):, com a
razão à vista do revisor. A instrução padrão do OpenSpec sugere "implementar e verificar" numa
tarefa só; a regra do modelo a substitui.
Uma armadilha: um item de rules com ": " fora de aspas vira chave e valor no YAML, e o OpenSpec
descarta todas as regras daquele artefato, só com um aviso. O teste
eng/tests/test_openspec_config.py do modelo pega esse erro.
TDD também no E2E
O caso E2E de um cenário novo é escrito antes da tela existir, e falha (não acha o botão, a rota responde 404). Na tela pronta, passa. Isso tem duas vantagens sobre um roteiro escrito depois da tela:
- o caso descreve a tela pelo que o usuário faz, não pela estrutura que acabou saindo;
- a quebra de um caso antigo por uma mudança de tela aparece durante a change, não numa corrida de roteiro dias depois.
O que é change, e o que não é
| É change (OpenSpec) | Não é change |
|---|---|
| comportamento novo ou alterado | Não é change: correção de texto em documentação |
| tela nova ou alterada | Não é change: ajuste de comentário |
atualização do design system (vendor/hibex/) | Não é change: regerar capturas sem mudança de tela |
atualização da arquitetura (ARQUITETURA) | Não é change: ADR nova sem código (a ADR já é o registro) |
| versão maior de dependência | Não é change: |
| nova aplicação na stack | Não é change: |
Correção de bug
Bug também entra por change, mas pequena, a critério do mantenedor: uma tarefa com o teste que reproduz o bug (vermelho) e outra com a correção (verde). A spec só muda quando o comportamento esperado estava errado; se a spec estava certa e o código não, ela fica como está.
Regras de convivência
- Uma change em
applypor vez. Outras podem estar propostas, esperando; - Change grande se divide antes do apply (ex.: adotar o design system em etapas, ou uma atualização grande dele em várias changes — cada uma verificável sozinha);
- Perguntas de escopo vão como lista numerada em prosa, cada uma com opções (a)/(b)/(c) e a recomendada primeiro — o mantenedor responde "1a, 2b";
- A change não termina no código: termina quando o mantenedor validou o ambiente no ar, as specs foram sincronizadas e a change foi arquivada.