---
name: glab-issue-create
description: >
  Generate an actionable GitLab issue from code context, specs, a bug, or a refactoring need
  and create it via the glab CLI: infer domain/type/priority/board labels, weight, parent epic,
  fill the project template, assign, and link it. Use when the user says /glab-issue-create,
  /glab-issue, /issue, 'crée un ticket', 'crée une issue', 'ouvre un ticket', 'rédige un ticket',
  'fais-moi un ticket', 'note ça en ticket', 'formalise ce bug en ticket', 'create a ticket',
  'create an issue', 'open an issue', 'write a ticket', 'file a bug', 'turn this into a ticket',
  or needs to formalize work into a GitLab issue. Distinct from /glab-issue-find (which locates an
  existing issue) and /glab-issue-from-ai-note (which spawns followup tickets from an ai-note's
  open questions) — this one creates a fresh issue from scratch.
argument-hint: "[issue title or description] [--from-airtable <recID> to seed from an Airtable support ticket] [--extra-labels \"a,b\" to force-add caller-provided labels]"
allowed-tools:
  - Read
  - Glob
  - Grep
  - Bash
  - AskUserQuestion
---

Generate an actionable GitLab issue from the provided context (code, specs, bugs, refactoring) and create it directly via the `glab` CLI.

## 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

Detect the execution context using `detect_context`:
```bash
CTX=$(pysae-ai-tools internal detect-context)
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')
```

In CI mode (`IS_CI` is `true`):
- **Never use `AskUserQuestion`** — skip the confirmation step (Step 7) and create the issue directly with auto-detected values

## Caller-provided extra labels (`--extra-labels "a,b,c"`)

When `$ARGUMENTS` contains `--extra-labels "<comma-separated>"`, **append** those labels verbatim to the set passed to `glab issue create` (in addition to the auto-detected domain/type/priority/board labels — never replace them). These come from a trusted delegating skill, so the « never invent labels » rule does not apply to them: pass them through as-is, even if they are not in [../references/gitlab-labels.md](../references/gitlab-labels.md).

Typical caller: `/code-review-pre-release` passes `--extra-labels "review::release,version::vX.Y.Z"` so every ticket it spawns is tagged with the release review and its target version. The delegating skill is responsible for creating any dynamic group label (e.g. `version::vX.Y.Z`) beforehand; here just apply them.

## Airtable-seeded mode (`--from-airtable <recID>`)

When `$ARGUMENTS` contains `--from-airtable <recID>`, the issue is seeded from an existing **Airtable support ticket** (Customer Service base). Typical flow: `/support-ticket-create` has already created the Airtable ticket, and we now propagate it to GitLab for the dev team. All the standard rules below still apply — we just pre-fill some fields and add two extra labels.

### Extra steps (before the normal workflow)

1. **Read the Airtable ticket** via MCP Airtable (`mcp__airtable__get_record` or equivalent) on base `app79vpJ07qFIOEm1`, table `tblHjCvyOcdq0Kw0z`, record `<recID>`. Extract: `Nom`, `Sujet`, `Criticité`, `Impact`, `Notes`, `Contact`, `Compte`. See [../references/support-airtable.md](../references/support-airtable.md).
2. **Map `Sujet` → GitLab project** and **`Criticité` → priority label** from [../references/support-vocabulary.md](../references/support-vocabulary.md#sujet-airtable--repo-gitlab). These override any auto-detection from the context.
3. **Pre-fill the title** with the English translation of the Airtable `Nom` (always English — translate if FR).
4. **Pre-fill the description using the project's `bug.md` template** (loaded dynamically — see [MR templates / Issue templates](#gitlab-issue-templates-dynamic) below). **Description body in French**, title stays English. If the Airtable `Notes` are already in French, reuse them as-is; if they are in English (rare), translate them to French. Always preserve GitLab file links, technical terms, code snippets, and `group_id` values verbatim — do not translate identifiers.

   **Mapping Airtable Notes (3-parts) → sections du template `bug.md`** :

   | Source dans la Notes Airtable | Section du template `bug.md` |
   |---|---|
   | Observations Patrick — comportement observé, page/action concernée | **🧩 Contexte** (1-2 phrases : où, quoi, pour qui) |
   | Observations Patrick — captures, logs d'erreur, IDs Datadog | **📸 Éléments de diagnostic** (logs, captures) |
   | Observations Patrick — ses tests, appels API, URLs testées | **🔁 Étapes pour reproduire** (numérotées) + **🔧 Informations techniques → URL ou Endpoint concerné** |
   | Observations Patrick — comportement erroné précis | **❌ Résultat observé** |
   | Comportement attendu (déduit du contexte ou inféré du code) | **✅ Résultat attendu** |
   | Environnement (prod / dev / review), client/réseau impacté | **🔧 Informations techniques** (env, version app, navigateur si pertinent) |
   | Liens utiles — Datadog, Intercom, GitLab source | **📸 Éléments de diagnostic** (en bas, sous forme de bullets `- Datadog : <url>` / `- Intercom : <url>` / `- Code source : <url>`) |
   | Pré-analyse Claude — Cause racine / Chemin d'exécution / Quoi faire | **Section additionnelle `## 🧠 Pré-analyse`** ajoutée juste **avant** la ligne `**Airtable ticket url :**` (le template `bug.md` ne couvre pas cette analyse — on l'ajoute pour les devs) |

   La ligne `**Airtable ticket url :**` est **déjà dans le template `bug.md`** (commentée par `_(If issue is ":telephone_receiver: Support" related)_`). Remplir avec l'URL construite à l'étape 5.
5. **Fill in the Airtable interface URL** in the template's placeholder line `**Airtable ticket url :**` (already present at the bottom of `bug.md`). Build the URL from `recID` — see [support-airtable.md § URL interface Airtable](../references/support-airtable.md#url-interface-airtable-obligatoire-dans-les-issues-gitlab). Replace the placeholder URL with the constructed one, do not duplicate the line.
6. **Add two extra labels** to the normal set (do not replace them): `~"workflow::To Do"` (board column) and `~"Support 📞"` (group-level label on the `pysae` group — automatically available on every project, never invent it as a project label). The full label set becomes:
   ```
   ~<domain> ~type::bug ~priority::<P1|P2|P3> ~"workflow::To Do" ~"Support 📞" ~augmented
   ```
   Note: in Airtable-seeded mode the board column is **always** `workflow::To Do` (the bug came from a real client incident, scoped via `support-bug-investigate`, and is queued for immediate work — never `workflow::Refinement` or `workflow::Ready`).
7. **Skip epic matching** (support bugs are not attached to roadmap epics).

### Post-creation steps (after `glab issue create`)

8. **Update the Airtable ticket** via MCP Airtable: append the GitLab issue URL to the `Ticket Gitlab` field (field ID `fldjuMRjWBX0XiQjm`, type `multilineText`). Parameters:
   ```
   baseId        = app79vpJ07qFIOEm1
   tableIdOrName = tblHjCvyOcdq0Kw0z
   recordId      = <recID>
   fields        = {"Ticket Gitlab": "<issue web_url>"}
   ```
9. **Propose direct MR** to the user if the root cause is 100% identified and the fix is small (1-5 files, no DB migration, no public API change). Do not auto-chain — always ask first: *« Cause racine claire et petit fix : on enchaîne avec `/support-mr-fix` ? »*

## Workflow

1. **Analyze context** — examine all mentioned files, selected code, logs, specs
2. **Identify scope** — extract exact file paths, function names, components, dependencies
3. **Assign a weight (MANDATORY)** — every issue MUST get a Fibonacci weight scored from the scope analysis (see [../references/gitlab-weight.md](../references/gitlab-weight.md)). Never create a ticket without a weight: if the scope is unclear, estimate conservatively (or ask the user in local mode) rather than leaving it unset. The weight is applied via the `/weight <N>` quick action in the description (see [Output format](#output-format))
4. **Detect labels and pick the template** — infer domain, type, priority, and **board column** from context (see [../references/gitlab-labels.md](../references/gitlab-labels.md)). The detected `type::` label determines which template to load — see [Type → template mapping](#type--template-mapping) below. Choose the type based on the **nature of the finding** (observed vs theoretical vs new need), not on its surface description
5. **Load template and complete every section** — load the template matching the chosen type, then perform whatever **additional analysis or research** is needed to fill each section accurately : re-read the relevant code, walk through execution paths, check logs/Datadog, look up callers, verify behavior, confirm hypotheses. Never improvise content to fit a section, and never reuse a section from a prior report (review, post-mortem) verbatim — gather the actual information for *this* template's sections. If a section legitimately has no content (rare), leave a `<PLACEHOLDER>` flagging what is missing rather than rewording the section header
6. **Match parent epic** — fetch open epics and pick the best match (see [Parent epic](#parent-epic) below)
7. **Ask for confirmation** (local mode only) — present weight, labels (including board column), and suggested parent epic; ask user to confirm or adjust. In CI mode, skip and use auto-detected values
8. **Create issue via glab** — create the issue directly using `glab issue create` (see [Output format](#output-format) below)

## Critical rules

- **Zero hallucination**: if information is not visible in context, use `<PLACEHOLDER>` or state it needs verification
- **Title in English**, action-oriented (User Story: "As [role] I want to..." / Other: "[Verb] + [complement]"). **No prefix of any kind** in the title — no scope (`[NDP]:`, `API -`), no conventional commit prefix (`feat:`, `fix:`, `chore:`), no parenthetical scope (`perf(ndp)`). Labels handle all classification
- **Description and body sections**: entirely in French (Steps, Expected Outcome, Risques, etc.)
- **Langue française** : respecter les [règles de rédaction en français](../references/french-writing-rules.md) pour tout texte généré
- **Emojis**: preserve exactly as they appear in the loaded template — do not add or remove any
- **Placeholder blockquotes are NOT structure**: the templates write their fill-in instructions as an italic blockquote (`> _Décrivez…_`). The `>` styles the *placeholder hint* — it is not part of the section structure to keep. When you fill a section, **replace the whole blockquote line, dropping the `>`**: the real content is plain prose, never inside a blockquote (unless you are genuinely quoting something). Preserve only headers, emojis, and `---` separators. (The "preserve template formatting verbatim" rule below refers to that structure, not to the placeholder's blockquote styling.)
- **No nested backticks** — they break GitLab markdown on copy-paste
- **Escape `#N` patterns** — in descriptions, numbered lists like `#1`, `#2`, `#3` create unwanted links to GitLab issues. Escape them as `\#1`, `\#2`, `\#3`. Only use bare `#N` when intentionally referencing a real issue (e.g. `Closes #42`)
- **No git commands** (commit, push) in steps — devs manage their own workflow
- **Do not infantilize developers** — provide just enough info to understand the problem and the resolution direction
- Use `<details>` blocks for code diffs (with 4-space indentation, no fenced code blocks inside)
- End the description body with GitLab quick actions: `/label`, `/weight`
- **`type::*` is MANDATORY (org-wide)**: every issue MUST carry exactly one of `type::bug` / `type::feature` / `type::technical` / `type::refactoring`, plus at least one domain label (`API`, `Op`, …). These — like `priority::*` and the board columns — are **group-level labels on the `pysae` group, inherited by every project and available in every mode (local AND CI)**. Never assume labels are "limited" or unavailable in local mode, never check availability with `glab label list` and conclude they're missing, never omit `type::`, and never invent a substitute (e.g. `Tooling`). When unsure of the type, default to `type::technical` for tooling/scripts/CLI/internal work (see the [Type → template mapping](#type--template-mapping)).
- **Auto board placement** (MANDATORY): every issue MUST receive exactly one board column label so it appears on the [Pysae board](https://gitlab.com/groups/pysae/-/boards/546268). Apply the auto-detection rules from [../references/gitlab-labels.md](../references/gitlab-labels.md) — default to `~"workflow::Refinement"` when no stronger signal exists. Never create an issue without a board column label.
- **Assignee is MANDATORY**: every issue must have an assignee. Resolve the current username via `glab api user | jq -r '.username'` and pass `--assignee <username>` to `glab issue create` (or the user-specified assignee). Do NOT use the `/assign @me` quick action — it is not processed by the GitLab API. Never create an unassigned issue.
- **Use only valid labels** from [../references/gitlab-labels.md](../references/gitlab-labels.md). Never invent labels — in particular, priorities are `priority::P1`, `priority::P2`, `priority::P3` (not `priority::high`, `priority::critical`, etc.). **Exception**: labels passed via `--extra-labels` are trusted and applied verbatim (see the *Caller-provided extra labels* section above)

## Nourrir les étapes aval (design, implémentation, review)

Un ticket bien nourri facilite tout l'aval. **Ces sections existent dans les templates `feature.md` / `technical.md` chargés dynamiquement** : on les *remplit* avec rigueur, on ne les *ajoute* jamais. La règle « le template est le contrat, ne pas ajouter de section » reste prioritaire : **si une section n'est pas dans le template chargé** (ex. la MR issue-templates pas encore mergée), **ne pas l'inventer**. Jamais inventer de contenu non plus (zéro-hallucination) :

- **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). Ces critères sont consommés tels quels par `/design-generate` (mapping écran) et `/code-review` (vérification) : atomiques et testables.
- **🗂️ Fichiers / points d'entrée concernés** : reprendre le scope déjà analysé (Step 2) en chemins exacts (fichiers, fonctions, routes, composants). Point de départ pour `/code-implement`, jamais une solution imposée.
- **🚫 Hors-scope** : expliciter ce que le ticket ne fait PAS, pour borner l'implémentation. Renvoyer vers d'autres tickets si pertinent.
- **❓ Questions ouvertes** : capturer les incertitudes réelles à lever (vide si aucune). Elles alimentent le gate « Préparation au développement » et `/glab-issue-refine`.
- **🖥️ Écrans et états** (feature UI) : énumérer les écrans et leurs états. C'est la matière première de `/design-generate`.

### Signal d'éligibilité design

Poser le label **`design::eligible`** (group-level) **uniquement si** chaque écran de la feature est un pattern UI standard (liste, formulaire, détail, onglets, modal CRUD) **sans interaction stateful custom**. Il route le ticket vers le fast-path `/design-generate`. **Ne PAS le poser** dès qu'il y a : drag-drop, planning/timeline éditable, carte / livemap, animation ou transition custom, canvas, flux multi-étapes à état complexe, ou un composant visuel nouveau. Dans le doute, ne pas poser le label (discovery + designer).

## Domain adaptation

Adapt focus based on the domain detected from context:

- **Backend/API**: endpoints, models, validation, performance
- **Frontend/UI**: components, state, UX, accessibility
- **Infra/DevOps**: deployments, config, observability
- **Mobile**: sync, offline, permissions, performance
- **Tests**: coverage, edge cases, flakiness
- **Documentation**: clarity, examples, onboarding

## Self-check before output

Verify mentally:
- All provided files/context analyzed
- Exact names used (no invention)
- Steps are actionable without ambiguity
- Validation criteria are measurable
- **Critères d'acceptation numérotés `AC-N`, testables (Quand/Alors), états UI couverts**
- **(feature/technical uniquement) Sections aval remplies** : Fichiers / points d'entrée, Hors-scope (Questions ouvertes vide si aucune ; Écrans et états pour l'UI)
- **`design::eligible` posé si et seulement si feature UI commodity** (absent sinon)
- Ticket is self-contained (actionable without further clarification)
- Markdown syntax is valid for GitLab rendering
- A board column label is present (Refinement, Ready, workflow::To Do, etc.)
- **Exactly one `type::*` label is present** (mandatory org-wide) and a domain label too — never skipped, never a made-up substitute
- **An assignee is set** (`--assignee <username>`) — **mandatory**: every issue must have an assignee (the current user by default, or the one the user named). Never create an unassigned issue.
- **Template integrity**: all sections from the loaded template are present, in the same order, with the same emojis and headers — none added, removed, renamed, or reordered
- **Type ↔ template coherence (mandatory)**: the `type::` label matches the template used for the description (`type::bug` ↔ `bug.md`, `type::technical` ↔ `technical.md`, `type::feature` ↔ `feature.md`, `type::refactoring` ↔ `technical.md`). The `type::*` label and the description template are never chosen independently.

## GitLab issue templates (dynamic)

### Type → template mapping

The detected `type::` label determines which template to load. Pick the type based on the **nature of the finding**, not on its surface description :

| Nature of the finding | Type label | Template |
| --- | --- | --- |
| Broken behavior actually observed (incident, repro steps available, regression) | `type::bug` | `bug.md` |
| Theoretical risk, race condition, fragility, dead code, tech debt, missing test, refactor (no user-visible failure) | `type::technical` | `technical.md` |
| New product need, user story, missing capability | `type::feature` | `feature.md` |

**Heuristic for code review findings**: if the issue has no reproducible user-facing failure (just *« this could break »*, *« this is fragile »*, *« this lacks a test »*), it is almost always `type::technical`, not `type::bug`. Findings produced by `/code-review` or `/code-review-pre-release` *feel* like bugs because they describe defects, but if they have not manifested, they belong in `technical.md` — whose sections (Contexte / Description technique / Critères d'acceptation) are designed for non-observed issues. `bug.md` requires « Étapes pour reproduire » and « Résultat observé », which only make sense when a real failure has occurred.

**Red flag — wrong template alarm**: if you find yourself wanting to rename, remove, skip, or improvise a section of the loaded template, you are using the wrong template. **Stop and re-pick the type** — never adapt the template to fit your content. The template is the contract; your content must map onto it, not the other way around.

### Template loading

The team's GitLab issue templates are loaded below. **Use the template matching the chosen type as the skeleton for the description.**

**CRITICAL — preserve template formatting exactly as-is:** keep all emojis, section headers, bullet markers, and structure from the template verbatim. Do NOT strip emojis, rename headers, reorder sections, add new sections, or reformat. Only fill in the placeholder content within each section. The template is the source of truth for formatting. If a section feels inappropriate for your content, you are using the wrong template (see Red flag above) — switch templates, do not edit the structure.

!`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"`

## Parent epic

Before creating the issue, match it to the most relevant parent epic using the scoring script:

```bash
pysae-ai-tools glab epic-attach-issues match-one \
  --title "<ISSUE_TITLE>" \
  --label "<LABEL1>" --label "<LABEL2>" \
  --description "<ISSUE_DESCRIPTION_FIRST_500_CHARS>"
```

The script scores the issue against all open epics (enriched with their child issue titles) and returns the top matches with confidence levels.

1. If the top match has **high** confidence → suggest that epic
2. If only **medium** confidence → suggest it but mention uncertainty
3. If all matches are **low** or no match → suggest "aucune epic"
4. If the user explicitly requests a specific epic, use that one
5. When presenting the confirmation summary, show the suggested epic (or "aucune")

## Output format

After the user confirms the weight and labels, create the issue using `glab issue create`. **Always write the description to a temp file first** to avoid shell expansion issues and hangs. Use `mktemp` to generate a unique filename — never a fixed path like `/tmp/issue_desc.md`, which would collide if several skill runs execute in parallel on the same machine:

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

cat > "$ISSUE_DESC" <<'EOF'
[filled-in GitLab template for the detected type]

/label ~DOMAIN ~type::TYPE ~priority::PRIORITY
/weight WEIGHT
EOF

# Resolve current GitLab username
GL_USER=$(glab api user | jq -r '.username')

pysae-ai-tools glab retry issue create \
  --title "Issue title here" \
  --description "$(cat "$ISSUE_DESC")" \
  --assignee "${GL_USER}" \
  --no-editor \
  --yes
```

## Attach to epic

After creating the issue, attach it to the matched epic using `epic-attach` (**never** use `/parent_epic` quick action — it silently fails in some cases):

```bash
pysae-ai-tools glab epic-attach --epic ${EPIC_IID} --issue ${PROJECT_PATH}#${NEW_IID}
```

Skip this step if no epic was matched.

## Timeout & retry

The `pysae-ai-tools glab retry` wrapper handles timeout, retry (3 attempts), process cleanup, and API fallback automatically. Always use it for `glab issue create`.

$ARGUMENTS
