scrub-issue

Par pytorch · pytorch

Récupère, analyse, reproduis et minimise les reproductions de problèmes GitHub. À utiliser lorsqu'on vous demande de vérifier si un problème se reproduit, de minimiser un repro, d'analyser un rapport de bug, ou de trier/qualifier un problème en termes de reproductibilité.

npx skills add https://github.com/pytorch/pytorch --skill scrub-issue

Minimiser la reproduction de l'issue

Récupérer une issue GitHub, évaluer si elle dispose d'une reproduction raisonnable, vérifier si elle se reproduit toujours, et minimiser systématiquement la reproduction au plus petit script autonome possible.

Outils

Supposez que l'environnement actuel est correct et exécutez python directement. Utilisez uniquement conda run -n <env> pour la bisection de version (étape 5a) où vous devez temporairement utiliser un environnement différent. Utilisez le paramètre timeout de l'outil Bash pour appliquer des délais d'expiration lors de l'exécution des scripts de reproduction.

  • gh issue view <NUMBER> --repo pytorch/pytorch pour récupérer le corps de l'issue
  • gh issue view --comments <NUMBER> --repo pytorch/pytorch pour récupérer les commentaires
  • python <script> pour exécuter les scripts de reproduction
  • gh issue comment <NUMBER> --repo pytorch/pytorch --body <BODY> pour commenter
  • gh issue edit <NUMBER> --repo pytorch/pytorch --add-label <LABEL> pour ajouter des labels

Plusieurs drapeaux gh issue edit peuvent être combinés dans une seule commande (par ex. --add-label "bug,help wanted" --add-assignee "@me"). Préférez regrouper les modifications en une seule commande pour minimiser les appels API et réduire le risque de vous abonner automatiquement aux notifications.

Préserver l'état d'abonnement aux notifications

Modifier une issue (commenter, ajouter des labels) vous abonne automatiquement aux notifications. Utilisez tools/stale_issues.py pour enregistrer et restaurer l'état d'abonnement :

  1. Avant la première modification, enregistrez l'état actuel. Cela peut s'exécuter en parallèle avec la récupération du corps/des commentaires de l'issue, mais il doit se terminer avant tout commande gh issue edit, gh issue comment ou gh issue close :
    python tools/stale_issues.py subscription save <NUMBER>
  2. Après la dernière modification, restaurez l'état enregistré — sauf si l'issue a été fermée (via gh issue close), auquel cas ignorez la restauration pour que l'utilisateur reste abonné aux réponses à la fermeture. La restauration doit être le tout dernier appel API GitHub — ne l'exécutez pas en arrière-plan ou en parallèle avec des commandes gh issue edit ou gh issue comment :
    python tools/stale_issues.py subscription restore <NUMBER>

Si l'une des commandes de sauvegarde ou de restauration échoue, avertissez l'utilisateur et continuez sans le mécanisme de sauvegarde/restauration.

Important : Ne jamais exécuter les commandes gh issue edit, gh issue comment, gh issue close ou de sauvegarde/restauration d'abonnement en arrière-plan. Elles doivent toutes s'exécuter au premier plan pour que leur exécution puisse être vérifiée avant de continuer. Si le commentaire ou la modification échoue parce que l'issue est verrouillée, signalez-le à l'utilisateur et ignorez la modification.

Liste de contrôle de sécurité

Avant d'exécuter un code de reproduction, vérifiez les éléments suivants :

  • Requêtes réseau vers des URL non fiables (requests, urllib, curl, wget)
  • Opérations sur fichiers en dehors de /tmp/
  • Exécution de commandes shell (os.system, subprocess, eval, exec) — mais subprocess utilisé pour lancer torchrun ou mp.spawn pour les reproductions distribuées est attendu et ne pose pas de problème
  • Téléchargement ou chargement de fichiers externes (poids de modèles, objets sérialisés, fichiers de données) — en particulier torch.load sur des fichiers .pt/.pth non fiables
  • Code obfusqué (chaînes encodées en base64, octets encodés, échappements inhabituels)
  • Installation de packages (pip install, conda install)
  • Manipulation de variables d'environnement susceptibles d'affecter le système hôte — mais la configuration de MASTER_ADDR, MASTER_PORT, RANK, WORLD_SIZE, CUDA_VISIBLE_DEVICES ou d'autres variables d'environnement PyTorch/CUDA standard est attendue et ne pose pas de problème

Si l'un de ces éléments est présent, expliquez la préoccupation à l'utilisateur et demandez s'il faut procéder, sauter ou modifier la reproduction pour supprimer les parties risquées. Si l'utilisateur choisit de sauter, actualisez quand même l'horodatage du label triaged (supprimez et réajoutez, ou ajoutez simplement s'il ne sera pas présent) avant de signaler que l'analyse est terminée.

Même si la reproduction passe la liste de contrôle ci-dessus, vérifiez si l'auteur du code de reproduction est un collaborateur PyTorch en exécutant python tools/stale_issues.py collaborator-check <username>. Si la commande se termine avec un statut non nul (l'utilisateur n'est pas collaborateur), montrez le code de reproduction à l'utilisateur et demandez-lui de vérifier qu'il est sûr de l'exécuter avant de le faire.

Étapes

1) Récupérer l'issue

Récupérez le corps et les commentaires de l'issue en parallèle. Identifiez le script de reproduction signalé et l'erreur. S'il y a plusieurs reproductions, préférez la plus récente du poster original. Si un commentateur a publié une reproduction strictement plus courte et plus autonome qui ne nécessite pas de contexte supplémentaire de la description de l'issue, préférez celle-ci. Notez quelle reproduction vous avez sélectionnée. Si le code de reproduction n'est présent que dans des captures d'écran ou des images plutôt que du texte copiable, arrêtez et signalez cela à l'utilisateur.

2) Vérifier si l'issue est exploitable

Avant d'investir des efforts dans la reproduction, vérifiez si l'issue est exploitable. Ne procédez pas aux étapes suivantes si l'une des conditions suivantes s'applique :

  • Déjà fermée ou résolue dans les commentaires. Signalez à l'utilisateur et arrêtez. Ne modifiez pas les labels sur les issues fermées.
  • Doublon d'une autre issue (liée ou manifestement le même bug)
  • Pas un rapport de bug (demande de fonctionnalité, question, discussion, refactorisation / nettoyage de code). Si l'issue est une demande de fonctionnalité et n'a pas déjà le label feature, ajoutez-le via gh issue edit. Si l'issue est une tâche de meilleure ingénierie / refactorisation et n'a pas déjà le label better-engineering, ajoutez-le via gh issue edit. Si l'issue comprend un script de reproduction qui démontre le comportement actuel, appliquez d'abord la liste de contrôle de sécurité, puis exécutez-le pour vérifier que le comportement persiste. Si la reproduction devait être modernisée (par ex. mise à jour des imports pour les API renommées), ou si vous avez vérifié que le comportement persiste toujours, commentez l'issue avec les résultats et la reproduction mise à jour. Après avoir ajouté les labels applicables (et éventuellement exécuté/commenté une reproduction), signalez l'analyse à l'utilisateur et ne procédez pas aux étapes suivantes.
  • Issue de suivi/méta (issue parapluie suivi plusieurs bugs, listes de fermeture, propositions d'amélioration sans reproduction spécifique). Si l'issue n'a pas déjà le label tracker, ajoutez-le via gh issue edit. Arrêtez ensuite et signalez à l'utilisateur.
  • Nécessite du matériel indisponible (modèles GPU spécifiques, TPUs, multi-nœuds) sans chemin pour simplifier. Remarque : CUDA est disponible sur la machine actuelle, donc les reproductions CUDA monogpu peuvent être exécutées directement.

Pour les issues non exploitables qui sont anciennes (plus d'~1 an), sans assignés, sans progrès récent, et sous-spécifiées ou manquant de motivation concrète, suggérez de les fermer comme « non planifiées » (gh issue close --reason "not planned") avec un commentaire expliquant la justification. Demandez à l'utilisateur avant de fermer.

Si l'issue n'est pas exploitable et aucune modification visible par GitHub n'a été faite (aucun label ajouté, aucun commentaire publié — enregistrer l'état d'abonnement n'en est pas), actualisez le label triaged pour mettre à jour l'horodatage « dernière mise à jour » de l'issue. Un simple gh issue edit avec à la fois --remove-label et --add-label ne fonctionne pas parce que la suppression et l'ajout s'annulent mutuellement. À la place, chaînez les deux édits en un seul appel outil Bash : gh issue edit ... --remove-label triaged && gh issue edit ... --add-label triaged. Si l'issue n'a pas le label triaged, ajoutez-le simplement.

Résumez ensuite pourquoi l'issue n'est pas exploitable. Si un label ou une autre mise à jour a été faite, signalez simplement que l'analyse est terminée. Demandez à l'utilisateur comment procéder uniquement si aucune mise à jour n'a été faite et la situation est ambiguë. Demandez toujours à l'utilisateur avant de fermer une issue.

3) Analyser la reproduction

Évaluez si l'issue a une reproduction raisonnable :

  • Y a-t-il un snippet de code qui peut être exécuté ?
  • Les dépendances sont-elles disponibles (CUDA, distributed, matériel spécifique) ? Remarque : les reproductions torch.distributed ne nécessitent souvent pas de matériel spécial — elles peuvent être lancées avec torchrun --nproc_per_node=1 ou mp.spawn sur une seule machine.
  • L'erreur attendue est-elle décrite ?
  • La reproduction est-elle autonome ou a-t-elle besoin de données/modèles externes ?

S'il n'y a aucun code de reproduction du tout, ou si l'issue manque d'informations critiques (pas de message d'erreur, pas de description du comportement attendu par rapport au comportement réel), ajoutez le label needs reproduction (s'il n'est pas déjà présent) via gh issue edit, puis arrêtez et signalez à l'utilisateur. Ne tentez pas d'écrire une reproduction à partir de la description sans être demandé.

Inversement, si l'issue a déjà le label needs reproduction mais a une reproduction valide, supprimez le label via gh issue edit --remove-label "needs reproduction".

Appliquez la liste de contrôle de sécurité (voir ci-dessus) à la reproduction avant de l'exécuter.

Si la reproduction nécessite des packages tiers qui ne sont pas installés (par ex. transformers, torchvision, numpy), arrêtez et demandez à l'utilisateur comment procéder plutôt que de les installer vous-même.

Résumez votre évaluation avant de continuer.

4) Vérifier la vérification récente

Si un commentaire des six derniers mois confirme déjà que l'issue se reproduit toujours (avec une erreur correspondante et une reproduction raisonnable), arrêtez et demandez à l'utilisateur s'il souhaite re-vérifier ou passer directement à la minimisation.

5) Vérifier si elle se reproduit toujours

Extrayez le code de reproduction dans un fichier temporaire sous /tmp/ et exécutez-le avec un délai d'expiration de 120 secondes. Pour les reproductions impliquant torch.compile qui appellent compile plusieurs fois dans le même processus, ajoutez torch._dynamo.reset() entre les invocations pour réinitialiser l'état Dynamo en mémoire. C'est inutile pour les scripts qui compilent une fois et se terminent.

Si le script expire, envisagez si un hang est le bug signalé ou un problème sans rapport, et signalez à l'utilisateur.

Enregistrez la version de PyTorch avant l'exécution (python -c "import torch; print(torch.__version__)") pour inclusion dans le rapport.

Vérifiez à la fois le code de sortie et la sortie pour déterminer le résultat. Un code de sortie > 128 indique que le processus a été tué par un signal (par ex. segfault = 139, OOM kill = 137) — c'est une reproduction d'accident valide même sans traceback Python. Si la reproduction plante avec une erreur CUDA mémoire insuffisante et OOM n'est pas le bug signalé, essayez de réduire la taille des tenseurs avant de conclure qu'elle ne se reproduit pas. Pour les bugs de correction (mauvais résultats numériques plutôt que des plantages), la reproduction devrait inclure une assertion qui échoue quand le bug est présent. Si la reproduction originale ne fait que sortir sans assertion, ajoutez une assertion simple basée sur le comportement attendu décrit dans l'issue (par ex. assert torch.allclose(actual, expected)) pour que la reproduction ait un signal pass/fail clair.

Si le résultat est inconsistant selon les exécutions, exécutez 3-5 fois pour évaluer l'instabilité. Pour les bugs non-déterministes, essayez de définir PYTHONHASHSEED=0 et un torch.manual_seed fixe pour stabiliser la reproduction. Signalez le ratio succès/échec à l'utilisateur. Les reproductions instables sont toujours des bugs valides — notez l'instabilité dans le commentaire de l'issue (étape 8) et incluez le ratio succès/échec.

Trois résultats possibles (pour déterminer quel résultat s'applique, comparez la classe d'exception et une sous-chaîne distinctive du message d'erreur — la sous-chaîne doit être assez spécifique pour identifier le bug, par ex. RuntimeError: expected scalar type Float plutôt que simplement RuntimeError) :

  • Même erreur que signalée : le bug se reproduit toujours. Continuez à l'étape 6.
  • Aucune erreur (passe) : le bug peut avoir été corrigé. Essayez d'identifier le PR qui corrige (voir étape 5a), puis signalez à l'utilisateur. L'issue sera fermée à l'étape 8.
  • Erreur différente : distinguez les problèmes de configuration (import manquant, API renommée) qui peuvent être corrigés des bugs véritablement différents. Si l'erreur est due aux changements API entre la version signalée et la version actuelle (fonctions renommées, modules déplacés, signatures modifiées), adaptez la reproduction pour utiliser l'API actuelle tout en préservant l'intention originale. Si l'erreur est peu claire, envisagez de re-exécuter avec TORCH_LOGS=+dynamo ou autres drapeaux de logging pertinents pour une sortie de diagnostic plus détaillée. Signalez les erreurs véritablement différentes à l'utilisateur.

5a) Identifier quand et comment c'était corrigé

Quand le bug ne se reproduit plus, essayez de déterminer quelle version l'a corrigé et quel PR a introduit la correction.

Bisection de version : Vérifiez si des environnements conda nommés pytorch-<version> (par ex. pytorch-2.6, pytorch-2.8) sont disponibles (conda env list | grep pytorch-). S'ils existent, recherche binaire sur eux pour trouver la première version où le bug est corrigé. Exécutez depuis /tmp dans une sous-coque et videz PYTHONPATH pour éviter de récupérer l'arborescence source locale (ce qui causerait des erreurs d'import torch._C) : (cd /tmp && PYTHONPATH= conda run -n pytorch-<version> python /tmp/repro_....py). Pour accélérer la bisection, choisissez 2-3 points de sondage régulièrement espacés de la plage candidate et testez-les en parallèle à chaque tour (par ex. si les candidats vont de 2.2 à 2.8, testez 2.4 et 2.6 simultanément pour diviser la plage en tiers). Si aucun environnement conda versionné n'est disponible, ignorez la bisection et signalez simplement que le bug ne se reproduit plus sur la version actuelle.

Identification du PR : Essayez de trouver le PR spécifique qui a corrigé le bug. Utilisez blame du contrôle de version sur le code de correction pertinent pour trouver l'ensemble de changements, puis recherchez le message de commit pour le numéro du PR (format (#NNNNN)). Alternatively, recherchez dans l'historique du contrôle de version les commits touchant le fichier pertinent avec un mot clé connexe. Si cela ne donne pas une réponse claire rapidement, signalez simplement la version — ne passez pas de temps supplémentaire sur l'identification du PR.

6) Minimiser la reproduction

Enregistrez la reproduction originale en travail sur /tmp/repro_<issue_number>_original.py avant de faire des modifications.

Tout d'abord, évaluez si la reproduction est déjà raisonnablement minimale. Minimisez uniquement si la reproduction a une complexité significativement inutile (grands modèles, chemins de code inutilisés, dépendances inutiles, etc.). Quand vous comptez la complexité, ne comptez pas le boilerplate irréductible — par ex. une définition de tensor subclass qui ne contient que les dunder methods requis (__new__, __init__, __torch_dispatch__, __tensor_flatten__, __tensor_unflatten__) n'est pas réductible même si elle fait 20+ lignes. Concentrez-vous sur la possibilité de réduire le code trigger et la complexité du modèle/setup. Si les seules « simplifications » possibles sont cosmétiques (inlining de variables, suppression de __repr__), ignorez la minimisation.

Réduisez systématiquement la reproduction en testant si chaque simplification déclenche toujours la même erreur. Utilisez un délai d'expiration plus court (30-60 secondes) pendant la minimisation car les reproductions simplifiées doivent s'exécuter plus rapidement. Exécutez plusieurs simplifications candidates en parallèle quand elles sont indépendantes (c.-à-d. qu'elles modifient des parties non-chevauchantes du code et l'une ne dépend pas de ce que l'autre supprime).

Stratégies de réduction (à appliquer à peu près dans cet ordre) :

  1. Supprimer les imports et setup inutiles (initialisation distribuée, variables d'env, logging)
  2. Réduire le modèle (remplacer les grands modules par des équivalents minimaux)
  3. Supprimer le wrapper class/module si une fonction seule suffit
  4. Réduire la taille des tenseurs (grandes dims → petites dims comme 4 ou 8)
  5. Supprimer les exigences device/dtype (essayez d'abord CPU et float32)
  6. Simplifier le calcul (remplacer les ops complexes par des ops minimales qui déclenchent toujours le bug)
  7. Supprimer le contrôle de flux inutile (branches, boucles, conditions)
  8. Essayer des backends plus simples (par ex. aot_eager au lieu de inductor) si le bug n'est pas spécifique au backend

Après chaque tour, vérifiez que l'erreur se reproduit toujours — même classe d'exception et une sous-chaîne distinctive du message d'erreur comme décrit à l'étape 5 (les différences mineures de traceback sont acceptables). Pour les bugs de correction, préservez l'assertion qui démontre le mauvais résultat et vérifiez qu'elle montre toujours le même comportement incorrect. En fusionnant plusieurs simplifications parallèles couronnées de succès, vérifiez que le résultat combiné se reproduit toujours car les simplifications indépendantes peuvent interagir. Arrêtez de minimiser quand la reproduction est sous ~20 lignes de code non-blanc non-import, ou quand deux tours consécutifs (où un tour est un passage complet par les stratégies de réduction applicables) échouent à simplifier davantage.

7) Appliquer les corrections triviales

Si l'analyse révèle une correction triviale (par ex. supprimer une annotation xfailIfTorchDynamo ou expectedFailure obsolète d'un test parce que le problème sous-jacent est corrigé), signalez la correction à l'utilisateur et demandez s'il faut l'appliquer. Ne modifiez pas les fichiers source sans approbation de l'utilisateur. À l'étape 8, mentionnez la correction indépendamment de si elle a été appliquée ou déclinée.

8) Signaler les résultats

Si la reproduction a été minimisée, enregistrez-la sur /tmp/repro_<issue_number>.py pour que l'utilisateur puisse l'exécuter directement.

Présentez les résultats à l'utilisateur en incluant :

  • Si le bug se reproduit toujours (et sur quelle version PyTorch)
  • Le PR qui corrige, s'il est identifié (uniquement si le bug ne se reproduit plus)
  • Le script de reproduction minimisé (uniquement si nous l'avons minimisé)
  • Les conditions nécessaires pour déclencher le bug
  • Toute correction triviale identifiée à l'étape 7 (qu'elle ait été appliquée ou non)
  • Action recommandée suivante (par ex. « toujours un bug valide », « apparemment corrigé », « a besoin de plus d'infos du signalez »)

Après présentation des résultats, commentez toujours l'issue avec les résultats. Gardez le commentaire concis — ne répétez pas les informations déjà sur l'issue. Incluez une reproduction uniquement si elle a été matériellement modifiée par rapport à l'originale (par ex. minimisée, imports modernisés, corrigée pour s'exécuter sur l'API actuelle). Incluez les conditions trigger uniquement si elles sont des résultats nouveaux non déjà discutés dans les commentaires antérieurs. Si le seul résultat est « toujours se reproduit » ou « ne se reproduit plus », un court commentaire est suffisant.

Cette issue [se reproduit toujours / ne se reproduit plus] sur PyTorch <version>.

[Si corrigé et PR identifié :]
Corrigé par #NNNNN.

[Si minimisé ou modernisé — incluez la reproduction uniquement si modifiée par rapport à l'originale :]
Reproduction minimisée :

\`\`\`python
<script de reproduction>
\`\`\`

[Uniquement si nouveaux résultats sur les conditions trigger :]
Toutes les conditions suivantes sont nécessaires pour déclencher le bug :
- <condition 1>
- <condition 2>

(Analyse faite par Claude.)

Si le bug ne se reproduit plus, après commentaire, fermez l'issue : gh issue close <NUMBER> --repo pytorch/pytorch --reason completed --comment "Fermeture puisque cela a été corrigé dans PyTorch <version>.". Ne demandez pas à l'utilisateur avant de fermer — les bugs corrigés doivent toujours être fermés.

Après la dernière modification GitHub, restaurez l'état d'abonnement aux notifications (voir « Préserver l'état d'abonnement aux notifications » ci-dessus) — sauf si l'issue a été fermée, auquel cas ignorez la restauration.

Skills similaires