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.

Tipos incluídos nos eixos principais
EixoTipos incluídos
harnessclaude_code, codex, kimi, opencode
trackerlocal, github, jira, azure_devops
boardlocal, github, jira, azure_devops
forgelocal, github, azure_devops
cinone e local, github e github_actions, azure_devops e azure_pipelines; cada par designa o mesmo observador
boxcontainer, worktree, fargate (só descrição; seu executor é um complemento)
box_runnerNenhum
channelpanel
notifierpanel
identitylocal, oidc
metricsnull, sqlite, memory
previewcompose, none
credential_storeenv, 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 exemplo forge.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.github nã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.
Como um registro resolve um tipoUm registro procura primeiro na própria tabela, e uma linha incluída sempre responde. Só para um tipo que não está incluído os pontos de entrada são carregados, uma vez. Responde uma linha complementar com o nome do eixo e do tipo; se não existir, o registro recusa e nomeia o tipo.simnãosimnãoUm registro procuraum tipo no seu eixoIncluído?A linha incluída respondeUma linha complementarcom o mesmo nome éignorada e informadaOs pontos de entradabeeverforge.adapters são carregadosuma vez e guardadosUm nome malformado é ignoradocom um aviso; um complementoquebrado é registrado e ignoradoUma linha chamada<axis>.<kind>?O construtor do complemento respondeRecusa: o tipo, ostipos conhecidos e o pacoteque o inclui, se conhecido.Nenhum provedor padrão.
Como um registro resolve um tipo. Ele lê primeiro a própria tabela, então uma implantação que usa só tipos incluídos nunca carrega um ponto de entrada.

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 None quando 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#