Arquitetura do núcleo do BeeverForge

Princípios do repositório

As regras que o repositório do núcleo impõe a si mesmo, de como um teste é nomeado a como uma versão é publicada. A maioria está escrita em CONTRIBUTING.md e docs/RELEASING.md e é garantida por um teste ou por um workflow; quando uma regra é uma convenção e não uma verificação, esta página informa isso.

Testes#

Nomeados como frases#

Um arquivo de teste diz o que prova, por exemplo test_a_card_is_closed_only_when_its_work_merges.py, para que buscar um comportamento na suíte encontre o teste que o garante.

Nas duas ordens e em paralelo#

A suíte roda em ordem aleatória por padrão (pytest-randomly) e em paralelo (pytest-xdist). Quem contribui deve rodá-la em paralelo tanto em ordem aleatória quanto em ordem fixa (-p no:randomly). Uma falha que depende da ordem é tratada como vazamento de estado, não como instabilidade. A CI roda a ordem aleatória, dividida em partes; ela não roda a ordem fixa. O conftest registra a semente de cada execução para que uma ordem possa ser reproduzida.

Guardas provadas por mutação#

tools/mutate.py executa um plano de mutação: cada plano nomeia um teste e uma lista de edições exatas do código, e cada edição precisa deixar esse teste vermelho. Ele se recusa a começar a menos que a âncora de cada edição corresponda exatamente uma vez e a linha de base esteja verde.

  • O executor é seguro contra falhas. Antes de mutar um arquivo, ele faz uma cópia fora da árvore e grava uma nota de operação em andamento na raiz do repositório; restaura o arquivo e verifica o texto restaurado em um bloco finally. Como um processo encerrado à força pula o finally, a próxima execução se recusa a começar enquanto existir uma nota e imprime os comandos que restauram o arquivo.
  • Os planos de mutação são executados por quem contribui, não pela CI. Um teste verifica que cada plano pode ser executado.

Uma guarda lê a coisa, não um texto sobre a coisa#

Um teste que protege uma configuração a executa ou a analisa: executa o script e lê seus argumentos, ou carrega o YAML e lê a chave. Quando é preciso buscar em texto, os comentários são removidos antes.

Uma suíte hermética#

  • As credenciais reais são removidas do ambiente antes da coleta e de novo antes de cada teste: variáveis da plataforma Git, do chat, do motor, do harness e da nuvem, e as configurações BEEVERFORGE_* do próprio operador.
  • A suíte roda com seu próprio HOME, com um novo para cada teste, e com o login do gh oculto. O registro de projetos é um diretório temporário, novo para cada teste.
  • Nenhum teste alcança um motor real: conectar ao Temporal lança um erro, exceto nos testes marcados owns_its_engine, que iniciam e descartam seu próprio servidor efêmero.
  • A suíte faz commits com sua própria identidade de git.

Dependências externas opcionais são puladas em tempo de execução#

A falta de uma ferramenta opcional, como Docker ou node, faz um teste ser pulado ao rodar; isso nunca muda o que é coletado. A CI define TEST_REQUIRE_DOCKER, que lá transforma o pulo por falta de Docker em falha. O servidor de testes do Temporal é um pré-requisito obrigatório, não um pulo.

A CI segue o Makefile e cada teste roda uma vez#

  • Cada etapa da CI que testa ou analisa executa um alvo do make (install, lint, test-collect, test-unit em quatro partes, test-integration, test-alone, test-ran-once). As demais etapas obtêm o código, preparam as ferramentas, movem artefatos entre jobs ou informam se todos os jobs passaram.
  • Uma guarda expande com make -n os alvos que a CI nomeia e verifica que eles continuam rodando a suíte e analisando cada raiz que a suíte cobre. Nenhuma guarda proíbe uma etapa de CI que não seja um alvo do make.
  • A suíte é coletada uma vez; cada parte registra os ids dos testes que executou; scripts/every_test_ran_once.py falha diante de um teste ausente, duplicado ou desconhecido. Uma coleta vazia é uma falha, não um sucesso.

Linters e verificação de tipos#

make lint executa ruff e shellcheck. mypy e bandit estão configurados em pyproject.toml e são instalados com o extra de desenvolvimento, mas nenhum deles roda no Makefile nem na CI; eles não são obrigatórios.

Código e documentação#

Dessas três regras, só o fragmento do changelog é verificado, por um workflow. As outras duas são convenções documentadas, em CONTRIBUTING.md e docs/adr/README.md.

  • Os comentários dizem por quê, com a medição: a data, o código de saída ou o incidente que tornou a linha necessária.
  • Fragmentos do changelog. Um pull request que muda comportamento adiciona changes/<issue>.<type>.md, onde o tipo é um entre highlight, behaviour, fix, security, upgrade ou limitation. Um workflow verifica cada pull request; um que só muda documentação ou testes leva, em vez disso, o rótulo no-release-note.
  • Os registros de decisão são alterados no próprio arquivo. Quando uma decisão muda, o ADR é editado com uma nota datada que nomeia o pull request. Um novo ADR substitui um antigo só quando a decisão é revertida, não quando é refinada.

Lançamentos e versões#

Estas regras são convenções documentadas, escritas em docs/RELEASING.md. O workflow de publicação roda com qualquer tag que comece com v e não as verifica, e o ensaio de instalação roda depois da publicação, não antes. O que é verificado está em Guardas de publicação.

  • As versões são versões semânticas anteriores à 1.0. Uma versão menor traz funcionalidades e mudanças de comportamento; um patch traz só correções e recebe a tag no seu branch release/x.y, nunca no main.
  • Uma versão candidata recebe a tag vx.y.z-rc.N e é publicada como pré-lançamento; as instalações continuam resolvendo a última versão final, a menos que a candidata seja nomeada. Uma versão menor sempre passa por pelo menos uma candidata.
  • O pacote declara exatamente a tag sem o v, e, quando a publicação no PyPI está habilitada, o workflow de publicação recusa uma tag que o pacote não declara. Cada mudança de versão regenera a descrição OpenAPI versionada no mesmo commit.
  • A versão final recebe a tag no commit em que a última candidata foi verificada, mais o commit que declara a versão e suas notas.
  • Uma versão publicada fica congelada: nenhuma tag é movida, apagada ou enviada de novo. O upgrade deve ser ensaiado antes de cada tag. Defeitos de segurança seguem um caminho de aviso privado. Só o responsável pelas versões cria um branch de versão ou uma tag.

Guardas de publicação#

  • O wheel. Um teste constrói um wheel real a partir de uma cópia limpa da árvore e verifica que ele inclui LICENSE e NOTICE, que seu único script de console é beeverforge, que sua descrição longa é a página declarada e que contém todos os arquivos de dados de que a plataforma precisa em tempo de execução.
  • A descrição da API. docs/reference/openapi.json precisa ser igual ao que a aplicação gera.

Licença e nome#

O núcleo é licenciado sob Apache-2.0. Não há acordo de licença de contribuidor: a licença já define o que acontece com uma contribuição, e os direitos autorais continuam com quem a escreveu. O arquivo NOTICE separa o código do nome: a licença não concede nenhum direito sobre o nome, o logotipo ou as marcas BeeverForge, e só os builds aprovados na suíte de conformidade publicada (beeverforge conformance, beeverforge conformance-adapter) podem se chamar BeeverForge.

Registros de decisão de arquitetura#

A coluna de status reflete o código no commit fixado. Quando a linha de status do próprio registro e o código divergem, vale o código: “implementado em parte” significa que parte da decisão está no código e parte não.

RegistroTítuloStatus
ADR-0001Foundational conceptual model of the platformAceito
ADR-0002Task sizing and the execution budgetAceito; alterado pelo ADR-0013
ADR-0003Autonomous merge on the current baseAceito
ADR-0004CI-aware autonomous repair (react, don't block)Aceito
ADR-0005Post-merge deploy watch (observe & notify, never block)Aceito
ADR-0006Bounded review-repair loop (react to a rejection, once)Aceito; opcional pelo ADR-0014
ADR-0007The floor frees at merge, not at agent-done (dependency safety)Aceito; revisado pelo ADR-0010
ADR-0008On-demand e2e via a labelled ticket (not on every PR)Aceito
ADR-0009Durability & resilience hardening (post-audit)Aceito
ADR-0010Single-line strict: the queue advances only on a clean merge (park on any impediment)Aceito
ADR-0011Suppression-repair in the sandbox + reviewer-vetted coverage pragmasAceito
ADR-0012Perfect resume after a rate-limit pause (token visibility + honoured backoff + session resume)Aceito
ADR-0013Pre-flight sizing gate, autonomous split, ticket effort budget, and stuck-recoveryAceito, implementado em parte
ADR-0014Single-agent execution + advisory review (frontier-default, still agnostic)Aceito
ADR-0015The tech-lead: impediment diagnosis + Slack, one brain, two triggersAceito; o transporte de chat é um complemento
ADR-0016Acting from Slack: read for all, write behind an allowlistAceito; o transporte de chat é um complemento
ADR-0017The Knowledge Layer: a derived, verifiable map the agent may not trust blindlyAceito; revisado pelos ADR-0023 e ADR-0035
ADR-0018Three harness roles: executor, reviewer, techleadAceito
ADR-0019The product role: requirements are authored documents, in their own repoAceito; o transporte de chat é um complemento
ADR-0020The tech-lead is on call: classify by remedy, resolve what it can, speak while doing itAceito
ADR-0021Agents that follow through: the open loop as the unit of memoryAceito
ADR-0022Provider seams: an axis is agnostic when it is BORN with twoAceito, implementado em parte
ADR-0023The map is derived, not learned: generate it where the checkout isAceito; revisado pelo ADR-0035
ADR-0024Conversational memory: the thread is the unit, the raw log is sacredProposto, implementado em parte
ADR-0025Delivery closes with the client, not with the boardAceito
ADR-0026A word the reader already has beats one you inventAceito
ADR-0027The client's board is not our test benchAceito
ADR-0028A "yes" is read; a "done" is not assertedAceito
ADR-0029A click is not interpretedAceito
ADR-0030What the parallel audit found, and the inversion it exposedAceito
ADR-0031Observing is not correcting: when the safety net becomes the damageAceito
ADR-0032The requirement cycle happens in the channel; merging is not agreeingAceito; refinado pelo ADR-0047
ADR-0033The lifecycle decides in pure code; the engine only executesProposto, implementado em parte
ADR-0034Providers stay in-process for now, and the door stays open by a testAceito
ADR-0035The knowledge layer becomes the behaviour, not an experimentAceito
ADR-0036Ordering across repositories: the product declares it, the floor enforces itProposto
ADR-0037The box: the client's image, an injected toolbox, and a proof before any pickupAceito
ADR-0038The platform is complete on its own; channels are add-onsAceito
ADR-0039The action layer: what a human can ask the factory to do, written onceAceito
ADR-0040The core runs on the client's own machines; a cloud is an add-onAceito
ADR-0041The roles read facts as files, not through a tool protocolAceito
ADR-0042The backfill has four inputs, and a legacy system is the productAceito, implementado em parte
ADR-0043The distribution is a published image, and one compose file both installs and buildsAceito
ADR-0044A project declares what it is: profiles as a cascade layerAceito
ADR-0045A fingerprint nothing reads is a promise, not a mechanismProposto, implementado em parte
ADR-0046A file nothing describes is the least safe to changeAceito
ADR-0047A requirement is agreed twice, in the conversation, and never through a pull requestAceito
ADR-0048The factory asks before it spends, and the question goes to the person who askedAceito
ADR-0049The whole cycle runs on one machine: local is a kind, not a modeAceito, implementado em parte
ADR-0050A preview before the merge, because nothing after it can be taken backAceito
ADR-0051One door, parallel conversations, one semaphore on workAceito, implementado em parte
ADR-0052The owner's view: what the product is doing, and the whole map of itProposto, implementado em parte
ADR-0053The guardian's memory: raw, evidence and curated truth, with time as dataProposto, implementado em parte
ADR-0054A card the product role opens is drafted, judged against a rubric, and shown whole before the yesAceito
ADR-0055A card has one lifecycle: one door for every change of its state, one table of what follows, one record of what happenedImplementado; o registro diz Proposed
ADR-0056A job's prompt has one budget, and every document it leaves out is named in itAceito
ADR-0057Who may act is one decision the core's policy makes, and a production approval is a person, not a passwordProposto, implementado em parte
ADR-0058A product owns its requirement templateAceito

Fontes#