Workflow de Desenvolvimento (@workflow) — v1.0
Status: Obrigatório Data: 2026-04-08 Última atualização: 2026-04-08
Invocar com a palavra-chave @workflow — execução obrigatória em toda feature nova (Cenário A) ou atualização de feature existente (Cenário B).
Este documento governa quando e como o processo de desenvolvimento é executado. Recursos técnicos consultados durante o processo:
- TESTING_MASTER.md — referência técnica de testes (Fase 2)
- AUDIT.md — checklist @audit (fases finais)
- MIGRATION_SAFETY_PROTOCOL.md — protocolo de banco (quando aplicável)
1. Princípio Central
Testes são entrega, não apêndice.
- Feature sem cobertura de testes adequada não está pronta — está incompleta.
- Testes são entregues no mesmo commit que o código. Nunca "depois", nunca "em sprint separada".
- O critério de conclusão de uma implementação é testes passando no pipeline CI, não "parece que funciona".
- Uma entrega sem testes é tratada com a mesma gravidade que uma entrega sem documentação: não é aceita.
2. Cenário A — Feature Nova (TDD Obrigatório)
Toda feature nova segue um processo de 4 fases sequenciais, cada uma com ponto de aprovação explícito.
Fase 1: Planejamento
Objetivo: Definir o escopo da feature antes de qualquer código.
Entregáveis:
- O que a feature faz (comportamento esperado)
- Quais camadas são afetadas (Edge Function, hook, componente, migration, RLS)
- Quais features adjacentes podem ser impactadas
- Quais documentos da
docs/foram consultados (obrigatório listar) - Se envolve banco: declarar que MIGRATION_SAFETY_PROTOCOL.md será seguido
- Declaração de feature flag (obrigatória — ver §7):
- Nome canônico (
snake_case, comparent_chavese sub-flag) - Categoria (
admin | especialista | escola | coordenador | diretor | portal | outros) - Estado de nascimento (sempre
ativa_global=false) - Plano de rollout: canary inicial (escolas/usuários piloto), critério de promoção a global, prazo
- Mapa de gates: lista de edge functions backend + componentes/abas frontend
- Plano de aposentadoria: condições para remoção da flag
- Nome canônico (
Gate: ⏸️ Aguardar aprovação do planejamento antes de avançar.
Fase 2: Escrita dos Testes (Antes da Implementação)
Objetivo: Definir o contrato de comportamento da feature via testes que falham.
Processo:
- Consultar a árvore de decisão de camada do TESTING_MASTER.md §3 para determinar quais tipos de teste são necessários (unitário, contrato Deno, integração de hook, componente, segurança, E2E)
- Escrever testes cobrindo:
- Happy path — comportamento esperado com inputs válidos
- Bordas documentadas — limites, valores nulos, permissões insuficientes
- Impacto adjacente — features identificadas no planejamento que podem ser afetadas
- Feature flag (4 testes mínimos — ver §7):
- Backend gate OFF → 403
FEATURE_DISABLED - Backend gate ON → 200 + payload esperado
- Frontend
<FeatureGate>/useVisibleTabs(admin bypass + papel não-admin com/sem flag) - Canary access (usuário em canary group +
ativa_global=false→ permitido)
- Backend gate OFF → 403
- Localização canônica de testes Deno (BLOQUEANTE): testes de Edge Function vão em
supabase/functions/<nome>/__tests__/e helpers emsupabase/functions/_shared/__tests__/. NUNCA co-localizar*.test.tsna raiz da pasta da function — o bundler do Supabase CLI inclui.tsda raiz no bundle e o deploy falha com OOM (exit 135). Validado pelo guardscripts/check-edge-function-test-location.sh. Ver TESTING_MASTER.md §3.
- Rodar os testes — todos devem falhar neste ponto. Isso é esperado e correto. Se algum teste passa antes da implementação, o teste está testando a coisa errada.
Anti-padrões:
- ❌ Escrever testes que testam implementação interna (mock de tudo)
- ❌ Escrever testes que passam sem a feature existir
- ❌ Pular testes de borda "porque é edge case"
Gate: ⏸️ Aguardar aprovação dos testes antes de implementar.
Fase 3: Implementação
Objetivo: Escrever o código que faz os testes passarem.
Processo:
- Implementar a feature guiada pelos testes escritos na Fase 2
- Rodar os testes localmente após cada mudança significativa
- O critério de conclusão é: todos os testes passam
- Se durante a implementação surgir um comportamento não coberto pelos testes, adicionar o teste primeiro, depois o código
Anti-padrões:
- ❌ Alterar testes para fazê-los passar (exceto se o teste tinha um bug genuíno)
- ❌ Declarar "pronto" com testes falhando
- ❌ Implementar funcionalidade além do escopo dos testes sem adicionar cobertura
Fase 4: Auditoria e Pipeline
Objetivo: Validar a entrega completa.
Processo:
- Executar
@auditconforme AUDIT.md - Verificar que todos os testes passam no pipeline CI (não apenas localmente)
- Atualizar documentação conforme DOCUMENTATION_MAINTENANCE.md
- Se houve mudança de banco: confirmar que MIGRATION_SAFETY_PROTOCOL.md foi seguido integralmente
Gate: ✅ Entrega concluída apenas quando @audit não tem itens 🔴 abertos.
3. Cenário B — Atualização de Feature Existente
Atualizar uma feature existente é mais complexo que criar uma nova, porque exige entender o estado atual antes de mudar qualquer coisa. O processo tem 5 fases.
Fase 1: Raio-X Arquitetural
Objetivo: Produzir um documento SSOT (Single Source of Truth) da feature no estado atual.
Entregáveis (documento em docs/ referenciando a feature pelo nome):
- Inventário de arquivos: quais Edge Functions, hooks, componentes, migrations compõem a feature
- Fluxo de dados ponta a ponta: do clique do usuário até o banco e volta
- RLS policies: quais policies protegem os dados desta feature
- Cobertura de testes atual: quais testes existem, o que cobrem, o que falta
- Checklist de Feature Flag (ver §7):
- A feature já tem flag? Em qual estado do ciclo (planejada/canary/global/estável)?
- Backend tem
requireFeatureFlagem todas as ações (incluindo leituralist_*/get_*)? - Frontend tem
<FeatureGate>ouuseVisibleTabs? - Categoria correta no banco (coluna
feature_flags.categoria)? - Chave segue
snake_casecanônico? - Sub-flags coerentes com
parent_chave? - Existe duplicação semântica (ex:
cursosvsformacao)?
- Desvios de padrão: anti-padrões identificados em relação à documentação (
docs/)- Ex: usa
fetchdireto em vez deinvokeAction, não temregistrarLog(), faltaWITH CHECK, gate backend ausente
- Ex: usa
Este raio-x se torna o SSOT da feature — o documento de referência para qualquer trabalho futuro nela.
Gate: ⏸️ Aguardar aprovação do raio-x antes de avançar.
Fase 2: Testes do Estado Atual
Objetivo: Cobrir o comportamento atual antes de mudar qualquer coisa.
Processo:
- Escrever testes que documentam o comportamento atual da feature (como ela funciona hoje)
- Se o raio-x identificou desvios de padrão, corrigi-los aqui junto com os testes
- Ex: se falta
registrarLog(), adicionar o log + teste que valida o log - Ex: se usa
useState + useEffectpara fetch, migrar para React Query + teste
- Ex: se falta
- Rodar todos os testes — devem passar (estamos testando o que já existe)
Por que corrigir desvios antes da atualização?
- Misturar correção de anti-padrões com feature nova torna impossível isolar regressões
- Se a atualização quebrar algo, sabemos que é a atualização — não uma correção de padrão
Gate: ⏸️ Aguardar aprovação dos testes baseline antes de avançar.
Fase 3: Planejamento da Atualização
Objetivo: Definir o escopo da mudança com conhecimento completo do estado atual.
Entregáveis:
- O que será mudado (delta explícito sobre o raio-x)
- Quais testes existentes serão afetados pela mudança
- Quais testes novos são necessários
- Se envolve banco: declarar que MIGRATION_SAFETY_PROTOCOL.md será seguido
Gate: ⏸️ Aguardar aprovação do planejamento antes de implementar.
Fase 4: Implementação + Testes Novos
Objetivo: Implementar a atualização com cobertura de testes.
Processo:
- Escrever testes novos para os comportamentos que a atualização adiciona/modifica (TDD — testes primeiro)
- Implementar a atualização
- Rodar todos os testes (baseline + novos) — todos devem passar
- Se um teste baseline quebrar, analisar se é regressão (bug) ou mudança intencional de comportamento (atualizar o teste com justificativa)
Fase 5: Auditoria Completa
Objetivo: Validar a feature inteira (não apenas a atualização).
Processo:
- Executar
@auditcobrindo a feature completa (não apenas o delta) - Atualizar o documento SSOT criado na Fase 1 para refletir o novo estado
- Atualizar documentação conforme DOCUMENTATION_MAINTENANCE.md
Gate: ✅ Entrega concluída apenas quando @audit não tem itens 🔴 abertos.
3.1 Sub-cenário — Migração de modelo de dados (JSONB → relacional)
Quando aplicar: sempre que uma feature for refatorada para mover dados de uma coluna JSONB para tabelas relacionais com FK (ex:
olimpiadas.fases_por_nivel→fases_olimpiada.nivel_id). Aplica-se em conjunto com o Cenário B.
A regressão H1 de save_cronograma (2026-04-27) mostrou que migrar a escrita para o relacional sem auditar todos os caminhos de leitura/escrita causa perda silenciosa de dados (UPDATEs com NULL sobrescrevendo a verdade recém-gravada). O checklist abaixo é obrigatório para qualquer mudança desse tipo.
Checklist de 6 passos
| # | Passo | Saída esperada |
|---|---|---|
| 1 | Inventário de consumidores — grep -rn '<coluna_legada>' src/ supabase/functions/ antes de qualquer alteração. Liste cada consumidor, indicando se é READ, WRITE ou ambos. | Tabela com 100% dos consumidores mapeados (frontend, backend, edge, cron, snapshots, mural). |
| 2 | Escrita dupla temporária — implemente o relacional escrevendo em paralelo ao JSONB nas mesmas mutations. Não migre leitura ainda. | Ambas as fontes ficam sincronizadas durante a janela de migração. |
| 3 | Migração de leitura por consumidor com fallback — cada consumidor lê primeiro do relacional e cai no JSONB se vazio. Logue console.warn quando o fallback é acionado para detectar inconsistências em prod. | Logs limpos = nenhum consumidor está dependendo do fallback. |
| 4 | Re-validação manual de persistência — para cada salvamento crítico, repita o ciclo salvar → recarregar página → conferir banco manualmente. Não confiar em testes contratuais black-box (eles validam status code, não diff de UPDATE). | Confirma que UPDATE não sobrescreve com NULL nem com eco de leitura. |
| 5 | Erradicação dos fallbacks — remova else if (legacy_jsonb) apenas depois de 100% dos consumidores migrados E logs de warn zerados em prod por ≥24h. | Cadeia de consumo 100% relacional documentada em tabela. |
| 6 | DROP COLUMN com janela de observação — agendar ALTER TABLE ... DROP COLUMN <coluna_legada> apenas após ≥24-48h em prod sem regressão. Aprovação explícita do dono do produto. | Migration aplicada, doc canônica atualizada, memória ajustada. |
Gates obrigatórios
- ⏸️ Após passo 1: aprovação do plano antes de tocar em código.
- ⏸️ Após passo 5: revisão da cadeia de consumo + janela de observação antes do DROP.
- ⏸️ Após passo 6: atualização da doc canônica de feature, audit em
docs/audits/AUDIT_*.mde memóriamem://.
Testes obrigatórios (não negociáveis)
- Predicado puro como gate: extrair a lógica de "quando escrever via legado" em uma função pura testável (ex:
shouldUpdateFasesPlanasInCronograma(modoConfig, fases)). Cobrir com ≥5 testes unitários cobrindo cada combinação modo × payload. - Teste de integração com spy de Supabase client: capturar todos os
.update()emitidos durante a operação crítica e assertar que nenhum UPDATE comdata: NULLé enviado quando o payload em modo relacional traz dados válidos.
Anti-padrões proibidos
- ❌ Migrar escrita para o relacional e continuar gravando JSONB no mesmo handler sem gate (causa double-persistence overwrite).
- ❌ Confiar em testes contratuais (
status === 200) como prova de que a persistência funcionou. - ❌ Remover
DROP COLUMNda pendência sem janela de observação documentada. - ❌ Tratar a coluna JSONB como "fallback eterno" — código morto deve morrer no DROP.
Caso real de referência
A migração de olimpiadas.fases_por_nivel (JSONB) → fases_olimpiada.nivel_id (FK) foi executada em 5 etapas + 1 hotfix entre 2026-04-12 e 2026-04-27. Documentação completa em AUDIT_FASES_POR_NIVEL_2026-04-12.md e features/OLIMPIADAS_FASES_POR_NIVEL.md.
4. Definição de "Entrega Completa"
Uma entrega só é declarada concluída quando todos os itens abaixo estão presentes:
| # | Item | Referência |
|---|---|---|
| 1 | Código implementado e funcional | — |
| 2 | Testes passando no pipeline CI | TESTING_MASTER.md |
| 3 | Documentação atualizada na mesma entrega | DOCUMENTATION_MAINTENANCE.md |
| 4 | @audit executado sem itens 🔴 abertos | AUDIT.md |
| 5 | Para mudanças de banco: protocolo de 3 fases seguido | MIGRATION_SAFETY_PROTOCOL.md |
| 5a | Helpers SECURITY DEFINER usados apenas em policies RLS: NÃO conceder EXECUTE a anon/public (apenas authenticated). Exceção: fluxos pré-login com COMMENT ON FUNCTION ... IS 'public-by-design: ...'. | RLS_DESIGN_GUIDE.md — Grants em helpers SECURITY DEFINER |
| 6 | Feature flag declarada, gates simétricos backend+frontend, testes ON/OFF passando | §7 + FEATURE_FLAGS_LIFECYCLE.md |
| 7 | Se a entrega tocou docs/**, docs/.vitepress/**, scripts docs:* em package.json ou os workflows docs-validate.yml/deploy-docs.yml: bun run docs:build rodado localmente, sem dead link e sem warning evitável | DOCUMENTATION_MAINTENANCE.md — Docs Build Guardrails |
Se qualquer item estiver ausente, a entrega está incompleta.
O que NÃO constitui entrega completa
- ❌ "Código funciona mas testes ficam pra depois"
- ❌ "Testes passam localmente mas não rodei no CI"
- ❌ "Documentação será atualizada no próximo commit"
- ❌ "@audit identificou problemas mas são menores"
- ❌ "Migration foi aplicada mas não segui o protocolo de 3 fases porque era simples"
5. Migração Incremental de Cobertura
A cobertura de testes da plataforma cresce junto com o desenvolvimento, não em sprints separadas.
Regra
Cada feature tocada (nova ou atualizada) sai com cobertura adequada.
- Não existe sprint de "adicionar testes em tudo"
- Não existe backlog de "dívida de testes" que cresce indefinidamente
- A cada feature entregue, o percentual de cobertura do sistema aumenta ou se mantém — nunca diminui
Como funciona na prática
- Feature nova (Cenário A): nasce com 100% de cobertura dos comportamentos definidos
- Feature atualizada (Cenário B): o raio-x identifica gaps de cobertura; a Fase 2 os preenche antes da atualização
- Feature adjacente impactada: se a análise de impacto identificar features adjacentes, seus testes são verificados e complementados se necessário
Resultado esperado
Com o tempo, todas as features do sistema terão cobertura adequada — não porque houve um esforço dedicado de testes, mas porque cada interação com o código deixa a cobertura melhor do que encontrou.
→ Ref: TEST_MIGRATION_REPORT.md como exemplo do que essa migração gradual produz.
6. Nota sobre Validação com Zod
A adoção de Zod como validação de runtime segue o mesmo princípio de migração incremental descrito na seção 5.
Regra atual
| Contexto | Obrigatoriedade |
|---|---|
| Feature nova (Cenário A) | Zod obrigatório nas fronteiras de entrada de dados |
| Feature existente atualizada (Cenário B) | Zod adotado durante a atualização |
| Feature não tocada | Sem obrigação imediata |
Fronteiras de entrada que exigem Zod
- Payloads de entrada em Edge Functions (
req.json()) - Respostas de APIs de terceiros (Wasender, Mercado Pago)
- Dados de importação de planilhas (Excel/CSV)
- Parâmetros de URL e query strings em rotas públicas
Estratégia futura
A estratégia detalhada de adoção de Zod (inventário de fronteiras, priorização, schemas compartilhados) será documentada separadamente após auditoria white-box da codebase. Este documento formaliza apenas o princípio: toda fronteira nova já nasce com Zod.
6.1 Imports remotos em Edge Functions
Regra:
npm:é canônico.jsr:ehttps://deno.land/std@<v>/...aceitos.esm.shé proibido — CDN público sujeito a 522 (Cloudflare). Guard de CI:scripts/audit/no-esm-sh-imports.ts.
Política completa, exemplos de migração e camadas de defesa: EDGE_FUNCTION_IMPORTS.md.
7. Disciplina de Feature Flags
Toda feature nova (Cenário A) nasce atrás de flag, exceto correções de bug ou ajustes puramente visuais sem impacto funcional. Toda atualização (Cenário B) que mude contrato ou comportamento observável também passa por flag, mesmo que temporária para o rollout.
SSOT detalhado: FEATURE_FLAGS_LIFECYCLE.md. Resumo das regras aplicadas neste workflow:
Convenção canônica
| Item | Regra |
|---|---|
chave | snake_case; sub-flag por dot-notation (mural.noticias); kebab é legado e exige alias |
categoria | Coluna física (admin|especialista|escola|coordenador|diretor|portal|outros) — nunca inferida |
parent_chave | Obrigatório em sub-flag |
| Estado inicial | ativa_global=false |
Cobertura simétrica obrigatória
| Camada | Mecanismo | Onde |
|---|---|---|
| Backend | requireFeatureFlag(supabase, user, "chave", req) | Toda ação da edge function, incluindo leitura (list, list_active, get_*) |
| Frontend | <FeatureGate flag="chave"> ou useVisibleTabs | Renderização da UI + abas |
Sem o gate backend, a flag é decorativa e bypassável por chamada direta a invokeAction. Os dois lados são obrigatórios.
Ciclo de vida (5 estados)
planejada ──► canary ──► global ──► estável ──► aposentada| Estado | ativa_global | Canary | Critério |
|---|---|---|---|
| planejada | false | nenhum | Flag criada, gates no código |
| canary | false | ≥1 grupo ativo | Escolas/usuários piloto |
| global | true | mantido | Liberada para todos |
| estável | true | mantido | ≥90 dias sem incidente |
| aposentada | — | — | Gates e flag deletados na mesma entrega |
Nunca pular planejada → global direto. Ao aposentar: remover gate e registro do banco em uma única entrega — gate sem flag = 403 perpétuo; flag sem gate = lixo de configuração.
Cobertura mínima de testes (por flag nova)
| # | Teste | Camada |
|---|---|---|
| 1 | Backend gate OFF → 403 FEATURE_DISABLED | Deno (*.test.ts) |
| 2 | Backend gate ON → 200 + payload | Deno |
| 3 | Frontend gate (admin bypass + papel não-admin com/sem flag) | Vitest + RTL |
| 4 | Canary access (usuário em canary group + ativa_global=false → permitido) | Deno ou hook |
Anti-padrões proibidos
- ❌ Flag só no frontend (UI esconde mas backend serve dados)
- ❌
requireFeatureFlagapenas em mutações (read sem proteção vaza estado oculto) - ❌
categoriaderivada por heurística (use a coluna do banco) - ❌ Chave em kebab-case em features novas
- ❌ Duplicação semântica (ex:
cursosvsformacao) - ❌ Gate referenciando flag inexistente — registrar flag na mesma entrega que o gate
Referências Cruzadas
| Documento | Relação com este workflow |
|---|---|
| TESTING_MASTER.md | Guia técnico — pirâmide, camadas, templates, pipeline |
| AUDIT.md | Checklist @audit executado nas fases finais |
| MIGRATION_SAFETY_PROTOCOL.md | Protocolo de 3 fases para mudanças de banco |
| DOCUMENTATION_MAINTENANCE.md | Regra de documentação na mesma entrega |
| TEST_MIGRATION_REPORT.md | Exemplo de migração incremental de cobertura |
| TEST_ERROR_PLAYBOOK.md | Catálogo SSOT de erros E2E |
| FEATURE_FLAGS_LIFECYCLE.md | SSOT do ciclo de vida de feature flags (referenciado pela §7) |