


Modifier un débogueur bas niveau en écrivant d'abord les tests, sans casser les autres plateformes
Quand une IA modifie un débogueur de langage compilé qui tourne sur plusieurs systèmes et backends. Elle écrit d'abord un test qui échoue, isole le code propre à chaque OS, vérifie tous les backends en changeant une interface partagée et commence les commits par le sous-système.
Pour utiliser ces règles d'IA
## Lire la couche responsable d'un comportement avant de modifier le débogueur
Un débogueur pour un langage compilé est un système complexe et multicouche. Y travailler efficacement exige de comprendre les mécanismes internes du débogage, les formats binaires d'informations de débogage, le contrôle de processus spécifique à l'OS, et les mécanismes internes du runtime du langage.
Traitez chaque modification comme touchant un système où la correction sur l'ensemble des plateformes et des backends compte plus que la vitesse brute. Une erreur subtile dans l'accès mémoire, la gestion des registres ou le comportement d'un backend se manifeste comme une défaillance difficile à reproduire, propre à une seule plateforme.
Avant de modifier quoi que ce soit, lisez la couche environnante pour comprendre quelle abstraction possède le comportement que vous changez. Les modifications des interfaces partagées se propagent vers l'extérieur, jusqu'à chaque backend qui les implémente : évaluez donc le rayon d'impact avant de commencer.
## Écrire un test qui échoue avant tout changement de code, sans jamais sauter de tests
Toute modification de code doit suivre le développement piloté par les tests. Ce n'est pas optionnel.
Suivez le cycle Rouge-Vert-Refactorisation-Vérification :
1. **ROUGE** - écrivez d'abord un test qui échoue ; il doit échouer pour la bonne raison.
2. **VERT** - écrivez le code minimal nécessaire pour le faire passer.
3. **REFACTORISATION** - nettoyez le code en gardant les tests au vert.
4. **VÉRIFICATION** - exécutez la suite de tests complète avant de committer.
Règles obligatoires :
1. N'écrivez jamais de code d'implémentation sans avoir d'abord un test qui échoue.
2. Ne committez jamais de code sans tests.
3. Ne sautez jamais de tests et ne les marquez jamais comme ignorés pour faire passer la CI.
4. Ne désactivez jamais de tests existants - corrigez le test ou corrigez le code.
5. Chaque test vérifie un comportement précis.
6. Les noms de tests décrivent le scénario testé.
Quand écrire un test en premier semble difficile, cette difficulté est un signal, pas une excuse. Cela signifie généralement qu'une fonction fait trop de choses, que les dépendances sont trop couplées, ou que les exigences ne sont pas claires. Ne sautez pas le TDD - corrigez le problème de conception qu'il révèle.
## Choisir où va chaque type de test et garder des résultats toujours identiques
Où placer les tests :
- **Tests unitaires** : à côté du code qu'ils couvrent.
- **Tests d'intégration** : un répertoire dédié aux tests de bout en bout.
- **Tests spécifiques à une plateforme** : conditionnés par des tags de build / compilation conditionnelle.
- **Tests spécifiques à un backend** : testent chaque backend séparément.
- **Fixtures** : conservées comme sources compilées pendant l'exécution des tests, pas comme binaires pré-construits ; utilisez l'assistant partagé de localisation de fixtures plutôt que de bricoler votre propre recherche de chemin.
Normes de qualité :
- Testez le comportement, pas l'implémentation.
- Privilégiez une assertion par test, afin qu'un échec pointe vers une seule cause.
- Nommez clairement, en utilisant un motif séparé par des underscores indiquant ce qui est testé, le scénario, et le résultat attendu.
- Réutilisez les utilitaires de test existants plutôt que de les réinventer.
- Les tests doivent être déterministes - pas de valeurs aléatoires, pas de conditions de concurrence.
- Pour les tests de format de sortie, vérifiez la sortie complète attendue plutôt qu'une sous-chaîne. La comparaison de la sortie complète détecte les régressions - espaces superflus, champs réordonnés, changements de format - que les vérifications de sous-chaîne laissent passer silencieusement.
## Séparer entre les couches l'interface en lecture seule et celle qui modifie l'état
Le système est organisé en couches avec une séparation claire des responsabilités : une CLI au sommet, une couche de service/protocole en dessous, des opérations de haut niveau plus bas, une abstraction processus/cible sous celles-ci, et des implémentations de backend enfichables tout en bas (contrôle direct de processus au niveau OS, client de protocole distant, analyse de dump hors ligne, traçage au niveau noyau).
Utilisez une conception d'interface à deux niveaux à la frontière de l'abstraction :
- Une interface publique en lecture seule que les appelants utilisent pour observer l'état.
- Une interface interne séparée qui porte toutes les opérations modifiant l'état.
- Un objet enveloppe à état autour de l'interface interne, plus une abstraction de groupe pour gérer plusieurs cibles à la fois.
Les backends sont enfichables derrière l'interface interne. Quand vous modifiez cette interface, le changement s'applique généralement à TOUTES les implémentations - tenez compte de chaque backend, pas seulement de celui devant vous.
## Placer le code propre à chaque OS et architecture dans ses propres fichiers
Isolez le code spécifique à l'OS et à l'architecture via une compilation conditionnelle basée sur le nom de fichier - des fichiers sources par OS et des fichiers sources par architecture, sélectionnés par le système de build plutôt que par des branchements à l'exécution.
- Ne placez jamais de code spécifique à une plateforme dans des fichiers génériques.
- Confinez le code spécifique à une plateforme aux backends qui en ont réellement besoin (le backend de contrôle direct de l'OS et le backend de protocole distant), pas la logique centrale partagée.
- Conservez les détails de registres, d'ABI et de convention d'appel dans des fichiers par architecture lorsque plusieurs architectures sont prises en charge.
Bien fait, le noyau partagé reste portable et l'ajout d'une nouvelle plateforme devient une question d'ajout de fichiers, pas de réécriture du code générique.
## Éviter les erreurs courantes en lisant la mémoire du processus et en posant des points d'arrêt
1. **Ne modifiez jamais l'état partagé directement** - passez par les méthodes de l'interface interne qui assurent le verrouillage et la cohérence de l'état.
2. **Les opérations spécifiques à un backend diffèrent** - une action conceptuelle (par exemple, insérer un point d'arrêt) est implémentée différemment selon le backend ; ne présumez jamais d'un mécanisme unique.
3. **Distinguez les abstractions similaires** - un thread OS et une unité de concurrence au niveau langage ne sont pas la même chose ; sachez laquelle une opération cible. La plupart des opérations agissent sur l'unité au niveau langage, pas sur le thread brut.
4. **Sécurité mémoire** - gérez toujours les erreurs lors de la lecture de la mémoire du processus cible. Le processus peut mourir, la mémoire peut être démappée, ou une adresse peut être invalide.
5. **N'ajoutez pas de passes redondantes** - si une passe existante itère déjà sur toutes les unités (unités de compilation, symboles, etc.), ajoutez votre vérification par unité à cet endroit plutôt que de scanner séparément.
6. **Placez l'instrumentation après le prologue** - positionnez les points d'arrêt/sondes après le prologue de fonction plutôt qu'au point d'entrée brut, car le prologue de vérification de pile d'un runtime peut se redéclencher s'il est instrumenté à l'entrée.
## Commencer les messages de commit par le sous-système concerné et expliquer pourquoi
Utilisez un format de commit basé sur le sous-système : une ligne de sujet nommant le sous-système concerné suivie d'un deux-points et d'un court résumé du changement, puis une ligne vide, puis un paragraphe de corps expliquant pourquoi le changement a été fait, puis une ligne de clôture référençant le ticket qu'il corrige.
- Préfixez le sujet par le sous-système concerné afin que l'historique soit consultable par domaine.
- Gardez la ligne de sujet concise et limitée (environ 70 caractères).
- Utilisez le corps pour expliquer pourquoi le changement a été fait, avec un retour à la ligne (environ 80 caractères).
- Référencez le ticket motivant afin que chaque changement soit relié à sa cause.
Des messages structurés maintiennent le journal des modifications navigable et facilitent la reconstruction des raisons de tout changement donné.
