Arquitetura do núcleo do BeeverForge

Ciclo de vida de um ticket

Um ticket na coluna de entrada do quadro se torna um job: um workflow durável que o dimensiona, prepara uma caixa, faz um agente implementá-lo, executa os controles do próprio projeto, faz outro agente revisá-lo, abre um pull request e o acompanha até o merge e, onde configurado, pela promoção.

Estados do job#

Os estados são a enumeração JobState em beeverforge/contracts/state.py. Esse módulo só declara os estados; não contém nenhuma tabela de transições. As transições são feitas pelo executor de jobs (orchestrator/machine.py), pelo workflow do job (runtime/temporal/workflow.py) e pelo caminho de promoção (orchestrator/promotion.py). O diagrama mostra as transições que esses módulos fazem.

Máquina de estados do job O caminho de trabalho vai de todo a done. Os estados em espera aguardam uma pessoa. Três estados declarados não são atribuídos pelo código. todo spec_validation preparing planning implementing validating reviewing pr_open merged staging_verifying repairing awaiting_prod_approval prod_releasing prod_verifying done rolling_back paused Limite de uso; retoma sozinho EM ESPERA: AGUARDA UMA PESSOA needs_refinement blocked on_hold failed skipped Pulo de uma pessoa, umapergunta prévia ou cartão retirado ready ci_waiting staging_deploying Declarados, não atribuídos pelo código
Estados de trabalho, o caminho de promoção, os estados em espera e os estados declarados que o código não atribui. Os estados com borda tracejada são opcionais ou não são alcançados.
  • Um job também pode terminar em done diretamente a partir de merged quando nada está configurado depois do merge, e a partir de todo quando o dimensionamento divide o ticket em filhos.
  • planning só é alcançado quando o projeto define planner_stage: true.
  • O caminho de promoção depois de merged só é executado quando a promoção está habilitada para o job. Por padrão, um job para no merge do pull request.
  • Qualquer estado de trabalho pode ficar em espera. needs_refinement significa que o próprio ticket precisa de uma pessoa; failed significa que a preparação do ambiente falhou, não o ticket; blocked retém uma decisão que o agente pediu a uma pessoa; on_hold cobre os demais impedimentos.
  • ready aparece apenas como mapeamento para uma coluna do quadro. ci_waiting e staging_deploying estão declarados na enumeração e não são atribuídos em lugar nenhum.

O cartão no quadro tem seu próprio ciclo de vida, menor, com uma tabela de transições explícita; ele é descrito em Princípios de engenharia.

Estações, de ponta a ponta#

1. Consulta e vagas#

Um agendamento do Temporal inicia o workflow de consulta a cada 3 minutos por padrão; um ciclo que se sobreporia a outro em andamento é ignorado. Cada ciclo inicia no máximo tantos jobs quantas vagas livres houver: BEEVERFORGE_MAX_CONCURRENT_JOBS (padrão 1) menos os jobs já em andamento, na ordem do quadro. Um projeto cuja caixa precisa de uma prova e não tem uma válida não é recolhido; a consulta informa o motivo.

2. Dimensionamento e divisão automática#

Antes de o job ser executado, uma passada de dimensionamento lê o ticket e o código (ADR-0013). Ela vem ativada por padrão. Seu veredito é um entre: cabe, pouco claro ou dividir. Pouco claro deixa o ticket em espera em needs_refinement. Dividir registra os filhos na coluna de entrada e fecha o pai; se a própria divisão falhar, o ticket fica em espera com a proposta para uma pessoa dividir manualmente. Uma passada de dimensionamento que não consegue julgar não interrompe o job: o job é executado sem dimensionamento e o cartão informa que o dimensionamento não foi executado.

3. Perguntar antes de gastar#

Um projeto pode fazer o job reunir contexto antes de gastar uma passada de implementação e perguntar no cartão quando o contexto reunido deixa uma pergunta em aberto (ADR-0048). Isso vem desativado por padrão: exige preflight.gather: true e okf_gate: enforce. Quando pergunta, o job termina como skipped com a nota de que perguntou antes de começar, e uma pergunta sem resposta é lembrada uma vez após 48 horas.

4. Controle da especificação#

O controle da especificação é determinístico. Exige um objetivo e critérios de aceitação, e recusa um ticket cujas listas de escopo incluído e excluído se sobrepõem. Uma recusa deixa o ticket em espera em needs_refinement com o motivo. Ele é executado depois do dimensionamento.

5. Preparação e configuração#

Em preparing, o job prepara seu espaço de trabalho na caixa e executa os comandos setup: do manifesto, com tempo limite de 1.800 segundos. Uma falha de configuração encerra o job como failed, porque é o ambiente, não o ticket, que precisa de atenção.

6. Plano e um único executor#

Um único agente executor planeja e implementa o ticket (ADR-0014). Uma passada de planejamento separada só é executada quando o projeto define planner_stage: true; por padrão ela fica desativada.

7. Controles de validação e reparo limitado#

O job executa na caixa os controles validate: declarados pelo projeto. Enquanto um controle bloqueante está vermelho, o agente faz reparos, até repair_max_attempts (padrão 2, no máximo 10) e dentro do teto de custo e do orçamento de esforço do ticket (effort_budget_turns, padrão 400). Um job que continua vermelho depois disso fica em espera em on_hold com o trabalho parcial no branch.

Reparo de supressões (ADR-0011). Quando o diff adiciona uma supressão como noqa, type: ignore, pragma: no cover ou nosec, o job executa até suppression_repair_max_attempts passadas (padrão 1) para corrigir a causa. Uma passada que quebra um controle deixa o job em espera; as supressões que restam são mostradas ao revisor.

8. Revisão independente#

Um agente revisor, em sua própria sessão, recebe o ticket, o diff, os resultados dos controles e as restrições, e nunca o relato do executor sobre o próprio trabalho. review_mode é advisory por padrão: os achados são publicados e não bloqueiam. Só com review_mode: blocking uma rejeição com achados acionáveis dispara uma passada de reparo (review_repair_max_attempts, padrão 1) seguida de uma nova revisão (ADR-0006).

9. Pull request#

O job publica o branch e abre o pull request. A abertura é buscar e depois atualizar: se já existe um pull request aberto para o branch, ele é reutilizado e seu título e corpo são atualizados, de modo que uma nova tentativa não cria um segundo. O resumo do próprio agente vai para o pull request, marcado como uma afirmação que nenhum controle verifica.

10. Reparo guiado pela CI#

Enquanto o pull request está aberto, o workflow o acompanha, qualquer que seja a política de merge (ADR-0004). Ele consulta a cada 2 minutos na primeira hora e a cada 15 minutos depois disso. Uma CI vermelha dispara até 2 passadas de reparo; um branch que ficou atrás da sua base é atualizado até 6 vezes; quando nenhum check informou nada após 10 minutos, o job diz isso em vez de interpretar o silêncio como verde. Após 14 dias sem merge, o job fica em espera.

11. Merge sobre a base atual#

Com o merge_policy: human padrão, uma pessoa faz o merge. Com merge_policy: auto, o job faz o merge só quando os controles estão verdes, nenhuma revisão bloqueante o rejeitou, não resta nenhuma supressão grave, a mudança não é avaliada como de alto risco, não toca nenhum caminho protegido, o piso e o diff puderam ser lidos e a contagem de testes não diminuiu, entre as condições de orchestrator/merge_policy.py. Antes, ele leva o branch para a base atual e executa os controles de novo se a base mudou (ADR-0003).

12. A vaga é liberada no merge#

O job mantém sua vaga até o merge do seu pull request ou, quando a promoção está habilitada, até a promoção terminar; um pulo também a libera. Terminar a passada do agente não libera a vaga, e um job em espera a mantém (ADR-0007, ADR-0010). Por isso, o próximo ticket parte de uma base que inclui este.

13. Observação da implantação e promoção#

Quando o manifesto configura post_merge_deploy, um workflow filho independente observa a cada minuto a implantação do próprio projeto e informa o resultado; ele não ocupa uma vaga e não bloqueia (ADR-0005). Quando a promoção está habilitada, o job verifica o staging e depois espera em awaiting_prod_approval por uma pessoa, por até 3 dias antes de ficar em espera. A aprovação chega ao workflow como uma resposta selada que é verificada antes de qualquer ação e só pode ser usada uma vez. Após a aprovação, o job cria a tag da versão, verifica a produção e termina em done, ou reverte e fica em espera.

14. Em espera com opções#

Um job que não pode continuar fica em espera e move seu cartão para a coluna de atenção do quadro com um comentário que explica o motivo. As opções oferecidas são retomar, que continua o trabalho preservado, e pular, que libera a vaga, além das opções de decisão que o job tenha levantado. Um job em espera mantém sua vaga. Um impedimento simples que ninguém responde em 3 dias é pulado automaticamente, o que libera a vaga para que a fila não trave; uma espera por decisão se mantém até ser respondida. Um job é registrado como skipped quando uma pessoa o pula, quando perguntou antes de começar (estação 3) ou quando seu cartão foi retirado, por exemplo fechado como não planejado, enquanto esperava na fila; o pulo automático o deixa em on_hold. Um job que atinge um limite de uso passa a paused e retoma sozinho com um intervalo que cresce de 30 minutos a 2 horas, no máximo 48 vezes, e depois fica em espera.

Fontes#