Solicitudes de cambio
Cómo el trabajo de sesión llega a la rama predeterminada mediante revisión.
Una solicitud de cambio (CR) combina una rama de git con otra. Beeuni crea una CR desde la rama de una sesión (head_ref) hacia la rama predeterminada del proyecto (base_ref, normalmente main). La fila de la CR contiene metadatos; la combinación, el diff y las comprobaciones de conflictos se ejecutan como operaciones reales de git en el repositorio del proyecto. Una CR es la única forma de que el trabajo de una sesión llegue a la rama predeterminada.
Por qué el trabajo pasa por una CR
Una sesión se ejecuta en un entorno aislado y en su propia rama, cuyo nombre corresponde al ID de la sesión. El entorno aislado no dura para siempre, pero la rama sí: git es el único registro duradero del trabajo de una sesión. Cada sesión nueva parte de la rama predeterminada. Hasta que una CR se combine, el trabajo permanece en su propia rama, sin revisar e invisible para los demás agentes, activadores y colaboradores. Esto se aplica a todos los cambios: código, agentes, habilidades y el manifiesto (beeuni.yaml), sin excepciones.
Obligación del agente
Un agente debe abrir una CR para incorporar cualquier cambio a la rama predeterminada del proyecto. El agente no combina su propia CR: combinarla es decisión del usuario. Sigue este procedimiento:
- Confirma los cambios en la rama de la sesión (
$BEEUNI_BRANCH_NAME). Haz confirmaciones pequeñas y funcionales. No reescribas el historial ni fuerces el envío. - Envía la rama:
git push origin HEAD. - Abre la CR:
beeuni cr open --title "..." --description "...". En el entorno aislado de una sesión,--heady--sessionusan$BEEUNI_BRANCH_NAMEy$BEEUNI_SESSION_IDcomo valores predeterminados.--baseusa como valor predeterminado la rama predeterminada del proyecto. - Dile al usuario el número de la CR para que pueda revisarla.
- Detente. No combines la CR tú mismo.
Malas prácticas
- Forzar el envío a la rama predeterminada. Esto rompe el proceso de revisión, aunque el backend lo permita.
- «Está en mi rama, descárgala tú». La rama de la sesión desaparece cuando se detiene el entorno aislado, a menos que una CR la haya combinado antes.
- Enviar el cambio como archivo, texto pegado o archivo comprimido. El sistema de CR ya resuelve este problema.
Modelo de datos
Las CR se almacenan en la tabla change_requests.
| Columna | Tipo | Notas |
|---|---|---|
cr_id |
uuid | Clave principal. Identificador de la API REST. |
project_id |
uuid | Proyecto al que pertenece la CR. |
number |
integer | Número visible dentro del proyecto (#1, #2…). Único por proyecto. Nunca se reutiliza. |
title |
text | Obligatorio. |
description |
text | Valor predeterminado: una cadena vacía. |
base_ref |
text | Rama en la que se combina. Normalmente main. |
head_ref |
text | Rama desde la que se combina. En una sesión, es el ID de la sesión (un UUID). |
status |
enum | open, merged o closed. |
head_commit_sha |
text | En las CR abiertas, se actualiza en cada lectura con la referencia actual de head_ref. En las CR combinadas, se captura al momento de la combinación. |
base_commit_sha |
text | Misma regla, para base_ref. |
origin_session_id |
text | Sesión que abrió la CR. Se establece en null si se elimina esa sesión. |
created_by |
uuid | Usuario que creó la CR. |
merged_at / merged_by |
timestamp / uuid | Cuándo y quién combinó la CR. |
merge_commit_sha |
text | Confirmación de combinación. Es igual a head_commit_sha si se hizo un avance rápido. |
closed_at / closed_by |
timestamp / uuid | Cuándo y quién cerró la CR sin combinarla. |
metadata |
jsonb | Contiene requested_changes, una lista de entradas {text, by, at} añadidas por POST /:crId/request-changes. Las CR no tienen una tabla de comentarios aparte. |
created_at / updated_at |
timestamp | Se establece al crear. Se actualiza con cada cambio de estado o actualización de SHA. |
Un índice único en (project_id, number) permite hacer referencia a una CR por su número corto en lugar de su UUID.
Ciclo de vida
open ──(merge)──▶ merged (terminal)
open ──(close)──▶ closed ──(reopen)──▶ open
openes el estado inicial.closedes reversible.POST /:crId/reopenlo devuelve aopen.mergedes terminal. No se puede volver a abrir ni cerrar una CR combinada. En su lugar, abre una CR nueva sobre el estado combinado.
Beeuni no permite crear una CR cuya rama no tenga confirmaciones posteriores a la rama predeterminada. Esto suele significar que el agente confirmó los cambios localmente, pero nunca los envió. Envía las confirmaciones y vuelve a crear la CR.
Actualización de SHA
Para una CR abierta, Beeuni actualiza head_commit_sha y base_commit_sha con las ramas actuales en cada GET. Si no se puede acceder al repositorio o falta una rama, Beeuni omite la actualización y devuelve los últimos metadatos conocidos de la CR. Una CR combinada conserva los SHA capturados al momento de combinarla: Beeuni no vuelve a actualizarlos.
Mecánica de combinación
POST /v1/projects/:projectId/change-requests/:crId/merge ejecuta estos pasos.
- Beeuni lee el manifiesto (
beeuni.yaml) desdehead_refy lo valida con el esquema del manifiesto. Si una rama no tiene manifiesto, se acepta. Si el manifiesto no es válido, devuelve422concode: "MANIFEST_INVALID"y detiene la combinación. - Beeuni adelanta
base_refsihead_refestá estrictamente por delante. - De lo contrario, Beeuni crea una confirmación de combinación. El mensaje predeterminado es
Merge CR #<n>: <title>y puedes sustituirlo conmessageen el cuerpo de la solicitud. El autor de la confirmación esBeeuni <noreply@beeuni.com>. - Si hay un conflicto, devuelve
409con la lista de conflictos. Comprueba la misma lista conGET /:crId/merge-previewantes de combinar. - Si todo sale bien, Beeuni establece
statusenmerged, registramerged_at,merged_byymerge_commit_sha, e invalida la caché de git del proyecto.
Si se intenta combinar una CR que no está open, se devuelve 409.
Vista previa de la combinación
GET /:crId/merge-preview devuelve:
| Campo | Tipo | Significado |
|---|---|---|
base_sha |
string | Referencia actual de base_ref. |
head_sha |
string | Referencia actual de head_ref. |
merge_base |
string | null | Ancestro común. Es null si los historiales no están relacionados. |
is_up_to_date |
boolean | head_ref ya está completamente combinado en base_ref. |
can_merge |
boolean | No hay conflictos. |
can_fast_forward |
boolean | head_ref está estrictamente por delante de base_ref. |
conflicts |
string[] | Rutas de archivos que entrarían en conflicto. |
API REST
Todas las rutas están bajo /v1/projects/:projectId/change-requests.
| Método | Ruta | Notas |
|---|---|---|
| GET | / |
?status=open|merged|closed|all. Si no se aplica ningún filtro, devuelve todos los estados. |
| POST | / |
Cuerpo: {title, description?, head_ref, base_ref?, session_id?}. Devuelve 201. |
| GET | /:crId |
Devuelve la CR. Actualiza los SHA como efecto secundario. |
| PATCH | /:crId |
Edita title o description. 409 si no es open. |
| GET | /:crId/diff |
Parche unificado: lista de archivos, adiciones y eliminaciones. |
| GET | /:crId/merge-preview |
Consulta la sección «Vista previa de la combinación». |
| POST | /:crId/merge |
Cuerpo: {message?}. 422 si el manifiesto no es válido. 409 si hay conflictos o si no es open. |
| POST | /:crId/close |
409 si ya está merged. |
| POST | /:crId/reopen |
409 si no está closed. |
| POST | /:crId/request-changes |
Cuerpo: {feedback}. Añade a metadata.requested_changes y reactiva el agente de la sesión de origen. 409 si no es open. |
POST / rechaza una head_ref que no tenga confirmaciones posteriores a base_ref: 422 code: "CR_HEAD_NOT_AHEAD". Este es el error que recibe un agente si abre una CR antes de enviar su rama.
Autorización
Las rutas de lectura requieren acceso de lectura al proyecto. Las rutas de escritura requieren acceso de escritura. Además, cada acción de escritura requiere una capacidad, indicada a continuación tanto para un token completo como para el token con permisos limitados de una sesión.
| Acción | Capacidad |
|---|---|
| Abrir una CR | project.gitops.push |
| Solicitar cambios | project.review.act |
| Combinar | project.gitops.merge |
Se requiere una capacidad por acción, tanto para un token completo como para el token con permisos limitados de una sesión. Abrir y combinar son permisos independientes, por lo que un token puede abrir solicitudes de cambio sin tener permiso para combinarlas: ese es el mecanismo que hace cumplir la obligación del agente descrita antes.
project.cr.open y project.cr.merge eran los nombres anteriores al cambio para project.gitops.push y project.gitops.merge: la misma capacidad con un segundo nombre. Un beeuni.yaml que aún incluya uno de ellos sigue funcionando (el permiso se reescribe con el nombre vigente al resolverlo), pero usa el nombre gitops en cualquier elemento nuevo.
Una sesión nunca puede combinar una solicitud de cambio que ella misma abrió, independientemente de los permisos que se le hayan concedido. Esta regla es estructural, no una capacidad.