---
title: "Demandes de modification"
description: "Comment le travail d’une session rejoint la branche par défaut après révision."
---

Une demande de modification (CR) fusionne une branche Git dans une autre. Beeuni crée une CR à partir de la branche d’une session (`head_ref`) vers la branche par défaut du projet (`base_ref`, généralement `main`). La ligne de la CR contient des métadonnées ; la fusion, le diff et les vérifications de conflits sont effectués par de véritables opérations Git sur le dépôt du projet. Une CR est le seul moyen pour que le travail d’une session rejoigne la branche par défaut.

<a id="why-work-goes-through-a-cr"></a>

## Pourquoi passer par une CR

Une session s’exécute dans un environnement isolé sur sa propre branche, nommée d’après l’ID de la session. L’environnement isolé n’est pas permanent, mais la branche, elle, le reste : Git est le seul enregistrement durable du travail d’une session. Chaque nouvelle session démarre depuis la branche par défaut. Tant qu’une CR n’est pas fusionnée, le travail reste sur sa propre branche, sans révision et invisible pour tous les autres agents, déclencheurs et collaborateurs. Cela s’applique à toutes les modifications : code, agents, compétences et manifeste (`beeuni.yaml`) — sans exception.

<a id="the-agent-mandate"></a>

## Règle imposée aux agents

Un agent doit ouvrir une CR pour intégrer toute modification à la branche par défaut du projet. L’agent ne fusionne pas sa propre CR : la fusion relève de la décision de l’utilisateur. Respectez les étapes suivantes :

1. Valider les modifications sur la branche de la session (`$BEEUNI_BRANCH_NAME`). Faites de petits commits fonctionnels. Ne réécrivez pas l’historique et ne faites pas de push forcé.
2. Poussez la branche : `git push origin HEAD`.
3. Ouvrez la CR : `beeuni cr open --title "..." --description "..."`. Dans un environnement isolé de session, `--head` et `--session` prennent par défaut les valeurs `$BEEUNI_BRANCH_NAME` et `$BEEUNI_SESSION_ID`. `--base` prend par défaut la valeur de la branche par défaut du projet.
4. Indiquez le numéro de la CR à l’utilisateur afin qu’il puisse la réviser.
5. Arrêtez-vous. Ne fusionnez pas vous-même la CR.

<a id="anti-patterns"></a>

### Anti-modèles

- **Forcer un push vers la branche par défaut.** Cela rompt le processus de révision, même si le système le permet.
- **« C’est sur ma branche, récupérez-la vous-même. »** La branche de session disparaît à l’arrêt de l’environnement isolé, à moins qu’une CR ne l’ait fusionnée entre-temps.
- **Envoyer la modification sous forme de fichier, de texte collé ou d’archive.** Le système de CR règle déjà ce problème.

<a id="data-model"></a>

## Modèle de données

Les CR sont stockées dans la table `change_requests`.

| Colonne | Type | Remarques |
| --- | --- | --- |
| `cr_id` | uuid | Clé primaire. Identifiant de l’API REST. |
| `project_id` | uuid | Projet auquel appartient la CR. |
| `number` | integer | Numéro d’affichage propre au projet (`#1`, `#2`…). Unique par projet. N’est jamais réutilisé. |
| `title` | text | Obligatoire. |
| `description` | text | Valeur par défaut : chaîne vide. |
| `base_ref` | text | Branche dans laquelle la fusion est effectuée. Généralement `main`. |
| `head_ref` | text | Branche depuis laquelle la fusion est effectuée. Dans une session, il s’agit de l’ID de la session (un UUID). |
| `status` | enum | `open`, `merged` ou `closed`. |
| `head_commit_sha` | text | Actualisé à partir de la tête en direct de `head_ref` à chaque lecture pour les CR ouvertes. Enregistré au moment de la fusion pour les CR fusionnées. |
| `base_commit_sha` | text | Même règle pour `base_ref`. |
| `origin_session_id` | text | Session qui a ouvert la CR. Défini sur null si cette session est supprimée. |
| `created_by` | uuid | Utilisateur qui a créé la CR. |
| `merged_at` / `merged_by` | timestamp / uuid | Date et auteur de la fusion de la CR. |
| `merge_commit_sha` | text | Commit de fusion. Égal à `head_commit_sha` en cas d’avance rapide. |
| `closed_at` / `closed_by` | timestamp / uuid | Date et auteur de la fermeture de la CR sans fusion. |
| `metadata` | jsonb | Contient `requested_changes`, une liste d’entrées `{text, by, at}` ajoutées par `POST /:crId/request-changes`. Les CR n’ont pas de table de commentaires distincte. |
| `created_at` / `updated_at` | timestamp | Défini à la création. Mis à jour à chaque changement d’état ou actualisation de SHA. |

Un index unique sur `(project_id, number)` permet de référencer une CR par son numéro court plutôt que par son UUID.

<a id="lifecycle"></a>

## Cycle de vie

```
open ──(merge)──▶ merged      (terminal)
open ──(close)──▶ closed ──(reopen)──▶ open
```

- `open` est l’état initial.
- `closed` est réversible. `POST /:crId/reopen` le rétablit à `open`.
- `merged` est un état terminal. Vous ne pouvez ni rouvrir ni fermer une CR fusionnée. Ouvrez plutôt une nouvelle CR à partir de l’état fusionné.

Beeuni refuse de créer une CR si sa branche n’a aucun commit en avance sur la branche par défaut. Cela signifie généralement que l’agent a effectué des commits localement, mais ne les a jamais poussés. Poussez les commits, puis créez à nouveau la CR.

<a id="sha-refresh"></a>

## Actualisation des SHA

Pour une CR ouverte, Beeuni actualise `head_commit_sha` et `base_commit_sha` à partir des branches en direct à chaque `GET`. Si le dépôt est inaccessible ou qu’une branche est introuvable, Beeuni ignore l’actualisation et renvoie les dernières métadonnées connues de la CR. Une CR fusionnée conserve les SHA enregistrés au moment de la fusion : Beeuni ne les actualise plus.

<a id="merge-mechanics"></a>

## Mécanique de fusion

`POST /v1/projects/:projectId/change-requests/:crId/merge` exécute les étapes suivantes.

1. Beeuni lit le manifeste (`beeuni.yaml`) depuis `head_ref` et le valide selon le schéma du manifeste. Une branche sans manifeste est acceptée. Un manifeste invalide renvoie `422` avec `code: "MANIFEST_INVALID"` et interrompt la fusion.
2. Beeuni effectue une avance rapide de `base_ref` si `head_ref` est strictement en avance sur cette branche.
3. Sinon, Beeuni crée un commit de fusion. Le message par défaut est `Merge CR #<n>: <title>`, mais vous pouvez le remplacer avec `message` dans le corps de la requête. L’auteur du commit est `Beeuni <noreply@beeuni.com>`.
4. En cas de conflit, la réponse `409` contient la liste des conflits. Vérifiez cette même liste avec `GET /:crId/merge-preview` avant la fusion.
5. En cas de réussite, Beeuni définit `status` sur `merged`, enregistre `merged_at`, `merged_by` et `merge_commit_sha`, puis invalide le cache Git du projet.

La fusion d’une CR dont l’état n’est pas `open` renvoie `409`.

<a id="merge-preview"></a>

### Aperçu de la fusion

`GET /:crId/merge-preview` renvoie :

| Champ | Type | Signification |
| --- | --- | --- |
| `base_sha` | string | Tête actuelle de `base_ref`. |
| `head_sha` | string | Tête actuelle de `head_ref`. |
| `merge_base` | string \| null | Ancêtre commun. Null si les historiques ne sont pas apparentés. |
| `is_up_to_date` | boolean | `head_ref` est entièrement fusionnée dans `base_ref`. |
| `can_merge` | boolean | Aucun conflit. |
| `can_fast_forward` | boolean | `head_ref` est strictement en avance sur `base_ref`. |
| `conflicts` | string[] | Chemins des fichiers susceptibles d’entrer en conflit. |

<a id="rest-api"></a>

## API REST

Toutes les routes se trouvent sous `/v1/projects/:projectId/change-requests`.

| Méthode | Chemin | Remarques |
| --- | --- | --- |
| GET | `/` | `?status=open\|merged\|closed\|all`. Sans filtre, renvoie tous les états. |
| POST | `/` | Corps : `{title, description?, head_ref, base_ref?, session_id?}`. Renvoie `201`. |
| GET | `/:crId` | Renvoie la CR. Actualise les SHA à titre d’effet secondaire. |
| PATCH | `/:crId` | Modifie `title` ou `description`. `409` si la CR n’est pas `open`. |
| GET | `/:crId/diff` | Patch unifié : liste de fichiers, ajouts et suppressions. |
| GET | `/:crId/merge-preview` | Voir « Aperçu de la fusion » ci-dessus. |
| POST | `/:crId/merge` | Corps : `{message?}`. `422` si le manifeste est invalide. `409` en cas de conflit ou si la CR n’est pas `open`. |
| POST | `/:crId/close` | `409` si la CR est déjà `merged`. |
| POST | `/:crId/reopen` | `409` si la CR n’est pas `closed`. |
| POST | `/:crId/request-changes` | Corps : `{feedback}`. Ajoute à `metadata.requested_changes` et réveille l’agent de la session d’origine. `409` si la CR n’est pas `open`. |

`POST /` rejette une `head_ref` qui n’a aucun commit en avance sur `base_ref` : `422 code: "CR_HEAD_NOT_AHEAD"`. C’est l’erreur qu’un agent reçoit s’il ouvre une CR avant d’avoir poussé sa branche.

<a id="authorization"></a>

### Autorisation

Les routes de lecture nécessitent un accès en lecture au projet. Les routes d’écriture nécessitent un accès en écriture. Chaque action d’écriture requiert également une capacité, indiquée ci-dessous pour un jeton complet et pour le jeton à portée limitée d’une session.

| Action | Capacité |
| --- | --- |
| Ouvrir une CR | `project.gitops.push` |
| Demander des modifications | `project.review.act` |
| Fusionner | `project.gitops.merge` |

Une capacité par action, pour un jeton complet comme pour le jeton à portée limitée d’une session. L’ouverture et la fusion sont des permissions distinctes : un jeton peut donc ouvrir des demandes de modification sans pouvoir les fusionner — c’est le mécanisme qui permet d’appliquer la règle imposée aux agents décrite ci-dessus.

`project.cr.open` et `project.cr.merge` étaient les anciens noms de `project.gitops.push` et `project.gitops.merge` avant la migration : il s’agissait de la même capacité sous un autre nom. Un `beeuni.yaml` qui en répertorie toujours une reste fonctionnel (l’autorisation est convertie en nom actuel lors de sa résolution), mais utilisez le nom `gitops` dans toute nouvelle configuration.

Une session ne peut jamais fusionner une demande de modification qu’elle a elle-même ouverte, quelles que soient les autorisations qui lui ont été accordées. Cette règle est structurelle et ne dépend pas des capacités.
