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 ofinally, 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 doghoculto. 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-unitem 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 -nos 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 domake. - A suíte é coletada uma vez; cada parte registra os ids dos testes que executou;
scripts/every_test_ran_once.pyfalha 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 entrehighlight,behaviour,fix,security,upgradeoulimitation. Um workflow verifica cada pull request; um que só muda documentação ou testes leva, em vez disso, o rótulono-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 nomain. - Uma versão candidata recebe a tag
vx.y.z-rc.Ne é 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
LICENSEeNOTICE, 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.jsonprecisa 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.
| Registro | Título | Status |
|---|---|---|
| ADR-0001 | Foundational conceptual model of the platform | Aceito |
| ADR-0002 | Task sizing and the execution budget | Aceito; alterado pelo ADR-0013 |
| ADR-0003 | Autonomous merge on the current base | Aceito |
| ADR-0004 | CI-aware autonomous repair (react, don't block) | Aceito |
| ADR-0005 | Post-merge deploy watch (observe & notify, never block) | Aceito |
| ADR-0006 | Bounded review-repair loop (react to a rejection, once) | Aceito; opcional pelo ADR-0014 |
| ADR-0007 | The floor frees at merge, not at agent-done (dependency safety) | Aceito; revisado pelo ADR-0010 |
| ADR-0008 | On-demand e2e via a labelled ticket (not on every PR) | Aceito |
| ADR-0009 | Durability & resilience hardening (post-audit) | Aceito |
| ADR-0010 | Single-line strict: the queue advances only on a clean merge (park on any impediment) | Aceito |
| ADR-0011 | Suppression-repair in the sandbox + reviewer-vetted coverage pragmas | Aceito |
| ADR-0012 | Perfect resume after a rate-limit pause (token visibility + honoured backoff + session resume) | Aceito |
| ADR-0013 | Pre-flight sizing gate, autonomous split, ticket effort budget, and stuck-recovery | Aceito, implementado em parte |
| ADR-0014 | Single-agent execution + advisory review (frontier-default, still agnostic) | Aceito |
| ADR-0015 | The tech-lead: impediment diagnosis + Slack, one brain, two triggers | Aceito; o transporte de chat é um complemento |
| ADR-0016 | Acting from Slack: read for all, write behind an allowlist | Aceito; o transporte de chat é um complemento |
| ADR-0017 | The Knowledge Layer: a derived, verifiable map the agent may not trust blindly | Aceito; revisado pelos ADR-0023 e ADR-0035 |
| ADR-0018 | Three harness roles: executor, reviewer, techlead | Aceito |
| ADR-0019 | The product role: requirements are authored documents, in their own repo | Aceito; o transporte de chat é um complemento |
| ADR-0020 | The tech-lead is on call: classify by remedy, resolve what it can, speak while doing it | Aceito |
| ADR-0021 | Agents that follow through: the open loop as the unit of memory | Aceito |
| ADR-0022 | Provider seams: an axis is agnostic when it is BORN with two | Aceito, implementado em parte |
| ADR-0023 | The map is derived, not learned: generate it where the checkout is | Aceito; revisado pelo ADR-0035 |
| ADR-0024 | Conversational memory: the thread is the unit, the raw log is sacred | Proposto, implementado em parte |
| ADR-0025 | Delivery closes with the client, not with the board | Aceito |
| ADR-0026 | A word the reader already has beats one you invent | Aceito |
| ADR-0027 | The client's board is not our test bench | Aceito |
| ADR-0028 | A "yes" is read; a "done" is not asserted | Aceito |
| ADR-0029 | A click is not interpreted | Aceito |
| ADR-0030 | What the parallel audit found, and the inversion it exposed | Aceito |
| ADR-0031 | Observing is not correcting: when the safety net becomes the damage | Aceito |
| ADR-0032 | The requirement cycle happens in the channel; merging is not agreeing | Aceito; refinado pelo ADR-0047 |
| ADR-0033 | The lifecycle decides in pure code; the engine only executes | Proposto, implementado em parte |
| ADR-0034 | Providers stay in-process for now, and the door stays open by a test | Aceito |
| ADR-0035 | The knowledge layer becomes the behaviour, not an experiment | Aceito |
| ADR-0036 | Ordering across repositories: the product declares it, the floor enforces it | Proposto |
| ADR-0037 | The box: the client's image, an injected toolbox, and a proof before any pickup | Aceito |
| ADR-0038 | The platform is complete on its own; channels are add-ons | Aceito |
| ADR-0039 | The action layer: what a human can ask the factory to do, written once | Aceito |
| ADR-0040 | The core runs on the client's own machines; a cloud is an add-on | Aceito |
| ADR-0041 | The roles read facts as files, not through a tool protocol | Aceito |
| ADR-0042 | The backfill has four inputs, and a legacy system is the product | Aceito, implementado em parte |
| ADR-0043 | The distribution is a published image, and one compose file both installs and builds | Aceito |
| ADR-0044 | A project declares what it is: profiles as a cascade layer | Aceito |
| ADR-0045 | A fingerprint nothing reads is a promise, not a mechanism | Proposto, implementado em parte |
| ADR-0046 | A file nothing describes is the least safe to change | Aceito |
| ADR-0047 | A requirement is agreed twice, in the conversation, and never through a pull request | Aceito |
| ADR-0048 | The factory asks before it spends, and the question goes to the person who asked | Aceito |
| ADR-0049 | The whole cycle runs on one machine: local is a kind, not a mode | Aceito, implementado em parte |
| ADR-0050 | A preview before the merge, because nothing after it can be taken back | Aceito |
| ADR-0051 | One door, parallel conversations, one semaphore on work | Aceito, implementado em parte |
| ADR-0052 | The owner's view: what the product is doing, and the whole map of it | Proposto, implementado em parte |
| ADR-0053 | The guardian's memory: raw, evidence and curated truth, with time as data | Proposto, implementado em parte |
| ADR-0054 | A card the product role opens is drafted, judged against a rubric, and shown whole before the yes | Aceito |
| ADR-0055 | A card has one lifecycle: one door for every change of its state, one table of what follows, one record of what happened | Implementado; o registro diz Proposed |
| ADR-0056 | A job's prompt has one budget, and every document it leaves out is named in it | Aceito |
| ADR-0057 | Who may act is one decision the core's policy makes, and a production approval is a person, not a password | Proposto, implementado em parte |
| ADR-0058 | A product owns its requirement template | Aceito |
Fontes#
CONTRIBUTING.mdMakefile.github/workflows/ci.yml.github/workflows/release-note.ymlpyproject.tomltests/conftest.pytools/mutate.pyscripts/every_test_ran_once.pyscripts/release_notes.pytests/test_ci_runs_what_we_run.pytests/test_ci_runs_every_test_exactly_once.pytests/test_the_wheel_ships_what_the_platform_needs.pytests/test_the_api_is_versioned.pydocs/RELEASING.mddocs/adr/README.mdLICENSENOTICE