Skip to content

Fluxo de trabalho ​

Como uma task anda do início à entrega. Somos várias pessoas — e vários agentes — no mesmo código, em três papéis: quem desenvolve abre a task, trabalha numa branch própria e pede revisão; quem revisa lê o trabalho sem ter participado dele e diz se está no padrão; quem integra leva o que foi aprovado ao develop, ao staging e, no corte de versão, ao main. Vale para pessoas e para agentes (Claude e outros), e não depende do GitLab nem do GitHub: o fluxo é do repositório e da ferramenta de tasks (taskin), não da hospedagem.

Por que assim ​

  • A develop é de todos. O que entra nela vai para o trabalho dos outros. Uma branch por task mantém o que está pela metade fora do caminho de quem trabalha em outra coisa.
  • Pouca coisa aqui é local. O Directus é gerado a partir dos tipos: um campo, um nome ou uma permissão muda o banco, as extensões e as policies. A revisão é onde isso aparece antes de chegar na develop.
  • Quem revisa não é quem fez. Uma revisão feita por quem escreveu o código confirma o que o código faz, não o que ele deveria fazer. Por isso a revisão é de uma pessoa — ou de uma sessão de agente — que só conhece a task e o padrão do repositório.
  • O que publica é protegido; o que integra, não. O main dispara versão e publicação. Entre ele e a develop há a staging, onde cada task vira um commit só: é o que se lê para gerar a versão e o que se promove quando a versão é cortada.
  • Vale para todo mundo, inclusive para quem revisa: o código de @sidartaveloso também passa pelo mesmo caminho quando outra pessoa estiver no papel de revisor.

O fluxo é combinado, não imposto pela hospedagem. Por isso ele está escrito, a ferramenta o protege onde dá (taskin, pre-push), e os agentes o seguem.

As três branches ​

BranchO que éComo se escreve nelaQuem
developIntegração. Histórico completo, com os commits de TDD.Merge direto da branch da task, depois da revisão: chore: integra feat/task-NNN no develop.quem integra
stagingCandidato a entrega. Um commit por task.Promoção: git cherry-pick -m 1 do merge commit da task, com a mensagem conventional da task. A tag task-NNN nasce aqui. É o done.quem integra, pelo comando
mainLançado. Dispara versão e publicação.Só no corte de versão: recebe a staging por fast-forward. O histórico do main é os squashes das tasks — é o que o gerador de versão lê.quem integra, no corte de versão

Nomes, de propósito: staging é a environment branch permanente (GitLab Flow), e já é o nome da imagem que sai dela (staging-latest). Não é release/x.y (branch temporária do Git Flow, apagada após a versão) nem next/canary (canal de pré-lançamento npm).

O ciclo de uma task ​

Três coisas que o diagrama diz e vale repetir:

  • A revisão devolve, não corrige. O achado volta para quem desenvolve, que corrige na mesma branch e pede revisão de novo. Duas pessoas (ou duas sessões) escrevendo na mesma branch é como um achado se perde.
  • done quer dizer "está no staging", não "o código ficou pronto". Entre o fim do desenvolvimento e o done há a revisão e a promoção.
  • Ninguém escreve no main por task. O main recebe a staging inteira, no corte de versão, por fast-forward. É o único momento em que versão e publicação acontecem.

Estados da task ​

EstadoQuem põeComandoO que precisa ser verdade antes
pendingquem criapnpm taskin new—
in-progressquem desenvolvepnpm taskin start NNNdevelop limpa e igual à remota
in-reviewquem desenvolvepnpm taskin review NNNbranch de task; pnpm lint e pnpm typecheck verdes
donequem integrapnpm taskin finish NNNrevisão aprovada; merge no develop feito; promovida para staging

Passo a passo ​

1. Pegar a task ​

bash
git checkout develop && git pull
pnpm taskin list
pnpm taskin start 047
git checkout -b feat/task-047
  • Tasks novas só com pnpm taskin new (o taskin numera); nunca escrevendo o .md à mão.
  • O taskin start grava o estado in-progress na branch atual. Rode-o com a develop limpa e igual à remota, antes de criar a branch; se a develop estiver em checkout em outro worktree, crie a branch a partir dela (git switch -c feat/task-047 origin/develop) e rode o taskin start nela.
  • Uma branch por task, com o nome feat/task-NNN (ou fix/task-NNN). Não use branches longas que juntam várias tasks.

2. Trabalhar na branch ​

  • Teste antes do código. O teste vermelho entra num commit; a implementação que o deixa verde entra no seguinte. Um teste escrito depois valida o que o código faz, não o que ele deveria fazer.

  • Commits pequenos, no padrão tipo(escopo): descrição, em português, como no git log.

  • pnpm lint, pnpm typecheck, pnpm format e pnpm test têm de passar. Erro se corrige na hora; nada de --no-verify, noqa ou @ts-ignore.

  • Decisão técnica que muda modelo, contrato ou arquitetura vira um RDT em docs/RDT/ (ver o README.md de lá), e espera a aprovação de quem revisa antes da implementação.

  • Para trazer o que entrou na develop enquanto você trabalhava:

    bash
    git fetch origin && git merge origin/develop

3. Pedir revisão ​

bash
pnpm taskin review 047

Um comando só. O taskin review:

  1. confere que você está numa branch de task. Se não, explica e para, com a task ainda em in-progress;
  2. roda pnpm lint e pnpm typecheck. Se algum falhar, a task também não vai para revisão: corrija e rode de novo;
  3. passa a task para in-review e commita o doc dela.

Depois disso, avise quem integra que a task está pronta para revisão — e pare. Quem desenvolve não faz merge na develop, não promove e não roda taskin finish. O doc da task precisa dizer o que mudou, por quê, e como testar (comandos, tela, hardware usado); se mexeu no modelo do Directus, diga.

4. Revisar ​

Quem integra chama quem revisa — uma pessoa que não participou da task, ou uma sessão de agente sem o contexto da implementação. O brief é mínimo de propósito, e é sempre o mesmo:

Revise a task 047 pelo padrão do repositório.
Escreva o resultado em TASKS/assets/revisao-task-047.md.

Mais que isso já contamina: dizer "confira se o cherry-pick está certo" entrega a conclusão junto com a pergunta. O que a revisora precisa saber está no doc da task, no CLAUDE.md e nas RDT — e o modelo do arquivo que ela escreve está em TASKS/assets/modelo-revisao-task.md.

Quem revisa:

  • lê o doc da task, o CLAUDE.md, as RDT que a task cita e o diff da branch contra a develop;
  • escreve TASKS/assets/revisao-task-047.md: cada achado com severidade (bloqueia / deveria / sugestão), arquivo:linha, o que está errado, por que importa e a correção proposta;
  • roda os gates dos pacotes tocados (test, typecheck, lint, format:check), o test:docker e o e2e quando a task alcança o que eles cobrem;
  • não corrige. Com achado que bloqueia, devolve: quem desenvolve roda pnpm taskin start 047, corrige na mesma branch e pede revisão de novo.

O critério que libera o finish: o TASKS/assets/revisao-task-047.md existe, traz um veredito explícito, e nenhum achado de severidade bloqueia continua de pé. Achado "deveria" e "sugestão" não seguram a task — viram item no doc dela ou task nova, e isso fica escrito no veredito. Sem o arquivo da revisão não há aprovação: a conversa não é a revisão, e o que não está no repositório some junto com a sessão.

5. Integrar ​

Com a revisão aprovada, quem integra:

bash
# no develop
git merge --no-ff feat/task-047 -m "chore: integra feat/task-047 no develop"

# promoção para o staging: um commit só, a tag, e a branch some
pnpm --filter @geocontrol/ferramentas promover-para-staging 047 --simular
pnpm --filter @geocontrol/ferramentas promover-para-staging 047

pnpm taskin finish 047

A promoção faz, pela ordem: git cherry-pick -m 1 do merge commit da task, sem commitar; o commit único na staging, com a mensagem conventional da task (feat(task-047): …, tirada do - Tipo: e do título do doc); a tag anotada task-047 nesse commit; push de staging e da tag juntas (git push --atomic) para cada remoto de entrega; exclusão da branch da task nesses remotos; volta para a develop e exclusão da branch local. Com --simular ela só mostra o plano, sem rodar nada.

Os remotos de entrega são origin e github, escritos no comando, não deduzidos do git remote. Ali convivem espelhos e checkouts de trabalho de outras máquinas (hoje há um desktop apontando para outra estação): deduzir a lista publicaria a entrega numa máquina pessoal e apagaria a branch da task lá. Para outra lista, --remoto origin --remoto github ou GEOHUB_REMOTOS=origin,github.

Ela recusa antes de começar o que não daria certo: fora da develop, árvore suja, staging que não existe aqui, tag task-047 que já existe (a task já foi promovida), merge da task que não está na develop, mais de um merge citando a task (aí ela lista os candidatos e pede --merge <sha>), remoto de entrega que não responde, e staging local atrás da de algum remoto — que é o caso que deixaria a entrega divergente entre eles. Se um passo falhar — um conflito no cherry-pick —, ela para ali e diz como sair do meio.

A busca do merge é por primeiro pai: sem isso ela acharia também o git merge origin/develop que o passo 2 ensina a fazer dentro da branch — o assunto que o git escreve sozinho cita a task —, e a promoção recusaria por ambiguidade no caminho normal. Medido na develop: 49 das 150 tasks com merge lá teriam mais de um candidato; por primeiro pai, 19.

Essas 19 são integrações múltiplas de verdade — a mesma branch mesclada várias vezes, ou branches diferentes para a mesma task. Para elas nenhum cherry-pick isolado leva o trabalho inteiro, e o --merge <sha> produz um squash incompleto sob a mensagem da task. Aí a entrega se monta da ponta da branch, não de um merge: git merge --squash feat/task-NNN na staging, com a mensagem conventional da task, e a tag à mão. Uma branch e uma integração por task evitam o caso; devolução de revisão depois do merge e segunda branch para a mesma task o recriam.

A ordem importa num ponto medido: a branch local só é apagada depois de voltar para a develop. De dentro da staging o git branch --delete recusa, porque o commit da promoção é um squash e a branch não é ancestral dele.

6. Cortar versão ​

Quando o staging tem o que se quer lançar, o main o recebe por fast-forward e ganha a tag de versão. É o único passo que dispara versão e publicação, e ele é do roteiro de release — nunca de uma task.

bash
git checkout main && git merge --ff-only staging
export GEOHUB_CORTE_DE_VERSAO=1
pnpm version                      # lerna version --conventional-commits
git push github main --tags       # o lerna só empurra para o remoto dele
unset GEOHUB_CORTE_DE_VERSAO

O --ff-only é a conferência: se ele recusa, alguém escreveu no main fora do corte, e isso se resolve antes de seguir. O GEOHUB_CORTE_DE_VERSAO=1 é o que o pre-push pede para deixar o main ir (ver abaixo).

A variável é exportada antes do pnpm version, e não só na linha do push, porque quem empurra o main é o próprio lerna: ele faz o push do commit de versão e das tags ao terminar (--no-push é o que desligaria isso, e o lerna.json não desliga). Pondo a variável só na linha seguinte, o lerna falha no pre-push depois de já ter escrito o commit de versão, as tags e o CHANGELOG — no passo mais caro do fluxo.

O lerna empurra para o remoto dele (origin), um só. O github é espelho e recebe o main e as tags na linha seguinte: se só um remoto tivesse o main, o espelho deixaria de ser espelho justamente no que o fluxo diz não depender da hospedagem.

O que o main dispara, e por isso ele é protegido:

  • a versão: pnpm version é lerna version --conventional-commits, que lê os assuntos conventional do histórico para decidir o próximo número e escrever o CHANGELOG. É por isso que a staging tem um commit por task, com o tipo da task no assunto: é esse histórico que o main recebe, e não os commits de TDD da develop;
  • a publicação: pnpm publish (lerna publish from-package), e o .gitlab-ci.yml, cuja .rules-template roda a esteira de pacote em push no main e em $CI_COMMIT_TAG.

A imagem :staging-latest não sai daqui: ela é do ambiente de staging, e quem a gera é o build do packages/deploy (comporTags, pelo ambiente), rodado à mão. O nome da branch e o da tag da imagem coincidem de propósito, mas são coisas diferentes.

A staging nasce uma vez, do main ​

bash
git fetch origin && git branch staging main
GEOHUB_PROMOCAO=1 git push origin staging
GEOHUB_PROMOCAO=1 git push github staging

Depois disso ela é permanente: só a promoção escreve nela, e o main a recebe por fast-forward no corte.

O pre-push protege as duas ​

O fluxo não depende da hospedagem: proteção de branch do GitLab ou do GitHub não serve, porque o repositório já trocou de host uma vez e os dois remotos convivem hoje. Quem protege é o .husky/pre-push, que recusa:

pushpassa quando
stagingGEOHUB_PROMOCAO=1 — o promover-para-staging põe sozinho
mainGEOHUB_CORTE_DE_VERSAO=1, à mão, no corte de versão
apagar staging ou mainnunca

É um guarda contra o engano, não contra quem quer burlar: quem põe a variável à mão está dizendo que sabe o que está fazendo. As provas de que ele recusa estão em scripts/pre-push-protege-entrega.test.mjs (pnpm test:scripts).

Para agentes ​

  • Siga este fluxo também quando a pessoa pedir só "termine a task": pare na task em in-review, com o aviso a quem integra.
  • A sessão que desenvolveu não inicia a sessão que revisa: quem a inicia é a sessão principal, para o relatório chegar a ela. A revisora nasce sem contexto da implementação, de propósito.
  • Não faça merge na develop, promoção para staging nem taskin finish sem que quem integra peça. Nunca escreva no main.
  • Push só com autorização da pessoa com quem você está trabalhando.
  • taskin finish em modo autopilot commita tudo o que estiver na árvore, inclusive arquivo de outra pessoa: árvore limpa antes, sempre.