Skip to content

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:


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, com parent_chave se 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

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:

  1. 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)
  2. 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)
    • Localização canônica de testes Deno (BLOQUEANTE): testes de Edge Function vão em supabase/functions/<nome>/__tests__/ e helpers em supabase/functions/_shared/__tests__/. NUNCA co-localizar *.test.ts na raiz da pasta da function — o bundler do Supabase CLI inclui .ts da raiz no bundle e o deploy falha com OOM (exit 135). Validado pelo guard scripts/check-edge-function-test-location.sh. Ver TESTING_MASTER.md §3.
  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:

  1. Implementar a feature guiada pelos testes escritos na Fase 2
  2. Rodar os testes localmente após cada mudança significativa
  3. O critério de conclusão é: todos os testes passam
  4. 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:

  1. Executar @audit conforme AUDIT.md
  2. Verificar que todos os testes passam no pipeline CI (não apenas localmente)
  3. Atualizar documentação conforme DOCUMENTATION_MAINTENANCE.md
  4. 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 requireFeatureFlag em todas as ações (incluindo leitura list_*/get_*)?
    • Frontend tem <FeatureGate> ou useVisibleTabs?
    • Categoria correta no banco (coluna feature_flags.categoria)?
    • Chave segue snake_case canônico?
    • Sub-flags coerentes com parent_chave?
    • Existe duplicação semântica (ex: cursos vs formacao)?
  • Desvios de padrão: anti-padrões identificados em relação à documentação (docs/)
    • Ex: usa fetch direto em vez de invokeAction, não tem registrarLog(), falta WITH CHECK, gate backend ausente

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:

  1. Escrever testes que documentam o comportamento atual da feature (como ela funciona hoje)
  2. 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 + useEffect para fetch, migrar para React Query + teste
  3. 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:

  1. Escrever testes novos para os comportamentos que a atualização adiciona/modifica (TDD — testes primeiro)
  2. Implementar a atualização
  3. Rodar todos os testes (baseline + novos) — todos devem passar
  4. 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:

  1. Executar @audit cobrindo a feature completa (não apenas o delta)
  2. Atualizar o documento SSOT criado na Fase 1 para refletir o novo estado
  3. 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_nivelfases_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

#PassoSaída esperada
1Inventário de consumidoresgrep -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).
2Escrita 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.
3Migraçã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.
4Re-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.
5Erradicaçã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.
6DROP 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_*.md e memória mem://.

Testes obrigatórios (não negociáveis)

  1. 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.
  2. 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 com data: 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 COLUMN da 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:

#ItemReferência
1Código implementado e funcional
2Testes passando no pipeline CITESTING_MASTER.md
3Documentação atualizada na mesma entregaDOCUMENTATION_MAINTENANCE.md
4@audit executado sem itens 🔴 abertosAUDIT.md
5Para mudanças de banco: protocolo de 3 fases seguidoMIGRATION_SAFETY_PROTOCOL.md
5aHelpers 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
6Feature flag declarada, gates simétricos backend+frontend, testes ON/OFF passando§7 + FEATURE_FLAGS_LIFECYCLE.md
7Se 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ávelDOCUMENTATION_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

  1. Feature nova (Cenário A): nasce com 100% de cobertura dos comportamentos definidos
  2. Feature atualizada (Cenário B): o raio-x identifica gaps de cobertura; a Fase 2 os preenche antes da atualização
  3. 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

ContextoObrigatoriedade
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 tocadaSem 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: e https://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

ItemRegra
chavesnake_case; sub-flag por dot-notation (mural.noticias); kebab é legado e exige alias
categoriaColuna física (admin|especialista|escola|coordenador|diretor|portal|outros) — nunca inferida
parent_chaveObrigatório em sub-flag
Estado inicialativa_global=false

Cobertura simétrica obrigatória

CamadaMecanismoOnde
BackendrequireFeatureFlag(supabase, user, "chave", req)Toda ação da edge function, incluindo leitura (list, list_active, get_*)
Frontend<FeatureGate flag="chave"> ou useVisibleTabsRenderizaçã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
Estadoativa_globalCanaryCritério
planejadafalsenenhumFlag criada, gates no código
canaryfalse≥1 grupo ativoEscolas/usuários piloto
globaltruemantidoLiberada para todos
estáveltruemantido≥90 dias sem incidente
aposentadaGates 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)

#TesteCamada
1Backend gate OFF → 403 FEATURE_DISABLEDDeno (*.test.ts)
2Backend gate ON → 200 + payloadDeno
3Frontend gate (admin bypass + papel não-admin com/sem flag)Vitest + RTL
4Canary 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)
  • requireFeatureFlag apenas em mutações (read sem proteção vaza estado oculto)
  • categoria derivada por heurística (use a coluna do banco)
  • ❌ Chave em kebab-case em features novas
  • ❌ Duplicação semântica (ex: cursos vs formacao)
  • ❌ Gate referenciando flag inexistente — registrar flag na mesma entrega que o gate

Referências Cruzadas

DocumentoRelação com este workflow
TESTING_MASTER.mdGuia técnico — pirâmide, camadas, templates, pipeline
AUDIT.mdChecklist @audit executado nas fases finais
MIGRATION_SAFETY_PROTOCOL.mdProtocolo de 3 fases para mudanças de banco
DOCUMENTATION_MAINTENANCE.mdRegra de documentação na mesma entrega
TEST_MIGRATION_REPORT.mdExemplo de migração incremental de cobertura
TEST_ERROR_PLAYBOOK.mdCatálogo SSOT de erros E2E
FEATURE_FLAGS_LIFECYCLE.mdSSOT do ciclo de vida de feature flags (referenciado pela §7)