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 saltafinally, 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 deghoculta. 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-uniten 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 -nlos 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 demake. - La suite se recolecta una vez; cada fragmento registra los ids de las pruebas que ejecutó;
scripts/every_test_ran_once.pyfalla 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 dehighlight,behaviour,fix,security,upgradeolimitation. Un workflow comprueba cada pull request; uno que solo cambia documentación o pruebas lleva en su lugar la etiquetano-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 enmain. - Una versión candidata se etiqueta
vx.y.z-rc.Ny 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
LICENSEyNOTICE, que su único script de consola esbeeverforge, 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.jsondebe 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.
| Registro | Título | Estado |
|---|---|---|
| ADR-0001 | Foundational conceptual model of the platform | Aceptado |
| ADR-0002 | Task sizing and the execution budget | Aceptado; modificado por ADR-0013 |
| ADR-0003 | Autonomous merge on the current base | Aceptado |
| ADR-0004 | CI-aware autonomous repair (react, don't block) | Aceptado |
| ADR-0005 | Post-merge deploy watch (observe & notify, never block) | Aceptado |
| ADR-0006 | Bounded review-repair loop (react to a rejection, once) | Aceptado; opcional según ADR-0014 |
| ADR-0007 | The floor frees at merge, not at agent-done (dependency safety) | Aceptado; revisado por ADR-0010 |
| ADR-0008 | On-demand e2e via a labelled ticket (not on every PR) | Aceptado |
| ADR-0009 | Durability & resilience hardening (post-audit) | Aceptado |
| ADR-0010 | Single-line strict: the queue advances only on a clean merge (park on any impediment) | Aceptado |
| ADR-0011 | Suppression-repair in the sandbox + reviewer-vetted coverage pragmas | Aceptado |
| ADR-0012 | Perfect resume after a rate-limit pause (token visibility + honoured backoff + session resume) | Aceptado |
| ADR-0013 | Pre-flight sizing gate, autonomous split, ticket effort budget, and stuck-recovery | Aceptado, implementado en parte |
| ADR-0014 | Single-agent execution + advisory review (frontier-default, still agnostic) | Aceptado |
| ADR-0015 | The tech-lead: impediment diagnosis + Slack, one brain, two triggers | Aceptado; el transporte de chat es un complemento |
| ADR-0016 | Acting from Slack: read for all, write behind an allowlist | Aceptado; el transporte de chat es un complemento |
| ADR-0017 | The Knowledge Layer: a derived, verifiable map the agent may not trust blindly | Aceptado; revisado por ADR-0023 y ADR-0035 |
| ADR-0018 | Three harness roles: executor, reviewer, techlead | Aceptado |
| ADR-0019 | The product role: requirements are authored documents, in their own repo | Aceptado; el transporte de chat es un complemento |
| ADR-0020 | The tech-lead is on call: classify by remedy, resolve what it can, speak while doing it | Aceptado |
| ADR-0021 | Agents that follow through: the open loop as the unit of memory | Aceptado |
| ADR-0022 | Provider seams: an axis is agnostic when it is BORN with two | Aceptado, implementado en parte |
| ADR-0023 | The map is derived, not learned: generate it where the checkout is | Aceptado; revisado por ADR-0035 |
| ADR-0024 | Conversational memory: the thread is the unit, the raw log is sacred | Propuesto, implementado en parte |
| ADR-0025 | Delivery closes with the client, not with the board | Aceptado |
| ADR-0026 | A word the reader already has beats one you invent | Aceptado |
| ADR-0027 | The client's board is not our test bench | Aceptado |
| ADR-0028 | A "yes" is read; a "done" is not asserted | Aceptado |
| ADR-0029 | A click is not interpreted | Aceptado |
| ADR-0030 | What the parallel audit found, and the inversion it exposed | Aceptado |
| ADR-0031 | Observing is not correcting: when the safety net becomes the damage | Aceptado |
| ADR-0032 | The requirement cycle happens in the channel; merging is not agreeing | Aceptado; precisado por ADR-0047 |
| ADR-0033 | The lifecycle decides in pure code; the engine only executes | Propuesto, implementado en parte |
| ADR-0034 | Providers stay in-process for now, and the door stays open by a test | Aceptado |
| ADR-0035 | The knowledge layer becomes the behaviour, not an experiment | Aceptado |
| ADR-0036 | Ordering across repositories: the product declares it, the floor enforces it | Propuesto |
| ADR-0037 | The box: the client's image, an injected toolbox, and a proof before any pickup | Aceptado |
| ADR-0038 | The platform is complete on its own; channels are add-ons | Aceptado |
| ADR-0039 | The action layer: what a human can ask the factory to do, written once | Aceptado |
| ADR-0040 | The core runs on the client's own machines; a cloud is an add-on | Aceptado |
| ADR-0041 | The roles read facts as files, not through a tool protocol | Aceptado |
| ADR-0042 | The backfill has four inputs, and a legacy system is the product | Aceptado, implementado en parte |
| ADR-0043 | The distribution is a published image, and one compose file both installs and builds | Aceptado |
| ADR-0044 | A project declares what it is: profiles as a cascade layer | Aceptado |
| ADR-0045 | A fingerprint nothing reads is a promise, not a mechanism | Propuesto, implementado en parte |
| ADR-0046 | A file nothing describes is the least safe to change | Aceptado |
| ADR-0047 | A requirement is agreed twice, in the conversation, and never through a pull request | Aceptado |
| ADR-0048 | The factory asks before it spends, and the question goes to the person who asked | Aceptado |
| ADR-0049 | The whole cycle runs on one machine: local is a kind, not a mode | Aceptado, implementado en parte |
| ADR-0050 | A preview before the merge, because nothing after it can be taken back | Aceptado |
| ADR-0051 | One door, parallel conversations, one semaphore on work | Aceptado, implementado en parte |
| ADR-0052 | The owner's view: what the product is doing, and the whole map of it | Propuesto, implementado en parte |
| ADR-0053 | The guardian's memory: raw, evidence and curated truth, with time as data | Propuesto, implementado en parte |
| ADR-0054 | A card the product role opens is drafted, judged against a rubric, and shown whole before the yes | Aceptado |
| 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; el registro dice Proposed |
| ADR-0056 | A job's prompt has one budget, and every document it leaves out is named in it | Aceptado |
| ADR-0057 | Who may act is one decision the core's policy makes, and a production approval is a person, not a password | Propuesto, implementado en parte |
| ADR-0058 | A product owns its requirement template | Aceptado |
Fuentes#
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