Arquitectura del núcleo de BeeverForge

Principios del repositorio

Las reglas que se impone el repositorio del núcleo, desde cómo se nombra una prueba hasta cómo se publica una versión. La mayoría están escritas en CONTRIBUTING.md y docs/RELEASING.md y las hace cumplir una prueba o un workflow; cuando una regla es una convención y no una comprobación, esta página lo indica.

Pruebas#

Nombradas como frases#

Un archivo de prueba dice lo que prueba, por ejemplo test_a_card_is_closed_only_when_its_work_merges.py, para que buscar un comportamiento en la suite encuentre la prueba que lo sostiene.

En ambos órdenes y en paralelo#

La suite se ejecuta en orden aleatorio de forma predeterminada (pytest-randomly) y en paralelo (pytest-xdist). A quienes contribuyen se les pide ejecutarla en paralelo tanto en orden aleatorio como en orden fijo (-p no:randomly). Un fallo que depende del orden se trata como una fuga de estado, no como inestabilidad. La CI ejecuta el orden aleatorio, dividida en fragmentos; no ejecuta el orden fijo. El conftest registra la semilla de cada ejecución para poder reproducir un orden.

Guardas probadas por mutación#

tools/mutate.py ejecuta un plan de mutación: cada plan nombra una prueba y una lista de ediciones exactas del código, y cada edición debe poner esa prueba en rojo. Se niega a empezar salvo que el ancla de cada edición coincida exactamente una vez y la línea base esté en verde.

  • El ejecutor es seguro ante caídas. Antes de mutar un archivo, guarda una copia fuera del árbol y escribe una nota de operación en curso en la raíz del repositorio; restaura el archivo y comprueba el texto restaurado en un bloque finally. Como un proceso terminado a la fuerza se salta finally, la siguiente ejecución se niega a empezar mientras exista una nota e imprime los comandos que restauran el archivo.
  • Los planes de mutación los ejecutan quienes contribuyen, no la CI. Una prueba comprueba que cada plan puede ejecutarse.

Una guarda lee la cosa, no un texto sobre la cosa#

Una prueba que protege una configuración la ejecuta o la analiza: ejecuta el script y lee sus argumentos, o carga el YAML y lee la clave. Cuando hay que buscar en texto, primero se eliminan los comentarios.

Una suite hermética#

  • Las credenciales reales se eliminan del entorno antes de la recolección y de nuevo antes de cada prueba: variables de la plataforma Git, del chat, del motor, del harness y de la nube, y la configuración BEEVERFORGE_* del propio operador.
  • La suite se ejecuta con su propio HOME, con uno nuevo para cada prueba, y con la sesión de gh oculta. El registro de proyectos es un directorio temporal, nuevo para cada prueba.
  • Ninguna prueba llega a un motor real: conectarse a Temporal lanza un error, salvo en las pruebas marcadas owns_its_engine, que inician y eliminan su propio servidor efímero.
  • La suite hace commits con su propia identidad de git.

Las dependencias externas opcionales se omiten en tiempo de ejecución#

La falta de una herramienta opcional, como Docker o node, hace que una prueba se omita al ejecutarse; nunca cambia lo que se recolecta. La CI define TEST_REQUIRE_DOCKER, que allí convierte la omisión por Docker en un fallo. El servidor de pruebas de Temporal es un requisito obligatorio, no una omisión.

La CI se atiene al Makefile y cada prueba se ejecuta una vez#

  • Cada paso de la CI que prueba o analiza ejecuta un objetivo de make (install, lint, test-collect, test-unit en cuatro fragmentos, test-integration, test-alone, test-ran-once). Los demás pasos obtienen el código, preparan las herramientas, mueven artefactos entre jobs o informan de si todos los jobs pasaron.
  • Una guarda expande con make -n los objetivos que nombra la CI y comprueba que siguen ejecutando la suite y analizando cada raíz que cubre la suite. Ninguna guarda prohíbe un paso de CI que no sea un objetivo de make.
  • La suite se recolecta una vez; cada fragmento registra los ids de las pruebas que ejecutó; scripts/every_test_ran_once.py falla ante una prueba que falta, se duplica o es desconocida. Una recolección vacía es un fallo, no un éxito.

Linters y comprobación de tipos#

make lint ejecuta ruff y shellcheck. mypy y bandit están configurados en pyproject.toml e instalados con el extra de desarrollo, pero ninguno se ejecuta en el Makefile ni en la CI; no se hacen cumplir.

Código y documentación#

De estas tres reglas solo el fragmento del registro de cambios se comprueba, mediante un workflow. Las otras dos son convenciones documentadas, en CONTRIBUTING.md y docs/adr/README.md.

  • Los comentarios dicen por qué, con la medición: la fecha, el código de salida o el incidente que hizo necesaria la línea.
  • Fragmentos del registro de cambios. Un pull request que cambia el comportamiento añade changes/<issue>.<type>.md, donde el tipo es uno de highlight, behaviour, fix, security, upgrade o limitation. Un workflow comprueba cada pull request; uno que solo cambia documentación o pruebas lleva en su lugar la etiqueta no-release-note.
  • Los registros de decisión se modifican en el mismo archivo. Cuando una decisión cambia, se edita el ADR con una nota fechada que nombra el pull request. Un ADR nuevo sustituye a uno antiguo solo cuando la decisión se revierte, no cuando se precisa.

Publicaciones y versiones#

Estas reglas son convenciones documentadas, escritas en docs/RELEASING.md. El workflow de publicación se ejecuta con cualquier etiqueta que empiece por v y no las comprueba, y el ensayo de instalación se ejecuta después de la publicación, no antes. Lo que sí se comprueba aparece en Guardas de publicación.

  • Las versiones son versiones semánticas anteriores a 1.0. Una versión menor incluye funcionalidades y cambios de comportamiento; un parche incluye solo correcciones y se etiqueta en su rama release/x.y, nunca en main.
  • Una versión candidata se etiqueta vx.y.z-rc.N y se publica como versión preliminar; las instalaciones siguen resolviendo la última versión final salvo que se nombre la candidata. Una versión menor siempre pasa por al menos una candidata.
  • El paquete declara exactamente la etiqueta sin su v, y, cuando la publicación en PyPI está habilitada, el workflow de publicación rechaza una etiqueta que el paquete no declara. Cada cambio de versión regenera la descripción OpenAPI versionada en el mismo commit.
  • La versión final se etiqueta en el commit sobre el que se verificó la última candidata, más el commit que declara la versión y sus notas.
  • Una versión publicada queda congelada: ninguna etiqueta se mueve, se borra ni se vuelve a subir. La actualización debe ensayarse antes de cada etiqueta. Los defectos de seguridad siguen una vía de aviso privado. Solo el responsable de versiones crea una rama de versión o una etiqueta.

Guardas de publicación#

  • El wheel. Una prueba construye un wheel real a partir de una copia limpia del árbol y comprueba que incluye LICENSE y NOTICE, que su único script de consola es beeverforge, que su descripción larga es la página declarada y que contiene todos los archivos de datos que la plataforma necesita en tiempo de ejecución.
  • La descripción de la API. docs/reference/openapi.json debe ser igual a lo que genera la aplicación.

Licencia y nombre#

El núcleo tiene licencia Apache-2.0. No hay acuerdo de licencia para contribuyentes: la licencia ya establece qué ocurre con una contribución, y los derechos de autor siguen siendo de quien la escribe. El archivo NOTICE separa el código del nombre: la licencia no concede ningún derecho sobre el nombre, el logotipo ni las marcas BeeverForge, y solo las compilaciones que superan la suite de conformidad publicada (beeverforge conformance, beeverforge conformance-adapter) pueden llamarse BeeverForge.

Registros de decisión de arquitectura#

La columna de estado refleja el código en el commit fijado. Cuando la línea de estado del propio registro y el código no coinciden, se usa el código: «implementado en parte» significa que una parte de la decisión está en el código y otra no.

RegistroTítuloEstado
ADR-0001Foundational conceptual model of the platformAceptado
ADR-0002Task sizing and the execution budgetAceptado; modificado por ADR-0013
ADR-0003Autonomous merge on the current baseAceptado
ADR-0004CI-aware autonomous repair (react, don't block)Aceptado
ADR-0005Post-merge deploy watch (observe & notify, never block)Aceptado
ADR-0006Bounded review-repair loop (react to a rejection, once)Aceptado; opcional según ADR-0014
ADR-0007The floor frees at merge, not at agent-done (dependency safety)Aceptado; revisado por ADR-0010
ADR-0008On-demand e2e via a labelled ticket (not on every PR)Aceptado
ADR-0009Durability & resilience hardening (post-audit)Aceptado
ADR-0010Single-line strict: the queue advances only on a clean merge (park on any impediment)Aceptado
ADR-0011Suppression-repair in the sandbox + reviewer-vetted coverage pragmasAceptado
ADR-0012Perfect resume after a rate-limit pause (token visibility + honoured backoff + session resume)Aceptado
ADR-0013Pre-flight sizing gate, autonomous split, ticket effort budget, and stuck-recoveryAceptado, implementado en parte
ADR-0014Single-agent execution + advisory review (frontier-default, still agnostic)Aceptado
ADR-0015The tech-lead: impediment diagnosis + Slack, one brain, two triggersAceptado; el transporte de chat es un complemento
ADR-0016Acting from Slack: read for all, write behind an allowlistAceptado; el transporte de chat es un complemento
ADR-0017The Knowledge Layer: a derived, verifiable map the agent may not trust blindlyAceptado; revisado por ADR-0023 y ADR-0035
ADR-0018Three harness roles: executor, reviewer, techleadAceptado
ADR-0019The product role: requirements are authored documents, in their own repoAceptado; el transporte de chat es un complemento
ADR-0020The tech-lead is on call: classify by remedy, resolve what it can, speak while doing itAceptado
ADR-0021Agents that follow through: the open loop as the unit of memoryAceptado
ADR-0022Provider seams: an axis is agnostic when it is BORN with twoAceptado, implementado en parte
ADR-0023The map is derived, not learned: generate it where the checkout isAceptado; revisado por ADR-0035
ADR-0024Conversational memory: the thread is the unit, the raw log is sacredPropuesto, implementado en parte
ADR-0025Delivery closes with the client, not with the boardAceptado
ADR-0026A word the reader already has beats one you inventAceptado
ADR-0027The client's board is not our test benchAceptado
ADR-0028A "yes" is read; a "done" is not assertedAceptado
ADR-0029A click is not interpretedAceptado
ADR-0030What the parallel audit found, and the inversion it exposedAceptado
ADR-0031Observing is not correcting: when the safety net becomes the damageAceptado
ADR-0032The requirement cycle happens in the channel; merging is not agreeingAceptado; precisado por ADR-0047
ADR-0033The lifecycle decides in pure code; the engine only executesPropuesto, implementado en parte
ADR-0034Providers stay in-process for now, and the door stays open by a testAceptado
ADR-0035The knowledge layer becomes the behaviour, not an experimentAceptado
ADR-0036Ordering across repositories: the product declares it, the floor enforces itPropuesto
ADR-0037The box: the client's image, an injected toolbox, and a proof before any pickupAceptado
ADR-0038The platform is complete on its own; channels are add-onsAceptado
ADR-0039The action layer: what a human can ask the factory to do, written onceAceptado
ADR-0040The core runs on the client's own machines; a cloud is an add-onAceptado
ADR-0041The roles read facts as files, not through a tool protocolAceptado
ADR-0042The backfill has four inputs, and a legacy system is the productAceptado, implementado en parte
ADR-0043The distribution is a published image, and one compose file both installs and buildsAceptado
ADR-0044A project declares what it is: profiles as a cascade layerAceptado
ADR-0045A fingerprint nothing reads is a promise, not a mechanismPropuesto, implementado en parte
ADR-0046A file nothing describes is the least safe to changeAceptado
ADR-0047A requirement is agreed twice, in the conversation, and never through a pull requestAceptado
ADR-0048The factory asks before it spends, and the question goes to the person who askedAceptado
ADR-0049The whole cycle runs on one machine: local is a kind, not a modeAceptado, implementado en parte
ADR-0050A preview before the merge, because nothing after it can be taken backAceptado
ADR-0051One door, parallel conversations, one semaphore on workAceptado, implementado en parte
ADR-0052The owner's view: what the product is doing, and the whole map of itPropuesto, implementado en parte
ADR-0053The guardian's memory: raw, evidence and curated truth, with time as dataPropuesto, implementado en parte
ADR-0054A card the product role opens is drafted, judged against a rubric, and shown whole before the yesAceptado
ADR-0055A card has one lifecycle: one door for every change of its state, one table of what follows, one record of what happenedImplementado; el registro dice Proposed
ADR-0056A job's prompt has one budget, and every document it leaves out is named in itAceptado
ADR-0057Who may act is one decision the core's policy makes, and a production approval is a person, not a passwordPropuesto, implementado en parte
ADR-0058A product owns its requirement templateAceptado

Fuentes#