


Maintenir une bibliothèque d’UI multiplateforme sans casser ses couches de paquets
Pour une bibliothèque d’UI découpée en paquets par couches pour plusieurs plateformes : l’IA préserve les couches et les exports publics, crée les adaptateurs de fournisseurs avec une structure de fichiers fixe et suit les règles de publication et de revue.
Pour utiliser ces règles d'IA
## Décider dans quelle couche de paquets va chaque partie du code
Organisez la base de code comme une petite pile de couches, chacune avec une seule responsabilité : des primitives réactives qui exécutent des hooks de framework comme des ressources headless ; une couche de liaison qui relie ces primitives au système d'état du framework ; un runtime central agnostique du framework qui détient la logique métier ; et un paquet de distribution par plateforme cible (web, natif, terminal) qui réexporte le noyau et ajoute des primitives d'interface spécifiques à la plateforme. Un kit de composants privé conserve la source d'interface canonique, qui est copiée dans les projets consommateurs plutôt que publiée comme dépendance de runtime. L'outillage de compilation partagé vit dans son propre paquet interne.
Règles de placement :
- La logique agnostique du framework appartient au paquet central. Les runtimes couplés à une plateforme appartiennent aux paquets de distribution.
- Le runtime couplé au framework vit dans un sous-chemin du paquet central que chaque distribution réexporte, de sorte qu'il n'existe qu'une seule implémentation au lieu d'une copie par plateforme.
- Les consommateurs n'installent jamais le paquet central directement ; ils installent exactement un paquet de distribution.
- Les paquets d'intégration qui mappent un fournisseur externe sur le runtime central constituent leur propre couche et ne dupliquent jamais la logique du noyau.
## Ajouter des exports publics sans jamais retirer ceux déjà publiés
La surface publique de chaque paquet publié est en ajout seul. Lorsqu'un symbole est déplacé, repointez son export vers le nouveau fichier ; ne supprimez jamais un export déjà livré. Un audit interne au dépôt ne peut pas voir les consommateurs en aval, donc supprimer même un type apparemment inutilisé constitue un changement cassant. Livrez un véritable changement de comportement comme un changement délibéré à part entière, jamais glissé discrètement dans un déplacement ou un renommage.
Lorsqu'une migration longue remplace une implémentation historique par une nouvelle architecture, le point d'entrée du paquet réexporte les deux chemins pendant toute la durée et reste malgré tout en ajout seul. Pendant une telle migration :
- Ajoutez le nouveau comportement sur la surface canonique, jamais sur le chemin déprécié.
- Réutilisez la primitive centrale partagée au lieu d'en écrire une copie propre à chaque intégration.
- Préférez la réutilisation à l'introduction d'un énième indicateur de désactivation.
## Créer un adaptateur de fournisseur avec la structure de fichiers standard
Un adaptateur d'intégration mappe un fournisseur externe sur le runtime central au moyen d'un hook d'entrée, d'un ensemble de hooks d'accès et de convertisseurs purs. La règle générale qui gouverne tout cela : le fichier de runtime orchestre, les modules purs convertissent, le contrôleur (s'il en existe un) réduit l'état, et les hooks se contentent de lire.
- Choix du runtime central. Appuyez-vous soit sur le runtime à magasin externe, lorsque les messages proviennent d'une source détenue par le fournisseur, soit sur le runtime local associé à un adaptateur de modèle de conversation, lorsque le fournisseur ne détient aucun état de fil de discussion. Encapsulez l'un ou l'autre dans le runtime multi-fils partagé lorsque des listes de fils sont nécessaires. Réutilisez les noyaux de runtime livrés ; ne fabriquez pas artisanalement un porteur d'état de runtime de fil parallèle.
- Exposition de l'état. Exposez l'état du runtime aux hooks d'accès via la fabrique partagée « runtime-extras », et non via une marque à symbole unique et un garde fabriqués maison.
- Ensemble de fichiers standard. Un fichier d'entrée de runtime dédié uniquement à l'orchestration, une instance « runtime-extras », un fichier de hooks d'accès et d'actions, un fichier pur de conversion de messages bidirectionnelle, et un fichier de types. Ajoutez un contrôleur de fil ainsi qu'un réducteur d'état pur lorsque l'adaptateur détient l'état du fil, et une entrée séparée en sous-chemin serveur ou node lorsque le protocole détient le format de transmission.
- Code réservé au serveur. Conservez le code réservé au serveur et le code du SDK du fournisseur dans le sous-chemin serveur ou node, en dehors de l'entrée par défaut et de l'entrée native.
- Tests. Placez-les à côté de chaque module, en couvrant le convertisseur dans les deux sens, le réducteur ou le contrôleur, et chaque hook d'accès.
Les choix dictés par le fournisseur restent flexibles : sur quelle primitive centrale s'appuyer, enveloppe légère face à accumulateur face à contrôleur, transports sur mesure, richesse du support de supervision humaine dans la boucle, et profondeur du support des listes de fils.
## Aligner les fonctions des adaptateurs et éviter que les convertisseurs plantent
Parité entre runtimes. Maintenez les capacités à parité entre les adaptateurs. Lorsqu'une capacité arrive sur un runtime, exposez-la sur tous les runtimes où le concept s'applique, ou consignez par écrit la raison d'une divergence. Un écart de parité silencieux devient une attente rompue pour quiconque change d'adaptateur.
Convertisseurs défensifs. Les convertisseurs et les moteurs de rendu de blocs de contenu ne doivent jamais lever d'exception sur des champs indéfinis, des champs manquants ou des capacités de plateforme indisponibles. Protégez-vous contre les charges utiles de fournisseur non conformes aux spécifications, et gardez les API de plateforme réservées au navigateur en dehors des chemins de code qui s'exécutent aussi sur un serveur, dans un moteur de rendu de terminal, ou sur une cible native.
Modèles à ne jamais introduire :
- Un porteur d'état de runtime de fil parallèle qui duplique les noyaux de runtime livrés.
- Une astuce de re-rendu combinant notification de changement et compteur de version pour faire le pont entre un état non réactif et l'arbre de composants.
- Greffer des méthodes sur des objets par création de prototype.
- Appliquer un monkeypatch à des objets détenus par l'appelant.
## Compiler avec l’outil de build commun et ajouter des notes de version
Chaque paquet publiable se compile via l'outil de compilation partagé. N'ajoutez pas de configuration de compilation propre à un paquet et ne recourez pas à un empaqueteur alternatif ni directement à la CLI du compilateur. Les cartes d'export sont uniquement de type module et privilégient les types en premier, les déclarations de types étant listées avant la condition par défaut, et les paquets se déclarent exempts d'effets de bord.
- Un seul répertoire source d'interface canonique. Les modèles et les exemples y font référence via des correspondances de chemins du compilateur et ne comportent aucun doublon identique octet pour octet, sauf lorsqu'un starter minimal livre délibérément sa propre copie. Un script de synchronisation maintient cette copie identique octet pour octet, et chaque divergence intentionnelle est déclarée dans la liste des exceptions du script.
- Les conventions de structure de répertoires diffèrent selon l'intention : des répertoires parallèles lorsque chaque primitive existe dans les deux variantes, et des suffixes de variante clairsemés lorsque seuls certains fichiers possèdent une seconde variante, le fichier sans suffixe faisant office de source par défaut.
- Relancez la tâche de vérification du plugin du compilateur lors de la mise à jour de la chaîne d'outils du compilateur ou de ses plugins. Une compilation réussie à elle seule ne prouve pas que la chaîne d'outils est encore intacte.
- Tout paquet que le bundle publié importe au moment de l'exécution doit figurer parmi les dépendances de runtime, et non parmi les dépendances de développement, afin que l'empaqueteur le traite comme externe. Le mal classer intègre le paquet en ligne et entraîne des imports transitifs non résolvables dans les compilations des consommateurs.
- Une entrée de composant installable par copie doit être autonome : énumérez chaque import de composant interne et chaque import de feuille de style comme dépendance du registre, afin que l'installateur ne dépose jamais un fichier contenant un import non résolvable.
- Chaque changement apporté à un paquet publié s'accompagne d'une entrée de notes de version. Utilisez toujours une montée de version de type correctif ; les montées de version mineures et majeures requièrent l'approbation d'un mainteneur. Les paquets privés en sont exemptés.
## Ne pas modifier à la main la référence d’API générée
L'ensemble de l'arborescence de référence d'API générée est la propriété de son script générateur, et non d'une intervention manuelle.
- Les pages portant le marqueur de génération de page complète appartiennent entièrement au générateur. Les modifications manuelles à l'intérieur d'un bloc généré sont détruites lors de la prochaine exécution ; le texte durable doit plutôt figurer dans les commentaires de la documentation source.
- Le contenu écrit à la main qui doit survivre à la régénération ne doit se trouver que dans les emplacements manuels ou d'exemples désignés à cet effet.
- Une page entièrement maintenue à la main doit porter le marqueur d'exclusion de la génération automatique immédiatement après son frontmatter. Ce marqueur maintient la page listée dans la navigation de la section et l'exempte de l'élagage ; sans lui, une page inattendue non générée est signalée comme non gérée et obsolète, et le mode strict échoue.
- Les fichiers de métadonnées de navigation de section et de racine sous cette arborescence sont eux aussi générés. Ne les modifiez jamais à la main.
- L'intégration continue régénère en mode strict et échoue au moindre écart ; régénérez donc en local avant de soumettre votre changement.
## Laisser le style au linter et au formateur, et écrire peu de commentaires
Exécutez l'outil d'analyse statique du dépôt, ainsi que son mode de correction automatique pour les problèmes mécaniques. Le formateur est seul responsable de la mise en forme ; ne formatez jamais à la main, et ne soulevez jamais de remarques sur l'espacement ou la mise en forme lors d'une revue.
Comme les ressources réactives reposent sur des hooks de framework, les règles relatives aux tableaux de dépendances et à l'ordre des hooks sont appliquées par les règles natives de l'outil d'analyse statique pour les hooks. Pour que ces règles puissent analyser un corps de fonction, celle-ci doit être nommée afin que l'outillage du framework la reconnaisse comme un hook : extrayez une ressource dans une fonction préfixée « use » et passez-la à la fabrique de ressources, et donnez aux fonctions d'assistance de création de racine une expression de fonction nommée plutôt qu'une fonction fléchée.
Partez du principe qu'il ne doit y avoir aucun commentaire de code. Supprimez tout commentaire qui reformule le code, retrace son historique, ou fait référence à la demande de changement en cours. Ne conservez un commentaire que lorsqu'il documente une raison qu'aucun lecteur futur ne pourrait retrouver à partir du code lui-même, telle qu'un invariant caché, une contrainte non évidente, ou un contournement lié à une dépendance amont, et rédigez-le sous forme de phrase déclarative neutre.
## Placer les tests à côté de chaque module et ne pas utiliser de snapshots
Les tests vivent à côté du module testé et l'importent par chemin relatif, jamais via le nom du paquet, de sorte qu'un test exerce toujours la source locale plutôt qu'un résultat de compilation résolu.
- Couvrez le convertisseur dans les deux sens, le réducteur ou le contrôleur, et chaque hook d'accès dans son propre fichier de test.
- Construisez les simulacres (mocks) avec l'assistant de mock hissé (« hoisted ») du lanceur de tests, et étalez toujours le module original dans le mock afin que les exports non modifiés conservent leur comportement réel.
- N'utilisez pas d'assertions par instantané (« snapshot ») ; vérifiez les valeurs précises qui comptent, afin qu'un échec désigne la régression réelle.
## Soumettre des changements à but unique, avec description et reproduction
- Une seule préoccupation par demande de changement. Découpez un changement volumineux ou multi-objectifs en soumissions distinctes, afin que chacune soit assez petite pour être revue, puisse être approuvée sur ses propres mérites, et puisse être annulée proprement en cas de régression.
- Rédigez une véritable description, pas un texte générique généré automatiquement. Indiquez en quoi consiste le changement, pourquoi il est nécessaire, et comment il a été réalisé, afin qu'un relecteur dispose d'assez de contexte sans devoir décortiquer le diff. Le titre nomme le changement en une ligne ; les compromis et les divergences appartiennent au corps du texte.
- Justifiez toute divergence par rapport à la pratique existante du dépôt. Si la base de code résout déjà un problème d'une certaine manière, suivez-la ; quelqu'un a choisi cette approche délibérément, donc une approche différente nécessite une raison écrite plutôt qu'une simple préférence personnelle.
- Gardez les changements sans rapport hors du diff. Les suppressions au passage, les remaniements, la remise en forme, et les ajouts « tant que j'y suis » enterrent le véritable changement et ralentissent la revue. Ouvrez une soumission séparée, ou un ticket lorsque le but est simplement de signaler quelque chose.
- Ouvrez un ticket avant un changement de fonctionnalité non trivial. Un seul paragraphe permet à un mainteneur de confirmer que la direction est souhaitée avant qu'un temps significatif ne soit investi. Les corrections triviales, comme des fautes de frappe ou de petites modifications de documentation, ne nécessitent aucun ticket.
- Joignez une reproduction minimale. Chaque rapport de défaut ou correctif nécessite une reproduction exécutable sur la version exacte concernée. Si un mainteneur ne peut pas reproduire le comportement rapporté, le rapport est clos.
- Confirmez que le correctif s'attaque à la cause racine. Vérifiez que l'échec rapporté disparaît avec le changement et réapparaît sans lui, et expliquez le mécanisme en jeu. Un changement qui masque le symptôme en substituant une valeur, en supprimant une fonctionnalité, ou en élargissant un type est rejeté.
- Gardez la documentation et la référence générée parfaitement synchronisées avec le code au sein de la même soumission. Régénérez la référence et mettez à jour chaque page dont les affirmations sont invalidées par le changement. L'automatisation doit échouer en cas d'écart de documentation plutôt que de laisser une API disparaître silencieusement ou d'en reporter la mise à jour à un changement ultérieur.
## Éviter les changements refusés en revue, comme réinventer des fonctions du framework
- Réinventer ce que le framework fournit déjà. Appuyez-vous sur les primitives partagées plutôt que de réimplémenter l'état du runtime, la réactivité, ou d'autre plomberie ; un mécanisme parallèle s'éloigne progressivement du reste de la base de code et coûte cher à maintenir pour tout le monde.
- Ajouter à la légère des dépendances lourdes ou non open source. Les bibliothèques sous licence commerciale sont interdites dans un projet open source, et les dépendances lourdes ou les nouvelles étapes de compilation affectent chaque contributeur ; elles nécessitent donc une justification et l'aval d'un mainteneur.
- Livrer une nouvelle application d'exemple qui duplique une application existante. Parcourez d'abord les exemples existants ; si l'un d'eux couvre déjà l'intégration, un quasi-doublon n'apporte rien de nouveau et devrait plutôt se trouver dans un dépôt personnel séparé, sauf si un mainteneur l'a demandé.
- Corriger un défaut en introduisant une régression d'expérience utilisateur. Désactiver une fonctionnalité, supprimer une animation, ou élargir une interface pour masquer un défaut de rendu ou de scintillement n'est pas un correctif acceptable, même lorsque le rapport initial est bien réel.
- Ajouter des vérifications défensives que la chaîne d'outils applique déjà. Sous les paramètres les plus stricts du compilateur, y compris la vérification exacte des propriétés optionnelles, les gardes de nullabilité et de propriétés optionnelles sont redondantes.
- Outrepasser les vérifications requises pour fusionner. Ne forcez pas une fusion tant que la liste des vérifications ne montre pas que chaque ligne est réussie ou explicitement ignorée ; filtrez les lignes réussies et ignorées et confirmez qu'il n'en reste aucune, car une liste tronquée a déjà, par le passé, dissimulé une vérification en échec. Lorsqu'une vérification propre au dépôt échoue ou est en attente, résolvez-la au lieu de la contourner.
## Ne pas écraser les changements d’autres agents dans le même arbre de travail
Partez du principe que d'autres agents automatisés et des humains modifient le même arbre de travail au même moment. Avant de modifier quoi que ce soit, inspectez l'état actuel de l'arbre de travail et évitez d'écraser des changements que vous n'avez pas faits vous-même. Gardez chaque changement étroitement circonscrit afin que les travaux concurrents puissent être séparés proprement.
Respectez le flux de travail de contrôle de version du projet plutôt que de supposer que des commandes brutes de contrôle de version conviennent. Lorsque la branche de travail indique que le projet est géré via un outil de flux de travail superposé au contrôle de version, ne créez pas de branches, ne mettez pas de fichiers en zone d'indexation, ne validez pas (commit), et ne réécrivez pas l'historique avec des commandes directes, sauf demande explicite.
