Arquitetura do núcleo do BeeverForge
Modelo de extensão
Cada dependência externa do núcleo, exceto o motor, o Temporal, fica atrás de um adaptador, e cada adaptador é escolhido por um registro a partir da configuração. Um novo provedor é adicionado instalando um pacote que declara um ponto de entrada, sem mudar o núcleo.
Eixos#
Um eixo é um tipo de coisa com que o núcleo pode ser estendido. A lista publicada é AXES em beeverforge/plugins.py:
tracker board forge ci harness box event metrics channel
notifier identity role session_store token_pool box_runner
credential board_setup extract embed preview credential_store
Um teste mantém essa lista exatamente igual ao que os registros pedem, e outro verifica que cada registro consulta o carregador de pontos de entrada.
| Eixo | Tipos incluídos |
|---|---|
harness | claude_code, codex, kimi, opencode |
tracker | local, github, jira, azure_devops |
board | local, github, jira, azure_devops |
forge | local, github, azure_devops |
ci | none e local, github e github_actions, azure_devops e azure_pipelines; cada par designa o mesmo observador |
box | container, worktree, fargate (só descrição; seu executor é um complemento) |
box_runner | Nenhum |
channel | panel |
notifier | panel |
identity | local, oidc |
metrics | null, sqlite, memory |
preview | compose, none |
credential_store | env, file |
O grupo de pontos de entrada#
- Um pacote declara seus adaptadores em um único grupo de pontos de entrada,
beeverforge.adapters. Cada nome é<axis>.<kind>, por exemploforge.gitlab. Um nome com qualquer outro formato é ignorado com um aviso que mostra o formato esperado. - Carregamento sob demanda. Os registros procuram primeiro na própria tabela. Os pontos de entrada são carregados uma vez, na primeira busca por um tipo que não é incluído, e ficam em cache; uma implantação que só usa tipos incluídos nunca carrega nenhum. Listar os complementos instalados lê os metadados dos pacotes sem importá-los.
- Um complemento com defeito é registrado no log e ignorado; os demais continuam sendo carregados.
- Os incluídos vencem uma colisão. Declarar
forge.githubnão substitui a linha incluída: a incluída responde e a colisão é registrada no log. Um complemento não pode mudar o que um tipo existente significa para todos os projetos de uma implantação. - Um tipo desconhecido gera uma recusa que o nomeia. O registro lança um erro com o tipo, os tipos conhecidos e, quando o tipo pertence a um pacote complementar conhecido, qual pacote o inclui. Ele nunca recorre a um provedor padrão.
Nascer com dois#
O ADR-0022 define o critério: um eixo é agnóstico não porque declara uma interface, mas quando nasce com mais de uma implementação e um ponto de junção que despacha entre elas. Os protocolos são @runtime_checkable e os testes os verificam com isinstance.
A regra não é cumprida hoje em todos os eixos. Os eixos channel, notifier, session_store e token_pool têm um único tipo incluído cada, e box_runner não tem nenhum. Nenhum teste exige dois tipos incluídos por eixo; existem testes por eixo para os eixos preview e credential_store.
Conformidade de adaptadores#
beeverforge conformance-adapter <kind> <factory> executa um adaptador contra a suíte de conformidade da plataforma, beeverforge/conformance/adapters.py. A função fábrica (factory) é um caminho de importação, pkg.module:attr, que nomeia uma instância, uma classe ou uma função que retorna uma.
- As verificações são funções simples que retornam achados. Não constroem nada remoto nem alteram nada remoto; exercitam o contrato local: assinaturas, regras de degradação e o formato das recusas.
- Há verificações para canais, notificadores, identidade, quadros, trackers, plataformas Git, harnesses, observadores de CI, caixas e prévias.
- Exemplos: uma leitura de tracker deve retornar
Nonequando não conseguiu ler e uma lista vazia quando não há nada; uma plataforma Git deve enviar uma credencial apenas ao host que a emitiu; a verificação de saúde de um observador retorna um booleano e nunca lança erros.
beeverforge conformance <project> é um comando diferente: verifica o manifesto de um projeto, não um adaptador.
O stack não precisa de adaptador#
O núcleo não nomeia nenhuma ferramenta de build. As entradas setup: e validate: do manifesto são comandos de shell, e a caixa executa cada um exatamente como escrito: sh -c em um contêiner, um shell em um worktree. Um projeto em qualquer linguagem é suportado declarando seus próprios comandos, sem adaptador.
Exemplo: um complemento de notificação#
De docs/writing-an-addon.md. O pacote declara um ponto de entrada:
[project.entry-points."beeverforge.adapters"]
"notifier.acme" = "beeverforge_acme:build_notifier"
e implementa o que o núcleo chama:
class AcmeNotifier:
def notify(self, *, message: str, level: str = "info",
about: str = "") -> str | None:
... # your provider
return None # or the provider's own handle for the message
def build_notifier(project=None, **_kw):
return AcmeNotifier()
Cada eixo chama seu construtor com seus próprios argumentos; o guia os lista por eixo e explica como uma linha declara seu nome de exibição e as soluções que o núcleo mostra para esse provedor.
Fontes#
beeverforge/plugins.pybeeverforge/adapters/forge/registry.pybeeverforge/adapters/agent/registry.pybeeverforge/adapters/sandbox/registry.pybeeverforge/adapters/environment/registry.pybeeverforge/conformance/adapters.pybeeverforge/cli.pybeeverforge/orchestrator/validation.pydocs/writing-an-addon.mddocs/core/07-extensibility.mdtests/test_a_stranger_can_add_an_adapter.py- ADR-0022, ADR-0034