---
name: glab-issue-refine
description: >
  Refine a GitLab issue sitting in `workflow::Refinement` so it becomes truly
  actionable: clarify scope, deepen the code analysis, lock down acceptance
  criteria, surface and answer open questions, then move it to `workflow::Ready`.
  Use when the user says /glab-issue-refine, /code-refine (legacy alias), /refine, 'affine ce ticket',
  'clarifie l issue', 'prépare ce ticket pour le sprint', 'refine this ticket',
  'ready ce ticket', or when an issue created by /glab-issue-create still needs
  scoping before development can start.
argument-hint: "[GitLab issue #IID or URL]"
allowed-tools:
  - Read
  - Edit
  - Write
  - Glob
  - Grep
  - Bash
  - AskUserQuestion
  - Skill
---

Take an issue that is still in `workflow::Refinement` (typically right after `/glab-issue-create`) and turn it into a fully-scoped, ready-to-implement ticket. The skill enriches the description with concrete file paths, acceptance criteria, and risks, **resolves every functional and technical ambiguity** (through questions, code reading, and small proofs-of-concept if needed), then transitions the issue to `workflow::Ready` once nothing is blocking implementation.

This skill is the logical bridge between `/glab-issue-create` (raw ticket) and `/code-implement` (start coding). It writes the refined description back to GitLab and may produce **throwaway exploratory code** locally to validate hypotheses, but it never commits, branches, pushes, or opens an MR — `/code-implement` is what produces the actual implementation.

## Required tools

- `glab`
- `jq`

Run `pysae-ai-tools tools require <tools>` before starting; if it exits non-zero, abort and report the install command from stderr to the user.

## Mode detection

```bash
CTX=$(pysae-ai-tools internal detect-context $ARGUMENTS_REFS --local)
IS_CI=$(echo "$CTX" | jq -r '.is_ci')
PROJECT_ID=$(echo "$CTX" | jq -r '.project_id // empty')
PROJECT_PATH=$(echo "$CTX" | jq -r '.project_path // empty')
ISSUE_IID=$(echo "$CTX" | jq -r '.issue_iid // empty')
```

In CI mode (`IS_CI` is `true`):
- **Never use `AskUserQuestion`** — open questions cannot be resolved interactively
- Document unresolved questions as a dedicated `## ❓ Questions ouvertes` section at the top of the description and post a comment that pings the assignee. Keep the issue in `workflow::Refinement` — do not force it to Ready
- All other refinements (file paths, acceptance criteria, weight, labels) still apply

## Step 1 : Resolve the target issue

If `ISSUE_IID` is empty after `detect_context`, delegate to `/glab-issue-find` with the raw `$ARGUMENTS` so the user can resolve the ticket by author, assignee or keywords. Once an IID is known, fetch the full record:

```bash
glab issue view "${ISSUE_IID}" --output json > /tmp/issue.json
```

Extract: `title`, `description`, `labels`, `weight`, `assignees`, `web_url`, `epic` (if any). Read the linked epic title and goals if present — they constrain the refinement.

**Abort early** if the issue is already in `workflow::In progress`, `workflow::Under review`, or `workflow::To deploy`: refinement no longer makes sense at that stage. Report the current state and stop.

## Step 2 : Read the codebase to ground the analysis

Refinement is grounded in the actual code, never in pure speculation. For each signal in the issue (file, module, endpoint, component, feature name), search the repository to locate the impacted code:

1. If a `CLAUDE.md` lives at the repo root, read it first (conventions, architecture, test commands).
2. Use `Glob` / `Grep` to identify the concrete files, functions, classes, components, routes or data models implied by the issue.
3. Read those files. Note the existing shape (types, interfaces, surrounding tests, error handling) so the refined description can be precise about what changes and where.
4. Spot adjacent concerns the original ticket missed: callers, migrations, fixtures, related endpoints, mobile/web symmetry, observability, feature flags.

Stay scoped to **reading the project's source files** at this step — no edits, no commits.

## Step 3 : Experimental validation (spikes, POCs, throwaway code)

Quand une incertitude **technique** ne peut pas être tranchée par la simple lecture du code (faisabilité d'une approche, comportement réel d'une lib, forme exacte d'un payload, perf d'une requête, comportement d'une API tierce…), il est explicitement autorisé d'écrire **du code temporaire** pour la lever. C'est même attendu : un ticket Ready ne doit plus contenir de question technique ouverte.

Règles pour le code exploratoire :

- **Isolation stricte** — tout le code temporaire vit dans `.refine-scratch/<issue_iid>/` à la racine du repo. Créer le dossier si besoin et y rester. Ne jamais éditer un fichier de production, même « juste pour tester ».
- **Throwaway** — le dossier `.refine-scratch/` est jetable. À la fin du skill, **toujours** demander à l'utilisateur (local) ou supprimer automatiquement (CI) :
  ```bash
  rm -rf .refine-scratch/<issue_iid>
  ```
  Ne jamais committer ce dossier. Si `.gitignore` ne couvre pas `.refine-scratch/`, ne pas l'ajouter au repo non plus — c'est local et éphémère.
- **No side effects on shared systems** — un POC ne doit pas écrire en base prod, ne pas envoyer de mails réels, ne pas appeler d'APIs tierces facturées sans accord explicite de l'utilisateur. Préférer : dev/staging, mocks, fixtures, comptes sandbox.
- **No secrets in the scratch dir** — utiliser `pysae-ai-tools env resolve` si un token est nécessaire ; ne jamais hardcoder une clé.
- **Petits POC seulement** — si le spike dépasse une dizaine de fichiers ou une demi-journée de travail, c'est qu'il devrait probablement devenir un vrai ticket `type::technical` d'investigation. Le proposer à l'utilisateur plutôt que de continuer.
- **Le résultat devient un fait, pas une histoire** — chaque hypothèse tranchée par un spike se retrouve dans la description sous forme d'**affirmation directe** intégrée à la section appropriée (contexte, contraintes techniques, fichiers impactés, approche retenue). Le ticket affirme ce qui est vrai, il ne raconte pas le chemin pour le savoir.
- **Justification courte autorisée pour les affirmations non-évidentes** — quand un fait risque d'être remis en cause par un futur lecteur (compat d'une lib, comportement d'une API tierce, perf d'une requête, contrainte d'infra), il est permis de l'**étayer en une demi-phrase** : « la librairie `xxx` supporte la fonctionnalité `yyy` (validé en refinement par un POC) », « l'endpoint `/foo` renvoie bien le champ `bar` (vérifié sur dev) », « la requête tient sous 50 ms à 10k docs (mesuré en refinement) ». La justification reste **inline**, **factuelle** et **brève** — pas de paragraphe, pas de récit, pas de lien vers le scratch dir. Réserver cette tournure aux affirmations qui en ont besoin ; les faits évidents (paths de fichiers lus dans le repo, signatures de fonctions visibles) n'ont pas à être étayés.

Cas d'usage typiques : valider qu'un endpoint MongoDB renvoie le champ attendu, tester un comportement de cache, mesurer le coût d'une requête, vérifier qu'une lib supporte une option, reproduire un bug pour le scoper, valider une migration sur un dump local.

## Step 4 : Build the refinement diff

Compare what the issue currently says with what the code shows. Produce a short internal diff with three buckets:

- **À préciser** : sections vagues à remplacer par des faits concrets (file paths, function names, endpoints, payload shapes).
- **À ajouter** : éléments absents que le dev aura besoin (étapes de repro précises, critères d'acceptation mesurables, risques, dépendances, impact perf, edge cases, tests à couvrir, compat mobile/web, observabilité).
- **À retirer** : éléments faux, redondants ou contredits par le code.

The bucket list is internal scaffolding for Step 4 — never paste it into the final description.

## Step 5 : Resolve every functional and technical ambiguity

Un ticket Ready ne tolère plus d'incertitude. Pour chaque ambiguïté repérée en Step 2/Step 3 :

1. **Classer** la nature de la question :
   - **Fonctionnelle / produit** — UX, scope, comportement attendu, edge cases métier, priorisation. → résolue par l'utilisateur (local) ou loggée pour l'assignee (CI).
   - **Technique** — faisabilité, comportement d'une lib/API, forme d'un payload, impact perf, contraintes de migration. → résolue en priorité **par lecture de code + spike** (Step 2 / Step 3). On ne pose une question à l'utilisateur que si le spike est concluant mais laisse un choix d'architecture.

2. **Classer la criticité** :
   - **Bloquante** — impossible d'implémenter sans réponse.
   - **Non-bloquante** — un défaut raisonnable existe ; on l'inscrit dans la description avec la mention `(à confirmer)`.

3. **Résoudre** :
   - **Local mode** : pose les questions bloquantes via `AskUserQuestion`, une à une, en regroupant les questions fonctionnelles. Pour les questions techniques, n'interroge l'utilisateur que si le spike (Step 3) n'a pas suffi à trancher.
   - **CI mode** : ne rien demander. Lister toutes les questions résiduelles dans une section `## ❓ Questions ouvertes` en haut de la description, en pingant l'assignee (`@username`). Dans ce cas, ne pas transitionner vers Ready.

Sources fréquentes d'ambiguïté à vérifier systématiquement :

| Catégorie             | Exemples |
|-----------------------|----------|
| Périmètre fonctionnel | Quels rôles concernés ? quelles plateformes (web/mobile/écran) ? quels réseaux clients ? |
| UX | États vides, erreurs, loading, accessibilité, copy FR/EN |
| Contrats d'API | Entrée/sortie, codes d'erreur, pagination, idempotence, versioning |
| Données | Schéma, migration, backfill, compatibilité ascendante |
| Sécurité | Permissions, RBAC, leak de données entre tenants, validation d'entrée |
| Performance | Charge attendue, P99, taille des collections, requêtes N+1, cache |
| Observabilité | Logs, métriques Datadog, traces, alertes |
| Tests | Unitaires, intégration, e2e, fixtures, environnement |
| Dépendances | Tickets prérequis, MR en cours, features flags, ordering deploy |

Pour chaque question technique tranchée par un spike, traduire le résultat en **affirmation directe** dans la section appropriée de la description (contexte, contraintes, approche retenue) — pas de section dédiée qui raconte le spike (voir Step 3).

## Step 6 : Rewrite the description in place — present-tense, no history

La description du ticket est **réécrite intégralement**, pas patchée. Elle doit décrire l'état **actuel et certain** du ticket, comme s'il avait toujours été clair. Aucune trace du processus de refinement, aucune mémoire du ticket initial, aucune mention du chemin parcouru.

Charger le template GitLab correspondant au type (`bug`, `feature`, `technical`) et l'utiliser comme squelette — emojis, intitulés, ordre des sections préservés à l'identique. Le template est chargé dynamiquement plus bas — voir [GitLab issue templates (dynamic)](#gitlab-issue-templates-dynamic).

Corps en **français**, titre en **anglais**. Appliquer les [règles de rédaction en français](../references/french-writing-rules.md) (accents y compris majuscules, espaces insécables, guillemets français, ton professionnel sans franglais inutile).

### Élimination du passé

Pour chaque élément hérité de la version initiale du ticket, appliquer **une** de ces trois actions — jamais autre chose :

| Cas d'origine | Action |
|---------------|--------|
| Phrase exprimant un doute (« on n'est pas sûr », « probablement », « à creuser », « TBD », « à confirmer », « peut-être que ») | **Remplacer** par l'affirmation tranchée correspondante (résultat de la lecture de code, du spike, ou de la réponse utilisateur). |
| Question ouverte qui a été résolue | **Supprimer** la question et **affirmer** directement le comportement retenu dans la section appropriée. |
| Hypothèse de scope qui s'est avérée fausse | **Supprimer** purement et simplement. |
| Note de processus (« voir avec Jean », « à valider en daily », « cf. discussion Slack ») | **Supprimer**. La description ne référence que des sources pérennes (code, docs, tickets liés). |
| Champ explicitement marqué « inconnu » et toujours inconnu | Laisser `<À vérifier>` **uniquement** si l'ambiguïté est non-bloquante et que le défaut retenu est documenté juste à côté. Sinon, le ticket reste en Refinement (voir Step 9). |

Aucune phrase de la description finale ne doit commencer par « initialement », « au début on pensait », « après analyse on confirme », « suite à la review », ni équivalent qui raconte le processus. Le ticket parle au présent de ce qui est vrai.

**Exception : justification inline d'une affirmation non-évidente.** Une affirmation technique qui pourrait être remise en cause par un futur lecteur peut être étayée en demi-phrase entre parenthèses : « (validé en refinement par un POC) », « (vérifié sur dev) », « (mesuré sur un dump prod) ». Ce n'est pas une trace du processus — c'est une caution de fiabilité posée à côté du fait. À réserver aux affirmations qui en ont vraiment besoin (compat de lib, comportement d'API tierce, contrainte perf), pas aux faits évidents.

### Conventions de contenu

- **Critères d'acceptation** — numérotés `AC-1`, `AC-2`…, chacun un énoncé **vérifiable** au format Quand / Alors ; pour l'UI, couvrir les états (loading, vide, erreur, cas limites). Consommés tels quels par `/design-generate` (mapping écran) et `/code-review` (vérification).
- **Fichiers / composants impactés** — liste à puces avec permaliens GitLab (`${PROJECT_URL}/-/blob/<sha>/<path>#L<line>`). Ne jamais inventer un chemin ; seul ce qui a été lu en Step 2 ou validé en Step 3 est éligible.
- **Sections aval du template enrichi** — remplir quand le type les prévoit : `🚫 Hors-scope` (borne l'implémentation), `❓ Questions ouvertes` (alimente le gate Ready, vide si aucune), `🖥️ Écrans et états` (UI, nourrit `/design-generate`). Le template chargé fournit le squelette ; ne jamais ajouter une section absente du template.
- **Label `design::eligible`** (à réévaluer en Step 7) — si le ticket est une feature UI commodity (écrans standard, zéro interaction custom : drag-drop, livemap, animation), le poser pour router vers `/design-generate`.
- **Approche technique retenue** — une phrase claire qui dit *comment* on s'y prend (sans entrer dans le code détaillé). Les hypothèses techniques tranchées par un spike apparaissent ici sous forme d'affirmations directes.
- **Risques & dépendances** — section explicite (migrations, breaking changes, services tiers, feature flags, ordering vis-à-vis d'autres tickets).
- **Tests** — préciser le périmètre attendu (unitaires, intégration, e2e) sans imposer la stratégie au dev.
- **No nested backticks** — ils cassent le rendu GitLab au copier-coller.
- **Escape `#N` patterns** — `\#1`, `\#2` pour éviter les liens auto. `Closes #42` reste autorisé pour de vraies références.
- **No git commands** (commit, push) dans les étapes.
- **Do not infantilize developers** — assez d'infos pour démarrer, pas un pas-à-pas pédagogique.
- Utiliser `<details>` pour les blocs de diff (indentation 4 espaces, pas de fences imbriqués).

Si le ticket initial avait des quick actions `/label` / `/weight` en bas, les **régénérer** sur la base de l'analyse affinée — ne jamais dupliquer ni reporter les anciennes.

## Step 7 : Re-evaluate weight, labels and epic

After rewriting, reassess :

1. **Weight** (Fibonacci) — apply [../references/gitlab-weight.md](../references/gitlab-weight.md). If the refined scope changes the effort, propose the new weight to the user (local) or just apply it (CI).
2. **Labels** — verify `~<domain>`, `~type::*`, `~priority::*` are still correct. Use only valid labels from [../references/gitlab-labels.md](../references/gitlab-labels.md). Never invent new ones.
3. **Parent epic** — if the issue has no epic, run the matcher and propose the best fit:
   ```bash
   pysae-ai-tools glab epic-attach-issues match-one \
     --title "<NEW_TITLE>" \
     --label "<LABEL1>" --label "<LABEL2>" \
     --description "<FIRST_500_CHARS>"
   ```
   Attach via `pysae-ai-tools glab epic-attach --epic <EPIC_IID> --issue ${PROJECT_PATH}#${ISSUE_IID}` only when confidence is **high** (or the user confirms in local mode).

## Step 8 : Confirm and push the refinement

**Local mode** : show the user a compact summary — title (if changed), proposed weight, labels, epic, list of resolved questions, list of any non-blocking defaults. Ask for confirmation before writing back.

**CI mode** : skip the confirmation and apply directly.

Once approved, write the description (and title if it changed) back to GitLab. **Always go through a temp file** to avoid shell expansion issues, never a fixed path:

```bash
DESC_FILE=$(mktemp -t issue-refine-XXXXXX.md)
trap 'rm -f "$DESC_FILE"' EXIT

cat > "$DESC_FILE" <<'EOF'
<refined description, French body, template-conformant>

/label ~<domain> ~type::<type> ~priority::<P1|P2|P3>
/weight <fibonacci>
EOF

glab issue update "${ISSUE_IID}" \
  --description "$(cat "$DESC_FILE")"
# Add --title "<new title>" only if the title actually changed
```

## Step 9 : Transition the workflow label

Apply the gate :

- **Toutes les questions bloquantes sont résolues** ET la description satisfait les critères de Ready (voir self-check ci-dessous) → transition vers `workflow::Ready` :
  ```bash
  pysae-ai-tools glab workflow-transition "${ISSUE_IID}" Ready
  ```
- **Sinon** (questions ouvertes restantes, ambiguïtés non levées, scope encore flou) → rester en `workflow::Refinement`. En CI, poster un commentaire pingant l'assignee avec la liste des questions :
  ```bash
  glab issue note "${ISSUE_IID}" --message "$(cat <<'EOF'
  @<assignee> ce ticket a été affiné automatiquement mais reste en Refinement — questions ouvertes :

  - <question 1>
  - <question 2>
  EOF
  )"
  ```

## Critères pour passer en Ready (self-check)

- [ ] Titre en anglais, action-oriented, sans préfixe de scope ou de type
- [ ] Description conforme au template GitLab du type détecté (emojis et sections préservés)
- [ ] **Aucune trace du passé** : pas de doute non levé, pas de question ouverte, pas de « probablement », « TBD », « à creuser », pas de mention du processus de refinement
- [ ] Critères d'acceptation mesurables (checklist `- [ ]`)
- [ ] Fichiers / composants impactés listés avec permaliens
- [ ] Approche technique retenue énoncée comme une affirmation
- [ ] Risques et dépendances explicites
- [ ] Poids Fibonacci attribué et cohérent avec le scope
- [ ] Labels valides : domain + type + priority + board column
- [ ] Epic parent rattachée si pertinent
- [ ] Pas de `#N` non échappé, pas de backticks imbriqués

Si un seul de ces points manque, ne pas transitionner vers Ready.

## Critical rules

- **Read-only on production code** — ce skill ne modifie jamais un fichier source du projet. Le seul endroit où il a le droit d'écrire est `.refine-scratch/<issue_iid>/` (code temporaire pour spikes/POCs, jamais committé). Tous les changements pérennes vont dans la description GitLab, les labels et le board column de l'issue.
- **Spike discipline** — pas de commit, pas de branche, pas de push, pas de MR. Le dossier scratch est supprimé à la fin du skill.
- **Description au présent, sans mémoire du passé** — la description finale est un snapshot de la vérité actuelle. Les doutes initiaux sont **levés et remplacés par des affirmations**, ou **purement supprimés** s'ils n'ont plus lieu d'être. Aucun « initialement », « à creuser », « TBD » ne survit dans le ticket Ready. La chronologie du ticket vit dans l'historique GitLab, pas dans sa description. **Seule exception** : une affirmation technique non-évidente peut être étayée par une justification inline brève (« validé en refinement par un POC », « mesuré sur dev ») — c'est une caution de fiabilité, pas un récit du processus.
- **Zero hallucination** — chaque fichier, fonction, endpoint cité doit avoir été lu en Step 2, ou validé par un spike en Step 3. Sinon, marquer explicitement `<À vérifier>` plutôt que d'inventer.
- **Langue française** pour le corps de la description, **anglais** pour le titre, conformément à [../references/french-writing-rules.md](../references/french-writing-rules.md).
- **Preserve template formatting** — emojis, ordre des sections, intitulés exacts du template ; ne pas reformater.
- **Never regress the board state** — ne pas redescendre un ticket en `workflow::Refinement` s'il est déjà en `workflow::Ready`, `To Do`, `In progress`, etc. Le skill se contente d'auditer et de signaler dans ce cas.
- **No assignee changes** — l'assignation reste la responsabilité du PM / de l'équipe.
- **No epic detach** — si une epic est déjà attachée, ne pas la retirer ; proposer seulement une meilleure correspondance à l'utilisateur en local.

## GitLab issue templates (dynamic)

Le template GitLab du type détecté est la source de vérité pour le formatage. Préserver tous les emojis, intitulés et sections verbatim — ne remplir que les placeholders.

!`for tpl in bug feature technical; do echo "=== Template: $tpl ==="; glab api "projects/pysae%2Fissue-templates/repository/files/.gitlab%2Fissue_templates%2F${tpl}.md/raw?ref=main" 2>/dev/null; echo -e "\n"; done 2>/dev/null || echo "Templates unavailable"`

## After refinement

Si le skill a été appelé **directement par l'utilisateur** (pas délégué), suggérer l'étape suivante :

> Ticket affiné et prêt. Tu peux lancer `/code-implement #${ISSUE_IID}` pour démarrer l'implémentation, ou `/code-autopilot #${ISSUE_IID}` pour le pipeline complet implement → review → CI → merge.

Si le ticket est resté en Refinement (questions ouvertes), proposer plutôt :

> Ticket affiné mais maintenu en Refinement — questions ouvertes loggées sur l'issue. Réponds-y puis relance `/glab-issue-refine #${ISSUE_IID}` pour finaliser la transition vers Ready.

$ARGUMENTS
