Solicitações de alteração
Como o trabalho da sessão chega à branch padrão por meio da revisão.
Uma solicitação de alteração (CR) mescla um branch git a outro. O Beeuni cria uma CR a partir do branch de uma sessão (head_ref) para o branch padrão do projeto (base_ref, geralmente main). A linha da CR contém metadados; a mesclagem, o diff e as verificações de conflitos são executados como operações git reais no repositório do projeto. Uma CR é a única maneira de o trabalho de uma sessão chegar ao branch padrão.
Por que o trabalho passa por uma CR
Uma sessão é executada em um sandbox no próprio branch, nomeado de acordo com o ID da sessão. O sandbox não dura para sempre, mas o branch dura: o git é o único registro durável do trabalho de uma sessão. Toda nova sessão começa no branch padrão. Até que uma CR seja mesclada, o trabalho permanece no próprio branch, sem revisão e invisível para todos os outros agentes, gatilhos e colaboradores. Isso se aplica a toda alteração: código, agentes, skills e o manifesto (beeuni.yaml) — sem exceções.
O requisito para o agente
Um agente deve abrir uma CR para incorporar qualquer alteração ao branch padrão do projeto. O agente não mescla a própria CR — a mesclagem é uma decisão do usuário. Siga este contrato:
- Faça commit no branch da sessão (
$BEEUNI_BRANCH_NAME). Faça commits pequenos e funcionais. Não reescreva o histórico nem faça force-push. - Envie o branch:
git push origin HEAD. - Abra a CR:
beeuni cr open --title "..." --description "...". Dentro de um sandbox de sessão,--heade--sessiontêm como padrão$BEEUNI_BRANCH_NAMEe$BEEUNI_SESSION_ID.--basetem como padrão o branch padrão do projeto. - Informe ao usuário o número da CR para que ele possa revisá-la.
- Pare. Não mescle a CR você mesmo.
Antipadrões
- Fazer force-push no branch padrão. Isso viola o contrato de revisão, mesmo quando o backend permite.
- “Está no meu branch, baixe você mesmo.” O branch da sessão desaparece quando o sandbox é encerrado, a menos que uma CR o tenha mesclado antes.
- Enviar a alteração como arquivo, texto colado ou arquivo compactado. O sistema de CR já resolve esse problema.
Modelo de dados
As CRs ficam na tabela change_requests.
| Coluna | Tipo | Observações |
|---|---|---|
cr_id |
uuid | Chave primária. O identificador da API REST. |
project_id |
uuid | O projeto ao qual a CR pertence. |
number |
integer | Número de exibição por projeto (#1, #2…). Único por projeto. Nunca é reutilizado. |
title |
text | Obrigatório. |
description |
text | O padrão é uma string vazia. |
base_ref |
text | O branch no qual a mesclagem é feita. Geralmente main. |
head_ref |
text | O branch do qual a mesclagem é feita. Em uma sessão, é o ID da sessão (um UUID). |
status |
enum | open, merged ou closed. |
head_commit_sha |
text | Atualizado em relação ao tip ativo de head_ref a cada leitura, para CRs abertas. Capturado no momento da mesclagem para CRs mescladas. |
base_commit_sha |
text | Mesma regra, para base_ref. |
origin_session_id |
text | A sessão que abriu a CR. Definido como null se essa sessão for excluída. |
created_by |
uuid | O usuário que criou a CR. |
merged_at / merged_by |
timestamp / uuid | Quando e por quem a CR foi mesclada. |
merge_commit_sha |
text | O commit de mesclagem. Igual a head_commit_sha em um fast-forward. |
closed_at / closed_by |
timestamp / uuid | Quando e por quem a CR foi fechada sem mesclagem. |
metadata |
jsonb | Contém requested_changes, uma lista de entradas {text, by, at} adicionadas por POST /:crId/request-changes. As CRs não têm uma tabela de comentários separada. |
created_at / updated_at |
timestamp | Definidos na criação. Atualizados a cada alteração de status ou atualização de SHA. |
Um índice exclusivo em (project_id, number) permite referenciar uma CR pelo número curto em vez do UUID.
Ciclo de vida
open ──(merge)──▶ merged (terminal)
open ──(close)──▶ closed ──(reopen)──▶ open
opené o status inicial.closedé reversível.POST /:crId/reopeno define novamente comoopen.mergedé terminal. Não é possível reabrir nem fechar uma CR mesclada. Abra uma nova CR com base no estado mesclado.
O Beeuni se recusa a criar uma CR cujo branch não tenha commits à frente do branch padrão. Isso geralmente significa que o agente fez commit localmente, mas nunca enviou o branch. Envie os commits e crie a CR novamente.
Atualização de SHA
Para uma CR aberta, o Beeuni atualiza head_commit_sha e base_commit_sha em relação aos branches ativos a cada GET. Se o repositório estiver inacessível ou um branch estiver ausente, o Beeuni ignora a atualização e disponibiliza os últimos metadados conhecidos da CR. Uma CR mesclada mantém os SHAs capturados no momento da mesclagem — o Beeuni nunca os atualiza novamente.
Mecânica de mesclagem
POST /v1/projects/:projectId/change-requests/:crId/merge executa estas etapas.
- O Beeuni lê o manifesto (
beeuni.yaml) dehead_refe o valida em relação ao esquema do manifesto. Um branch sem manifesto é aceito. Um manifesto inválido retorna422comcode: "MANIFEST_INVALID"e interrompe a mesclagem. - O Beeuni faz fast-forward de
base_refsehead_refestiver estritamente à frente dele. - Caso contrário, o Beeuni cria um commit de mesclagem. A mensagem padrão é
Merge CR #<n>: <title>, e você pode substituí-la usandomessageno corpo da solicitação. O autor do commit éBeeuni <noreply@beeuni.com>. - Um conflito retorna
409com a lista de conflitos. Verifique a mesma lista comGET /:crId/merge-previewantes de mesclar. - Em caso de sucesso, o Beeuni define
statuscomomerged, registramerged_at,merged_byemerge_commit_shae invalida o cache git do projeto.
A mesclagem de uma CR que não esteja open retorna 409.
Prévia da mesclagem
GET /:crId/merge-preview retorna:
| Campo | Tipo | Significado |
|---|---|---|
base_sha |
string | Tip atual de base_ref. |
head_sha |
string | Tip atual de head_ref. |
merge_base |
string | null | Ancestral comum. Null se os históricos não tiverem relação. |
is_up_to_date |
boolean | head_ref está totalmente mesclado em base_ref. |
can_merge |
boolean | Sem conflitos. |
can_fast_forward |
boolean | head_ref está estritamente à frente de base_ref. |
conflicts |
string[] | Caminhos de arquivos que entrariam em conflito. |
API REST
Todas as rotas ficam sob /v1/projects/:projectId/change-requests.
| Método | Caminho | Observações |
|---|---|---|
| GET | / |
?status=open|merged|closed|all. Sem filtro, retorna todos os status. |
| POST | / |
Corpo: {title, description?, head_ref, base_ref?, session_id?}. Retorna 201. |
| GET | /:crId |
Retorna a CR. Atualiza os SHAs como efeito colateral. |
| PATCH | /:crId |
Edita title ou description. 409 se não estiver open. |
| GET | /:crId/diff |
Patch unificado: lista de arquivos, adições e exclusões. |
| GET | /:crId/merge-preview |
Consulte Prévia da mesclagem acima. |
| POST | /:crId/merge |
Corpo: {message?}. 422 quando o manifesto é inválido. 409 em caso de conflito ou se não estiver open. |
| POST | /:crId/close |
409 se já estiver merged. |
| POST | /:crId/reopen |
409 se não estiver closed. |
| POST | /:crId/request-changes |
Corpo: {feedback}. Anexa a metadata.requested_changes e desperta o agente da sessão de origem. 409 se não estiver open. |
POST / rejeita uma head_ref sem commits à frente de base_ref: 422 code: "CR_HEAD_NOT_AHEAD". Esse é o erro que um agente vê se abrir uma CR antes de enviar o branch.
Autorização
As rotas de leitura precisam de acesso de leitura ao projeto. As rotas de escrita precisam de acesso de escrita. Cada ação de escrita também precisa de uma capacidade, mostrada abaixo para um token completo e para o token com escopo de uma sessão.
| Ação | Capacidade |
|---|---|
| Abrir uma CR | project.gitops.push |
| Solicitar alterações | project.review.act |
| Mesclar | project.gitops.merge |
Uma capacidade por ação, tanto para um token completo quanto para o token com escopo de uma sessão. Abrir e mesclar são folhas separadas, portanto um token pode abrir solicitações de alteração sem ter poder para mesclá-las — esse é o mecanismo por trás do requisito para o agente acima.
project.cr.open e project.cr.merge eram os nomes anteriores à transição para project.gitops.push e project.gitops.merge — a mesma capacidade com um segundo nome. Um beeuni.yaml que ainda liste um deles continua funcionando (a concessão é reescrita para a grafia vigente quando é resolvida), mas use o nome gitops em tudo que for novo.
Uma sessão nunca pode mesclar uma solicitação de alteração que ela mesma abriu, independentemente das concessões que tenha recebido. Essa regra é estrutural, não uma capacidade.