Skill analyser-etape¶
Référence technique des choix internes du skill Claude Code analyser-etape, qui orchestre le cadrage d'une étape Kirexo : création d'une issue GitLab parente, des work-items enfants, des branches Git associées et d'un premier commit de cadrage. Cette page documente le comment — pièges observés en intégrant l'API GitLab, glab CLI, GraphQL, bash et les permissions Claude. Le quoi vit dans .claude/skills/analyser-etape/SKILL.md. Le pourquoi de la délégation Claude est décrit dans l'explication Workflow des agents Claude.
Pour qui¶
- Le développeur qui maintient les scripts du skill (corrections, évolutions).
- Le main Claude lorsqu'il diagnostique un échec d'orchestration.
Pour quoi¶
- Comprendre pourquoi les scripts de
bin/parsent telle clé JSON, manipulent tel GID, valident tel cas de figure. - Retrouver rapidement les regex, mutations GraphQL et garde-fous bash qui rendent l'orchestration reproductible.
- Éviter de réintroduire un piège déjà résolu (PascalCase vs snake_case,
tee /dev/ttysans TTY, mots-clés awk).
Scripts livrés¶
Tous vivent dans .claude/skills/analyser-etape/bin/ et sont sourcés depuis le SKILL.md :
| Script | Rôle | Args | Effet remote |
|---|---|---|---|
reset-workspace.sh |
Vide var/claude/ de ses artefacts d'étape pour démarrer une nouvelle étape proprement (préserve le dossier et l'état des hooks de session). |
aucun | non |
preflight.sh |
Vérifie l'état du working tree et la synchro avec origin/main avant tout démarrage d'étape. |
aucun | non (fetch lecture seule) |
create-parent-issue.sh |
Crée l'issue GitLab parente via glab issue create (assignée à cpereira) et récupère son GID au format WorkItem. |
— | oui (crée une issue) |
create-task.sh |
Crée un work-item enfant type=task via glab work-items create, tente la liaison hiérarchique parent-enfant (best-effort) et l'assignation à cpereira via mutation GraphQL assigneesWidget (best-effort). |
— | oui (crée un work-item) |
inject-task-links.sh |
Met à jour le tableau Markdown de l'issue parente avec les URLs des enfants — sert de table des matières visible quand la liaison GraphQL échoue. | — | oui (édite l'issue) |
create-branches.sh |
Crée une branche par work-item (parente sur main, enfants sur la parente) et pousse sur origin. |
— | oui (push) |
inject-frontmatter.sh |
Injecte l'URL de l'issue parente dans le front-matter de Étapes/étape<n>.md. |
— | non |
first-commit.sh |
Propose le premier commit de cadrage sur la branche parente. Refus explicite si la cible est main/master. |
— | non |
Nettoyage du workspace (reset-workspace.sh)¶
Au début du skill, en §2 (Prérequis), après les vérifications « fichier d'étape existant » et « front-matter ticket_gitlab existant », reset-workspace.sh vide var/claude/ des artefacts des étapes précédentes :
Ce qui est supprimé¶
Tout artefact d'étape : dossiers etape<n>/, scripts ship-*.sh / fix-*.sh, fichiers *-commit.txt / *-mr.md, dossier skill-improvements/, logs et dossiers de travail divers.
Le script ne prend aucun argument et n'a aucun effet distant : il est destructif en local uniquement. Tous les artefacts supprimés sont régénérables ou déjà publiés sur GitLab.
Ce qui est préservé¶
- Le dossier
var/claude/lui-même. Les hooks de session (checklist, dashboard,guard-delegation,count-prod-php) y écrivent leur état sans le recréer ; le supprimer casserait la statusLine et la checklist de clôture. - Les trois fichiers d'état de ces hooks :
checklist-state.txt,prod-php-files.txt,reviewer-verdict.txt. Les retirer réinitialiserait la session active.
Garde-fou : lancé après la confirmation de re-cadrage¶
Le script est invoqué après la vérification du front-matter ticket_gitlab (point de re-cadrage), jamais avant. Cet ordre évite d'effacer l'output.json — qui porte les liens GitLab — d'une étape déjà cadrée : si l'étape est déjà lancée, le skill avertit et demande confirmation avant d'atteindre l'appel à reset-workspace.sh.
glab work-items¶
Sortie JSON en PascalCase¶
glab work-items create --output json retourne du JSON avec des clés PascalCase : IID, ID, WebURL, Type, Title…
Ce n'est pas iid, web_url, id (qui seraient le snake_case habituel des API GitLab). Les scripts du skill (create-task.sh) extraient avec jq -r '.IID', .WebURL, .ID.
Asymétrie avec glab issue create
glab issue create --output json renvoie du snake_case classique (iid, web_url). Seul glab work-items part en PascalCase. Ne pas réutiliser aveuglément un jq qui marche pour l'un sur l'autre.
ID global numérique¶
Le champ ID est un entier global (ex. 191954734), pas un GID au format gid://gitlab/.... Pour usage en mutation GraphQL, construire le GID manuellement :
URLs /work_items/<n> et plus /issues/<n>¶
glab issue create retourne désormais une URL de la forme https://gitlab.com/<group>/<repo>/-/work_items/<n> (et plus /issues/<n>). GitLab migre tout vers les Work Items dans l'UI moderne.
Regex à utiliser dans les scripts qui parsent la sortie :
GraphQL GitLab¶
Récupérer le GID d'une issue parente — pluriel workItems(iids:)¶
Pour récupérer le GID au format WorkItem d'une issue créée via glab issue create, utiliser workItems au pluriel (retourne une connection avec nodes) :
Le singulier workItem(iid:) n'existe pas sur le type Project — il échoue avec une erreur de schéma cryptique.
Mutation de liaison parent-enfant¶
mutation {
workItemUpdate(input: {
id: "gid://gitlab/WorkItem/<child>",
hierarchyWidget: { parentId: "gid://gitlab/WorkItem/<parent>" }
}) { errors workItem { id } }
}
Les deux GID doivent être au format WorkItem (pas Issue). Une issue créée via glab issue create doit donc être convertie via la query workItems(iids:) ci-dessus avant d'être utilisable comme parentId.
Caractère expérimental¶
L'API Work Items est marquée EXPERIMENTAL côté GitLab.com. Le script create-task.sh traite la liaison parent comme best-effort : si la mutation échoue, la tâche reste créée mais non liée, un warning est écrit sur stderr. Le tableau Markdown de l'issue parente (mis à jour par inject-task-links.sh) sert alors de table des matières visible — l'utilisateur n'est jamais bloqué par une régression côté GitLab.
Assignation systématique¶
Tout objet GitLab créé par le skill est assigné à un utilisateur, par défaut cpereira, surchargeable par la variable d'environnement GITLAB_ASSIGNEE :
C'est la même convention que celle appliquée par implementer-lot (MR de lot) et cloturer-etape (MR parente). Deux mécanismes distincts selon l'objet créé.
Ticket parent — flag --assignee¶
create-parent-issue.sh passe directement le flag à glab issue create :
Tâches (work-items) — mutation GraphQL assigneesWidget¶
glab work-items create n'expose pas de flag --assignee. L'assignation d'une tâche passe donc par une mutation GraphQL workItemUpdate sur le widget assigneesWidget, après création du work-item.
Le GID utilisateur se construit à partir de l'id numérique renvoyé par l'API REST users :
ASSIGNEE_ID=$(glab api "users?username=${ASSIGNEE}" 2>/dev/null | jq -r '.[0].id // empty' 2>/dev/null || echo "")
mutation {
workItemUpdate(input: {
id: "gid://gitlab/WorkItem/<child>",
assigneesWidget: { assigneeIds: ["gid://gitlab/User/<assignee_id>"] }
}) {
errors
workItem { id }
}
}
L'assignation est best-effort, comme la liaison hiérarchique parent-enfant : si l'utilisateur est introuvable (ASSIGNEE_ID vide) ou si la mutation échoue, la tâche reste créée mais non assignée, et un warning est écrit sur stderr. L'orchestration n'est jamais bloquée par une régression côté GitLab.
Bash en non-interactif¶
Les scripts du skill tournent depuis le main Claude (PreToolUse / Bash), sans TTY. Trois pièges à éviter :
- Pas de
tee /dev/tty— échoue avec « No such device or address » sans TTY. Utiliser un fichier de log dansvar/claude/si une trace est nécessaire. - Pas de
python3dans le devcontainer Kirexo. Utiliser awk/sed en pur bash. Aucune dépendance Python n'est garantie côté image. - Variables awk : ne pas utiliser de mots-clés réservés comme
close,print,for, etc. comme nom de variable. Préférercline,line,n:
Git¶
git status --porcelain¶
Le check de working tree « propre » dans preflight.sh autorise les ?? (untracked survivent au checkout -b) mais bloque les modifications :
Vérification sync branche courante / origin/main¶
preflight.sh fait un git fetch origin main puis compare les SHAs :
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse origin/main)
AHEAD=$(git rev-list --count origin/main..HEAD)
BEHIND=$(git rev-list --count HEAD..origin/main)
Trois cas distincts :
| Condition | Action attendue |
|---|---|
BEHIND > 0 et AHEAD == 0 |
git pull — main local en retard, à mettre à jour avant de démarrer. |
AHEAD > 0 et BEHIND == 0 |
git push — commits locaux non poussés, à publier avant de brancher. |
AHEAD > 0 et BEHIND > 0 |
Divergence — résolution manuelle requise, le skill refuse de continuer. |
Permissions Claude¶
Commits et merges déniés au main Claude¶
.claude/settings.json dénie au main Claude git commit, git rebase, git push origin main et git merge. Tout commit ou merge passe par un script .sh lancé manuellement par l'utilisateur — y compris le premier commit de cadrage produit par first-commit.sh.
Ce verrou est documenté dans la mémoire utilisateur (« Rationaliser les commits en fin d'étape » : commit/rebase déniés pour Claude, regroupement thématique via script .sh).
Garde-fou anti-main dans first-commit.sh¶
first-commit.sh refuse explicitement de committer sur main ou master, à double tour :
- Si la variable
EXPECTED_BRANCHvautmain/master, refus. - Si la branche courante (
git rev-parse --abbrev-ref HEAD) vautmain/master, refus.
Garde-fou en code, en plus de la règle dans le SKILL.md — la duplication est volontaire pour intercepter une invocation manuelle qui shunterait le skill.
Voir aussi¶
.claude/skills/analyser-etape/SKILL.md— description fonctionnelle du skill (quoi, dans quel ordre, avec quels prérequis).- Hooks Claude Code — référence des trois hooks (
guard-delegation.sh,count-prod-php.sh,stop-checklist.sh) qui encadrent les actions du main Claude et des sous-agents. - Workflow des agents Claude — pourquoi le main Claude délègue, pourquoi
analyser-etapecadre avant d'écrire du code.