Files

13 KiB
Raw Permalink Blame History

Contributing — DeskcommCRM

🇧🇷 Português · 🇺🇸 English

Antes de começar

  1. Abra o repositório no seu assistente de código (Claude Code, Codex, Cursor, OpenCode ou Antigravity): o guia deskcomm-contribuir (.agents/skills/deskcomm-contribuir/SKILL.md) mede antes do PR o que a triagem mede depois — branch atrasada, tripla de migration, marca do fork no diff, fragmento de release — e arma os hooks de git com bash .agents/skills/deskcomm-contribuir/scripts/armar-hooks.sh. Para ter os guias em qualquer pasta: bash scripts/instalar-guias.sh. Vai editar um guia? Rode bash scripts/instalar-guias.sh --fonte . no seu clone — no Claude Code a skill global vence a do projeto, e sem isso você testaria a versão da main, não a sua.
  2. Leia CLAUDE.md — convenções não-negociáveis.
  3. Leia ARCHITECTURE.md — visão de 1 página.
  4. Identifique o epic de origem em docs/stories/epics/MASTER.md.

Fluxo

Branches

feat/EPIC-XX-short-slug         # nova feature
fix/EPIC-XX-short-slug          # bug fix
chore/short-slug                # chore (deps, configs)
docs/short-slug                 # apenas docs

Commits

Conventional commits + escopo EPIC-XX:

feat(EPIC-04): kanban drag-and-drop com fractional indexing
fix(EPIC-03): cron recover-stuck-messages marcando sending stuck >5min como failed
docs(EPIC-12): mark complete + wave log

Mensagens em PT-BR são aceitas. O assunto deve ser imperativo e ≤72 chars.

epic-executor

Mudanças grandes seguem docs/stories/epics/. O epic-executor consome o frontmatter (epic_id, priority, depends_on, status) e executa wave-by-wave com validação E2E continuous.

Ao finalizar um epic:

  1. Atualizar frontmatter status: pending → completed (partial: ...) ou status: completed.
  2. Append "Wave Completion Log" no final do arquivo.
  3. Atualizar a row correspondente em docs/stories/epics/MASTER.md.

PR process

  1. Branch a partir de main.

  2. Implementar. Adicionar testes (E2E pra fluxos, unit pra lógica pura).

  3. Definition of Done. A lista está separada em duas por um motivo: até hoje ela misturava o que uma máquina reprova com o que só uma pessoa percebe, e contribuidor marcava o checklist inteiro de boa-fé para ser barrado por um gate que ninguém tinha contado a ele.

    O que o CI reprova sozinho — rode antes de abrir o PR e não terá surpresa:

    pnpm cercas    # ~30 s: as guardas estruturais (baseline, MANIFEST, docs, workflows, espanhol do i18n, fragmentos de .changes/) — o que mais reprova PR
    pnpm typecheck && pnpm lint && pnpm lint:channels && pnpm test:unit && pnpm test:shell && pnpm build
    pnpm test:db   # precisa de Docker; sobe um Postgres limpo e aplica o baseline
    

    O que o CI NÃO vê — fica com você e com a revisão, e é onde moram os defeitos caros:

    • RLS habilitada e policy tenant_isolation_<tabela>_all se você criou tabela tenant-aware (o teste de isolamento cobre uma lista fixa de tabelas; a sua nova não entra sozinha)
    • Audit log emitido se há mutação relevante
    • Rate limit aplicado se a rota é pública
    • Zod validando todo input externo
    • Sem console.log esquecido (use lib/logger.ts). O pnpm lint não reprova isso — a regra está como aviso, então ele passa verde; a conferência é humana
    • Env vars novas em .env.example e lib/env.ts, com default que não quebre instalação nova
    • Mudança de schema saiu como tripla: arquivo em supabase/migrations/, apêndice idempotente no supabase/baseline.sql e linha no MANIFEST.md. O kit self-host aplica só o baseline — migration que não chega lá não chega em quem instalou numa VPS. Nenhum job de CI confere isso
    • Se você tocou Dockerfile*, docker-compose*.yml ou hostgator-setup-kit/: a mudança alcança quem já instalou. Lei em docs/doctrine/packaging.md. O CI reprova serviço build:-only, instalação em tag móvel e imagem quebrada (imagens-ok); o que fica com você é o resto: variável nova com default que não quebre .env antigo, e a atualização não pedindo edição manual de arquivo. Nenhum bump pode exigir que o operador da VPS edite alguma coisa na mão — se exigir, abra issue com plano de migração em vez de PR
    • Docs atualizadas se mudou contrato (PRD/spec)
    • pnpm test:e2e (subset relevante) — opcional se você contribui de fora, ver abaixo
  4. Abrir PR contra main. Description deve referenciar o epic e listar evidências (logs/screenshots dos testes).

  5. Tocou um documento de autoridade? Corrija as afirmações de estado daquele documento — as que dizem o que está ativo, o que falta, o que aponta para onde. Não saia caçando nos outros: a dívida decai sozinha se ninguém a alimentar. Achados medidos, com o comando de cada um, em docs/audits/2026-08-14-afirmacoes-de-estado.md.

  6. CI deve passar antes de merge. Obrigatórios: verify, invariants (isolamento RLS), build-and-size, e2e e imagens-ok.

    O imagens-ok (em .github/workflows/publish-image.yml) constrói as três imagens que o self-hoster instala, roda em PR e bloqueia desde 2026-08-13.

    Verde no e2e não é "jornada provada": ele mesmo imprime, no resumo, quais specs não cobriu. Quais são, leia do próprio workflow em vez de desta linha — ela já disse que a de fora era vps-fresh-onboarding, a instalação do zero, e desde o PR #983 essa roda no CI:

    git show origin/main:.github/workflows/e2e.yml | grep -A4 'FORA_DO_CI:'
    

    E mesmo a jornada que TEM gate continua devendo a prova pela tela quando você mexe nela (DoD 12): gate prova que não regrediu, não que a experiência ficou boa.

    Esta lista dizia "três obrigatórios" e chamava o e2e de não-bloqueante. Estava desatualizada nos dois pontos, e quem a usasse como régua mediria contra a régua errada. Confira na fonte antes de confiar em qualquer lista escrita: gh api repos/melgarafael/DeskcommCRM/branches/main/protection --jq '.required_status_checks.contexts'

Pegando uma issue — o protocolo

Existe porque já falhamos nisto: em 2026-07-30 abrimos uma issue, um contribuidor começou a resolvê-la, e um mantenedor entregou a mesma correção 21 segundos antes sem que nenhum dos dois pudesse ver o outro. O trabalho dele foi para o lixo. As regras abaixo são para que isso não se repita.

  1. Comente "pego esta" antes de codar. Uma linha basta. Um mantenedor te atribui a issue — a partir daí ela é sua e ninguém mais mexe.
  2. Issue com pessoa atribuída não se duplica. Se você quer ajudar mesmo assim, comente oferecendo; não abra PR concorrente.
  3. Mantenedor não implementa issue marcada good first issue ou help wanted sem antes se atribuir a ela publicamente. Se você vir uma dessas sem dono, ela é sua para pegar — essa é a garantia que damos em troca do passo 1.
  4. Sem resposta em 48h depois do "pego esta"? Comece assim mesmo e diga no PR. A demora é nossa, o custo não pode ser seu.

Se você está contribuindo de fora (fork) — leia isto

Uma coisa vai parecer erro seu e não é:

  • Os workflows ficam parados esperando aprovação no seu primeiro PR. É política do GitHub para quem nunca contribuiu antes. Um mantenedor libera; do segundo PR em diante roda sozinho. Se demorar, comente no PR.

Abra o PR de um ramo com nome, nunca do main do seu fork. Se o main do fork já tem personalizações suas — e ele quase sempre tem, porque é dele que a sua VPS puxa —, o PR propõe essas personalizações ao produto inteiro. Isso não gera conflito e não acende gate nenhum: elas entram em silêncio para todas as instalações. Foi medido (PR #465): sete arquivos com a marca de um cliente, seis deles mergeando sem um único conflito. O caminho é git checkout -b fix/o-que-voce-conserta a partir da main deste repositório, com só o seu conserto dentro.

Com "Allow edits by maintainers" ligado no seu PR, o projeto pode empurrar um conserto direto na branch do PR — um ajuste mecânico, ou a main trazida para dentro quando há conflito. Sempre como commit novo: nunca --force, nunca rebase, e os seus commits ficam como estão. Avisamos no PR antes de empurrar. Quando isso acontecer, traga a branch antes de continuar (git pull --no-rebase) e só então empurre de novo; um --force do seu lado apagaria o que foi empurrado do lado de cá. Com a opção desligada, o conserto vai numa branch nossa. Nos dois caminhos, o trabalho que é seu entra com você como autor.

A marca da sua instalação não se troca editando código. Não altere DEFAULT_APP_NAME em lib/branding.ts, nem os títulos em app/. O banco manda (platform_branding, organizations.settings.branding), APP_NAME no .env é a semente que o install.sh pergunta, e o resto é a tela Configurações › Marca. Receita inteira em docs/white-label.md. Editar a constante troca o padrão do PRODUTO — e a sua marca some no próximo git pull, o que é a razão prática de o caminho suportado ser melhor para você também.

E sobre o pnpm test:e2e do DoD: rodar a suíte completa exige Docker, banco semeado e WAHA local. Não travamos PR externo nisso — mande o que conseguiu provar (unit + descrição do que testou na mão), que a prova de tela fica com o mantenedor. Exigir prova sem entregar a ferramenta de produzi-la seria pedágio, não rigor.

tests/invariants/ é congelado — e isso vale para o COMPORTAMENTO, não só para o arquivo

Os arquivos de tests/invariants/ guardam leis do produto, e mexer neles pede justificativa escrita. Duas coisas que não estão óbvias e já custaram tempo a quem contribui:

  1. O guarda é um hook local do mantenedor (core.hooksPath=loop/hooks), não um check do CI. Você não vai vê-lo reprovar no seu fork — o que você vê é a integração travar depois.
  2. Um PR pode reprovar um invariante sem tocar no arquivo dele. Se o seu conserto muda o comportamento que a lei afirma, o vermelho aparece lá. Isso não é um descuido seu — é o sinal de que existem duas regras concorrentes, a que está escrita e a que você propõe.

Quando acontecer, não apague nem afrouxe a asserção: diga no PR qual é a sua razão e deixe a escolha explícita. Quem tria escreve a mudança do invariante com a justificativa exigida, ou ajusta o conserto para preservar a lei antiga — e a decisão fica registrada no PR, que é onde ela serve para a próxima pessoa.

Texto de tela: toda frase nova precisa do espanhol

O produto fala português e espanhol, e o CI reprova frase nova sem tradução. A regra não estava escrita aqui até 16/09/2026, e um PR de primeira contribuição foi reprovado por ela — a falha era nossa, não de quem contribuiu.

Se você acrescentou uma frase que aparece na tela, ela passa por t("...") e ganha uma linha em lib/i18n/dicionario.ts:

"Digite o identificador do modelo": { es: "Escribe el identificador del modelo" },

A chave é o texto em português (não um código). Só o espanhol precisa de linha; o resto degrada para o português de propósito.

Para conferir antes de abrir o PR, sem rodar a suíte inteira:

pnpm test:unit tests/unit/i18n-espanhol-cobre-a-tela.test.ts

Ele reprova nas duas direções: chave usada na tela sem espanhol, e prosa em português que não passou por t(). Se você não fala espanhol, mande assim mesmo e diga no PR — a tradução é trabalho de dez segundos para quem tria, e não é motivo para segurar um conserto.

Anti-patterns proibidos

Lista completa em CLAUDE.md. Os mais letais:

  • Trigger Postgres fazendo HTTP
  • Service role usado em handler sem filtrar organization_id manualmente
  • getSession() no backend (use getUser())
  • API key em query string
  • Bearer plaintext no DB
  • console.log em código merged

Setup local

Veja README.md §Como rodar local.

Suporte

GitHub Discussions — é o canal público, funciona para qualquer pessoa e é onde a resposta fica registrada para quem vier depois. Para bug, abra uma issue.

Se for algo que não cabe em público (segurança, por exemplo): rafael@maudibrasil.com.br — o mesmo endereço do CODE_OF_CONDUCT.md.

Esta seção apontava para um Discord interno cujo convite mora num Notion privado — inalcançável justamente para quem mais precisava dela, que é quem vem de fora. Ficou aqui como lembrete de que canal de suporte se testa pelo lado de fora.