Skip to content

Commit 7cd7970

Browse files
committed
chore(claude): add stacked PR pattern for inter-ticket dependencies
Les tickets peuvent désormais déclarer leurs dépendances dans une section `Depends on:` de leur body. `/epic` parse cette section et calcule la branche de base à passer à code-dev : - Aucune dépendance → origin/alpha - Toutes dépendances mergées → origin/alpha (refresh) - 1 dépendance en In review → ticket/<parent-slug> (stacked PR) - 2+ dépendances en In review → partial blocking v1 code-dev crée sa branche depuis la base reçue et ouvre la PR avec --base correspondant. GitHub retargette automatiquement vers alpha quand la PR parent est mergée. L'exécution n'attend jamais une validation humaine. Parent issue GitHub reste utilisé uniquement pour la relation epic → ticket. Les dépendances inter-tickets passent par la section Depends on pour être parseable et éviter l'ambiguïté.
1 parent 4298c8d commit 7cd7970

5 files changed

Lines changed: 87 additions & 35 deletions

File tree

.claude/agents/architect/AGENT.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,25 @@ N sub-issues of the epic, each **strictly** following `rules/ticket-spec-format.
2828

2929
1. **Lire** epic + scénarios + mockups + fichiers source pertinents
3030
2. **Cartographier** — modules, patterns existants, fichiers à toucher
31-
3. **Draft ticket list** inline, montré à l'utilisateur :
32-
- Titre, résumé 1 ligne, fichiers impactés, dépendances
33-
4. **Validation utilisateur** — « valide-tu ce découpage ? »
34-
5. **Sur approbation** — créer chaque ticket :
35-
- Parent issue = epic
36-
- Sub-issues ordonnées par dépendance
31+
3. **Découper + établir le DAG de dépendances** :
32+
- Chaque ticket = unité cohérente (≤ 8 critères d'acceptation)
33+
- Pour chaque paire de tickets, identifier si A doit être livré avant B (ex : schéma DB → routes tRPC → composants UI)
34+
- Ces dépendances iront dans la section `Depends on` du body de chaque ticket
35+
4. **Draft ticket list** inline, montré à l'utilisateur :
36+
- Titre, résumé 1 ligne, fichiers impactés, **dépendances explicites** (`T2 dépend de T1`)
37+
5. **Validation utilisateur** — « valide-tu ce découpage ? »
38+
6. **Sur approbation** — créer chaque ticket :
39+
- Parent issue = **epic** (uniquement pour relier au parent, PAS pour les dépendances)
40+
- **Section `Depends on`** dans le body listant les tickets dont il dépend (format `- #<N>`)
3741
- Labels appliqués
38-
6. **Commentaire final sur l'epic**`[Validation utilisateur] Architecture validée — N tickets créés, prêt pour /epic`
42+
7. **Commentaire final sur l'epic**`[Validation utilisateur] Architecture validée — N tickets créés, prêt pour /epic`
3943

4044
## Contraintes
4145

4246
- **Respecter `rules/ticket-spec-format.md`** — toutes les sections requises, chemins de fichiers explicites, pas de « voir le code »
43-
- **Max 8 critères d'acceptation par ticket** — découper sinon
44-
- **Dépendances via Parent issue / Sub-issues GitHub**`/epic` respecte cet ordre
47+
- **Max 8 critères d'acceptation par ticket** — découper sinon, et lier via `Depends on`
48+
- **Dépendances inter-tickets via section `Depends on`** dans le body (jamais via `Parent issue` qui sert uniquement à lier l'epic) — `/epic` parse cette section pour orchestrer le stacked-PR pattern
49+
- **Pas de cycles** dans le DAG de dépendances — sinon `/epic` refuse de dispatcher
4550
- **Aucune décision résiduelle** pour `code-dev` — Sonnet exécute, ne conçoit pas
4651

4752
## Output Format

.claude/agents/code-dev/AGENT.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ You execute one pre-specified ticket end-to-end : edit code, write/update tests,
1212
- Ticket issue number
1313
- Worktree path (assigned by `/epic`, e.g. `../egapro-epic42-t1`)
1414
- Dev server port (assigned by `/epic`, e.g. 3001)
15+
- **Base branch** (assigned by `/epic` ou `/code`) :
16+
- `origin/alpha` si aucune dépendance ou toutes mergées
17+
- `ticket/<parent-slug>` si le ticket dépend d'un autre encore en `In review` (stacked PR)
1518

1619
## Workflow
1720

@@ -21,7 +24,10 @@ You execute one pre-specified ticket end-to-end : edit code, write/update tests,
2124

2225
3. **Status ticket****In progress** (op. 3+4 de `rules/github-board.md`, option ID `47fc9ee4`).
2326

24-
4. **Branche** dans le worktree : `git checkout -b <ticket-slug>`.
27+
4. **Branche** dans le worktree, à partir de la **base branch** reçue en input :
28+
- `git fetch origin <base-branch>` pour rafraîchir
29+
- `git checkout -b ticket/<N>-<slug> <base-branch>`
30+
- Si `<base-branch>``origin/alpha`, on est en mode **stacked PR** — la PR sera ouverte avec `--base <base-branch>`, GitHub retargettera vers `alpha` quand la PR parent sera mergée
2531

2632
5. **Implémenter** :
2733
- Modifier les fichiers listés dans le ticket
@@ -42,10 +48,11 @@ You execute one pre-specified ticket end-to-end : edit code, write/update tests,
4248
- Démarrer dev server sur le port assigné
4349
- Playwright → screenshots desktop (1280×800) + mobile (375×667)
4450

45-
8. **PR draft** via `gh pr create --draft` :
46-
- Base = branche epic (ou master)
51+
8. **PR draft** via `gh pr create --draft --base <base-branch>` :
52+
- Base = la `<base-branch>` reçue en input (`origin/alpha` ou `ticket/<parent-slug>`)
4753
- Body : lien ticket (`Closes #NNN`), résumé, test plan, screenshots
4854
- **Ticket reste en In progress** pendant les validators
55+
- Si stacked : noter dans le body « Stacked on #<parent-PR> — GitHub retargettera automatiquement sur `alpha` une fois le parent mergé »
4956

5057
9. **Validations en parallèle** — 3 axes simultanés, tous doivent être verts avant de passer à l'étape 10.
5158

.claude/rules/ticket-spec-format.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,22 @@ Si pas d'UI, écrire "N/A".>
6666
- [ ] Lint vert (`pnpm lint:check`)
6767
- [ ] Scénario(s) rejoué(s) sans erreur par `functional-validator`
6868
- [ ] (si UI) Comparaison visuelle PASS par `design-validator`
69+
70+
## Depends on
71+
72+
<Liste des tickets dont celui-ci dépend (le code/schéma de ces tickets doit exister avant que celui-ci puisse être implémenté). Un ticket par ligne avec le numéro GitHub :
73+
74+
- #<N1>
75+
- #<N2>
76+
77+
Si aucune dépendance, écrire "N/A" ou omettre la section entièrement.>
6978
```
7079

7180
## Règles de rédaction
7281

73-
- **Un ticket = une unité cohérente** : si les critères d'acceptation dépassent 8 éléments, découper en deux tickets avec dépendance parent-enfant GitHub.
82+
- **Un ticket = une unité cohérente** : si les critères d'acceptation dépassent 8 éléments, découper en deux tickets et exprimer la dépendance via la section `Depends on`.
7483
- **Pas de décision architecturale** dans le ticket : l'architect a déjà tranché. Le dev exécute.
7584
- **Pas de "voir le code pour comprendre"** : les fichiers à lire doivent être listés explicitement.
7685
- **Le label `complexe`** est ajouté uniquement si la tâche demande un raisonnement multi-étapes non trivial (refacto multi-fichiers, perf critique, algo complexe). Par défaut, Sonnet suffit.
77-
- **Dépendances** : utiliser le champ `Parent issue` / sub-issues GitHub. `/epic` respecte l'ordre : les tickets sans dépendance partent en premier, les suivants dès que leurs parents sont **Done**.
86+
- **Dépendances inter-tickets** : listées dans la section `Depends on` du body. `/epic` parse cette section et applique la stratégie **stacked PRs** : si un ticket `T2` dépend de `T1` encore `In review` (PR non mergée), `T2` est créé avec `--base` sur la branche de `T1`. Ainsi l'exécution n'attend jamais une validation humaine.
87+
- **Parent issue GitHub** : utilisé uniquement pour pointer vers l'epic (relation epic → ticket). **Pas** pour exprimer des dépendances inter-tickets — utiliser `Depends on` à la place.

.claude/skills/code/SKILL.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,20 @@ Exécute **un seul ticket** end-to-end via l'agent `code-dev`. Utilisé seul (de
2525

2626
---
2727

28-
# Step 1 — Worktree + port
28+
# Step 1 — Worktree + port + base branch
2929

30-
Si déjà dans un worktree assigné par `/epic` → le réutiliser (variable d'env ou chemin courant).
30+
Si déjà dans un worktree assigné par `/epic` → le réutiliser (variable d'env ou chemin courant), `/epic` a déjà calculé la base branch.
3131

32-
Sinon (standalone) :
33-
- Créer `../egapro-ticket<N>` depuis `origin/alpha` : `git worktree add ../egapro-ticket<N> -b ticket/<N>-<slug> origin/alpha`
32+
Sinon (standalone), déterminer la base branch selon la section `Depends on` du body :
33+
- Aucune dépendance → `origin/alpha`
34+
- Toutes les dépendances en `Done``origin/alpha` (après `git fetch`)
35+
- 1 dépendance en `In review``ticket/<parent-slug>` (stacked PR)
36+
- 2+ dépendances en `In review`**exit** avec message : « attendre le merge d'au moins une dépendance avant `/code` »
37+
- 1+ dépendance en `In progress` ou `To Do`**exit** avec message : « dépendance pas prête, attendre `/epic` »
38+
39+
Création :
40+
- `git fetch origin <base-branch>`
41+
- `git worktree add ../egapro-ticket<N> -b ticket/<N>-<slug> <base-branch>`
3442
- Assigner le prochain port libre (check via `lsof -i :3001`, incrémenter)
3543

3644
---
@@ -41,6 +49,7 @@ Invoquer l'agent `code-dev` (`.claude/agents/code-dev/AGENT.md`) avec :
4149
- Ticket number
4250
- Worktree path
4351
- Dev server port
52+
- Base branch (cf. Step 1)
4453

4554
Model :
4655
- **opus** si ticket a label `complexe`

.claude/skills/epic/SKILL.md

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,35 @@ Dispatche les sous-issues d'un epic sur plusieurs worktrees en parallèle, invoq
1919

2020
- Vérifier que l'issue existe et a le label `Epic`
2121
- Fetcher ses sous-issues avec leur statut board — utiliser le snippet op. 5 de `rules/github-board.md`
22-
- Filtrer : ne conserver que les tickets dont le champ `Status` = **To Do** (ignorer Backlog / In progress / In review / Done)
23-
- Valider le graphe de dépendances (parents avant enfants, pas de cycles)
22+
- **Parser la section `Depends on`** du body de chaque sub-issue pour construire le DAG de dépendances inter-tickets (regex : extraire les `#<N>` listés dans la section).
23+
- Valider le DAG : pas de cycles. Si cycle détecté → exit avec diagnostic.
24+
- Filtrer les tickets dispatchables = en **To Do** (ignorer Backlog / In progress / In review / Done).
2425

25-
Si rien en To Do → exit avec `Nothing to dispatch on epic #<N>`.
26+
Si aucun ticket dispatchable → exit avec `Nothing to dispatch on epic #<N>`.
2627

2728
---
2829

29-
# Step 1 — Worktree + port assignment
30+
# Step 1 — Worktree + port + base branch
31+
32+
Pour chaque ticket à dispatcher, déterminer :
3033

31-
Pour chaque ticket à dispatcher, assigner :
3234
- **Worktree** : `../egapro-epic<N>-t<ticket-id>`
3335
- **Dev server port** : `3000 + <index>` (3001, 3002, 3003, …)
36+
- **Base branch** selon l'état de ses dépendances (stacked PR pattern) :
37+
38+
| État des dépendances | Base branch |
39+
|---|---|
40+
| Aucune dépendance | `origin/alpha` |
41+
| Toutes `Done` (PR mergées) | `origin/alpha` (après `git fetch`) |
42+
| Exactement 1 en `In review` (PR non mergée) | `ticket/<parent-slug>` (branche de cette dépendance) |
43+
| 2+ en `In review` | **ne pas dispatcher** — attendre qu'au moins une soit mergée (partial blocking v1) |
44+
| 1+ en `In progress` ou `To Do` | **ne pas dispatcher** — attendre que la dépendance arrive en `In review` au minimum |
3445

35-
Création :
46+
Création du worktree à partir de la base branch choisie :
3647

3748
```bash
38-
git worktree add ../egapro-epic42-t123 -b ticket/123-<slug> origin/alpha
49+
git fetch origin <base-branch>
50+
git worktree add ../egapro-epic42-t123 -b ticket/123-<slug> <base-branch>
3951
```
4052

4153
Si un worktree avec ce path existe déjà (re-run d'`/epic`), le réutiliser plutôt que d'en créer un nouveau.
@@ -44,39 +56,46 @@ Si un worktree avec ce path existe déjà (re-run d'`/epic`), le réutiliser plu
4456

4557
# Step 2 — Dispatch loop
4658

47-
Tant qu'il reste des tickets en **To Do** dont toutes les dépendances (Parent issue / Sub-issues GitHub) sont en **In review** ou **Done** :
59+
Tant qu'il reste des tickets en **To Do** dispatchables (toutes dépendances résolues selon le tableau de Step 1) :
4860

4961
1. **Sélectionner les N prochains tickets dispatchables** — parallélisme par défaut **N = 3**. Ajuster selon la RAM de la machine (~1–2 GB par dev server).
5062

5163
2. **Invoquer `code-dev` en parallèle** (un Agent tool call par ticket, dans un seul message) :
5264
- Model : **opus** si label `complexe`, sinon **sonnet**
53-
- Arguments : ticket number, worktree path, dev server port
65+
- Arguments : ticket number, worktree path, dev server port, **base branch** (cf. tableau Step 1)
5466
- `run_in_background: true` pour que `/epic` continue à orchestrer pendant l'exécution
5567

5668
3. **Attendre la complétion** de tous les `code-dev` lancés. Chaque retour est PASS ou REFACTO.
5769

5870
4. **Traiter les verdicts** :
59-
- **PASS** → ticket en **In review**, PR prête (`gh pr ready`). Worktree conservé pour revue utilisateur.
60-
- **REFACTO** → ticket retourné en **To Do** avec diagnostic. Signaler à l'utilisateur qu'une intervention `architect` est probablement nécessaire.
71+
- **PASS** → ticket en **In review**, PR prête (`gh pr ready`). Worktree conservé pour revue utilisateur. Les tickets qui dépendaient de celui-ci deviennent **dispatchables** en stacked (leur base branch = la branche de ce ticket).
72+
- **REFACTO** → ticket retourné en **To Do** avec diagnostic. Signaler à l'utilisateur qu'une intervention `architect` est probablement nécessaire. **Tous les tickets qui en dépendent restent bloqués** jusqu'à résolution.
6173

6274
5. **Reboucler** sur Step 2 tant qu'il reste des tickets dispatchables.
6375

76+
**Fin de boucle** — il peut rester des tickets en To Do dont les dépendances ne sont ni `In review` ni `Done` (ex : dépendances multiples toutes encore en flight). Dans ce cas, sortir proprement du loop et signaler à l'utilisateur que certains tickets attendent le merge humain d'une de leurs dépendances.
77+
6478
---
6579

6680
# Step 3 — Report
6781

6882
```
6983
## Epic #<N>: dispatch complete
7084
71-
PASS (In review): #N1, #N3, #N4
85+
PASS (In review):
86+
- #N1 (PR #101, base: origin/alpha)
87+
- #N3 (PR #103, base: ticket/N1-slug ← stacked)
88+
- #N4 (PR #104, base: ticket/N3-slug ← stacked)
7289
REFACTO (retour To Do): #N2 — <diagnostic>
90+
Bloqués (en attente merge humain) : #N5 (dépend de #N3 merged)
7391
Worktrees actifs: ../egapro-epic<N>-t*
7492
7593
Next:
76-
1. Revoir les PR ouvertes
77-
2. Passer manuellement les tickets In review → Done
78-
3. Si REFACTO : re-lancer /ticket sur l'epic (phase architect) puis /epic
79-
4. `git worktree remove ../egapro-epic<N>-t*` après merge de toutes les PR
94+
1. Revoir et merger les PR dans l'ordre du stack (#101 → #103 → #104)
95+
2. Les PR dépendantes seront auto-retargettées par GitHub vers `alpha` après chaque merge
96+
3. Passer manuellement les tickets In review → Done
97+
4. Si REFACTO : re-lancer /ticket sur l'epic (phase architect) puis /epic
98+
5. `git worktree remove ../egapro-epic<N>-t*` après merge de toutes les PR
8099
```
81100

82101
---
@@ -86,4 +105,6 @@ Next:
86105
- **Nettoyage worktrees** : conservés après `/epic` pour la revue humaine. L'utilisateur les supprime après merge.
87106
- **Never auto-Done** : `/epic` termine sur **In review** pour tous les tickets PASS. La transition **Done** est manuelle.
88107
- **Parallélisme** : max 3 par défaut. Chaque dev server consomme de la RAM et des ports. Ajuster `N` si machine modeste.
89-
- **Ordre de dispatch** : les tickets sans dépendance partent en premier. Les tickets dépendants attendent que leur parent soit **In review** (pas Done — sinon `/epic` bloquerait sur la validation humaine).
108+
- **Stacked PR pattern** : un ticket dépendant part immédiatement dès que son parent est **In review** (PR prête, pas encore mergée). Sa PR est ouverte avec `--base ticket/<parent-slug>`. GitHub retargette automatiquement vers `alpha` quand le parent est mergé. Évite le blocage sur la validation humaine.
109+
- **Ordre de merge humain** : critique. Merger les PR **dans l'ordre du stack** (parent avant enfant). Merger un enfant avant son parent casse l'historique. `/epic` le rappelle dans le report final.
110+
- **Multi-dépendances non mergées** (v1) : partial blocking — attendre qu'au moins une dépendance soit mergée avant de dispatcher le ticket. v2 future : merge commits croisés si besoin.

0 commit comments

Comments
 (0)