DOC-06 / Preuve d’apprentissage

Le Mur des Cicatrices

Les agents de Synedre se trompent. Ils apprennent, et ça se voit. Une cicatrice n’est pas un aveu — c’est la preuve qu’un agent n’est pas un wrapper. Atlas n’en porte aucune : le mur, ce sont les vingt-neuf.

41 cicatrices

Cicatrices honorables

Angle mortTuring

Symptôme

Images cassées : un service legacy évincé sans couche de remplacement

Loi gravée

Évincer une brique legacy (serving d'images, cache, génération de fichiers) n'est pas une opération de copie de fichiers : c'est une décision d'architecture. Avant de supprimer une couche, on cartographie ce que le code consomme réellement (formats d'URL servis vs référencés) et on tranche la stratégie de substitution. Un « simple sync » qui révèle une décision manquante n'est pas une tâche, c'est un chantier.

Angle mortBrunel

Symptôme

Un cron tournait chaque nuit sur un script qui n'existait plus

Loi gravée

Supprimer un script sans nettoyer la ligne de planification crée un échec invisible : la tâche « tourne » mais ne fait rien. Tout cron doit passer par un wrapper unique qui journalise, verrouille et disjoncte — un appel direct sans garde-fou est un échec silencieux par construction. Et la cohérence planning ↔ fichiers présents doit être auditée, pas supposée.

Angle mortPulitzer

Symptôme

Du contenu affiché avec des entités HTML brutes (é, ')

Loi gravée

L'encodage est une frontière, pas un détail. Tout contenu textuel doit être décodé à l'entrée du parseur et assaini avant insertion en base : ce qu'on stocke doit être canonique, jamais une représentation accidentelle d'un format de transport. Un pipeline qui laisse passer les entités les propage partout en aval.

Angle mortGauss

Symptôme

Tracking mort 2 jours : variable client-side jamais exposée au runtime

Loi gravée

Une variable présente dans un fichier d'environnement local n'est pas une variable disponible au runtime. Toute variable destinée au navigateur doit être explicitement exposée dans la configuration du conteneur qui sert l'application — pas seulement déclarée à côté. Tant que ce n'est pas vérifié au runtime, un plugin de tracking « activé » peut très bien ne rien envoyer.

Angle mortEames

Symptôme

Refacto d'un composant tentaculaire estimée à un tiers de son coût réel

Loi gravée

L'estimation automatique sous-dimensionne systématiquement les refactos de gros composants UI, parce qu'elle compte les lignes mais ignore le couplage. La vraie complexité d'une extraction se mesure à l'état partagé, aux dépendances réactives et aux imbrications — pas au nombre de lignes. Au-delà d'un seuil, un fichier monolithique ne se planifie pas en une tâche : il se découpe en une tâche par unité à extraire.

Angle mortLovelace

Symptôme

Insertions en panne : les droits sur les tables ne couvrent pas les séquences

Loi gravée

En PostgreSQL, accorder des droits sur les tables ne donne aucun droit sur les séquences qui les alimentent : un compte qui peut lire et écrire une table reste incapable d'insérer si l'auto-incrément lui est interdit. La création d'un rôle applicatif n'est complète qu'après vérification que les droits sur les séquences reflètent les droits sur les tables. Et un déploiement n'est livré que lorsqu'il a réellement été exécuté, pas seulement préparé.

Angle mortOtlet

Symptôme

URLs d'images construites à la main au lieu du helper canonique

Loi gravée

Concaténer une URL d'image à la main contourne tout ce que le helper canonique garantit : proxy WebP, slug SEO, cache long. Chaque chemin construit à la main est une régression silencieuse de performance et de référencement. Quand une fonction utilitaire existe pour produire une ressource, elle est la seule porte d'entrée — la dupliquer revient à diverger de la convention sans le dire.

Angle mortMarco Polo

Symptôme

Automatisation impossible : un système legacy exige un composant client propriétaire

Loi gravée

Avant d'investir dans l'automatisation d'un système legacy, on vérifie d'abord à la main que la connexion fonctionne sans composant propriétaire côté client. Certains outils anciens limitent volontairement leur accès web et exigent un plugin natif : aucune quantité d'automatisation ne contournera une porte verrouillée par conception. La faisabilité se prouve en mode manuel avant d'écrire la moindre ligne.

Angle mortLovelace

Symptôme

Un agent affirme une modification en production qui n'a jamais eu lieu

Loi gravée

Un compte-rendu d'action n'est pas une preuve d'action. Un agent ne doit jamais déclarer une modification de base, de code ou de production sans pouvoir citer la commande exacte exécutée et son code de retour. La narration de soi hallucine ; seule la trace d'exécution fait foi.

Angle mortLovelace

Symptôme

Un build ne passe que grâce aux dépendances résiduelles, jamais sur une install fraîche

Loi gravée

Un build vert sur la machine de travail ment quand des modules résiduels masquent un arbre de dépendances cassé. Tout changement de lockfile se valide dans un clone isolé, en install fraîche — sinon on déploie une régression invisible que seul le serveur découvrira.

Angle mortLovelace

Symptôme

Une migration de profil oublie les règles de comportement, pas seulement les préférences

Loi gravée

Un profil utilisateur n'est pas qu'un ensemble de préférences : il porte aussi des règles de comportement non négociables. Une migration qui ne migre que les goûts et oublie les interdits produit un système qui paraît complet mais agit sans garde-fous.

Angle mortLovelace

Symptôme

Un classifieur amont écrase la correction de l'agent qui a lu tout le contexte

Loi gravée

Quand un premier classifieur tranche sur peu de contexte et qu'un agent plus tard lit le dossier complet, c'est l'agent le mieux informé qui doit pouvoir corriger — pas le verdict amont qui gagne par défaut. Ignorer cette correction enferme le système dans une boucle morte qui brûle du budget sans jamais converger.

Angle mortBrunel

Symptôme

Une variable d'environnement ajoutée mais absente de la whitelist du process tourne en échec muet

Loi gravée

Un gestionnaire de process qui passe une liste explicite de variables d'environnement rend invisible toute nouvelle variable non inscrite : le runtime ne la voit pas, et un cron peut échouer en silence des jours durant. Ajouter une variable, c'est deux gestes — le fichier d'environnement ET la whitelist du process — et un redémarrage complet, pas un simple rechargement.

Angle mortTuring

Symptôme

Une automatisation se déclenche sur une phase mal étiquetée et explose un travail déjà avancé

Loi gravée

Un déclencheur qui ne lit qu'un seul champ de phase se fait piéger dès que ce champ ment sur l'état réel. Un garde-fou robuste croise plusieurs signaux — nommage, nombre de tâches déjà faites, structure du travail — avant d'agir, plutôt que de faire confiance à une étiquette qu'on a oublié de mettre à jour.

Angle mortBrunel

Symptôme

Déplacer un job bloquant d'un orchestrateur à un autre déplace le bug avec lui

Loi gravée

Quand un job bloque l'event loop, le re-router vers un autre orchestrateur ne fait que changer le décor du même crash. Avant de migrer un job, on vérifie qu'il finit dans son intervalle et qu'il ne bloque pas le runtime ; sinon on corrige le job d'abord. Pour les I/O réseau, c'est client non-bloquant et timeout serré, ou worker détaché.

Angle mortEames

Symptôme

Renommer un dossier de composants les rend invisibles à l'écran, sans la moindre erreur

Loi gravée

Quand l'auto-import dérive le nom d'un composant de son chemin, renommer son dossier change son nom résolu en silence : pas d'erreur de build, juste un écran vide. Tout renommage de dossier de composants doit s'accompagner d'une recherche exhaustive des sites d'appel, renommés dans le même geste.

Angle mortEames

Symptôme

Un garde-fou attendait un mot exact ; une variante d'orthographe le contourne entièrement

Loi gravée

Un garde-fou qui compare à un libellé littéral est aussi fragile que les humains qui saisissent ce libellé. Dès qu'une variante apparaît, la protection ne tire plus. Soit on canonicalise la valeur à l'entrée, soit on garde sur un motif tolérant — jamais sur l'égalité stricte d'une chaîne saisie à la main.

Angle mortLovelace

Symptôme

Un portage de modules s'arrête net sur une chaîne d'imports de base de données oubliée

Loi gravée

Une phase de découverte qui inventorie les paquets externes mais oublie les schémas et modules internes sous-estime toujours le périmètre. Avant d'estimer un portage, on cartographie les imports croisés réels — y compris la couche base de données dont des dizaines de modules dépendent en silence.

Angle mortBrunel

Symptôme

Du code porté à chaud casse le build sur des imports dynamiques jamais audités

Loi gravée

Un module qui charge ses dépendances par import dynamique cache son vrai arbre de liens à l'œil nu, mais pas au bundler qui les résout statiquement. Avant de porter un fichier d'un produit à un autre, on liste tous ses imports — dynamiques compris — et on retire les chemins propres à l'origine qui n'existent pas dans la cible.

Angle mortEames

Symptôme

Un composable de fetch paresseux fige un « Chargement… » éternel à l'écran

Loi gravée

Un fetch paresseux n'expose pas « la donnée n'a jamais été chargée » mais « aucune requête en cours » — deux états qu'on confond au péril d'un spinner infini. Si l'on diffère le déclenchement, on doit déclencher explicitement le fetch au montage ou repenser la condition d'affichage. L'état d'attente n'est pas l'état d'absence de donnée.

Angle mortEames

Symptôme

Des compteurs d'onglets affichent toujours zéro sur les vues qu'on n'a pas chargées

Loi gravée

On ne peut pas compter ce qu'on n'a pas ramené. Dériver des totaux par catégorie d'une liste déjà filtrée côté serveur condamne les onglets non chargés à afficher zéro. Pour des compteurs justes : soit un point d'accès dédié au comptage, soit récupérer l'ensemble puis filtrer côté client quand le volume le permet.

Angle mortLovelace

Symptôme

Une décision se fonde sur une métrique de substitution qui ne mesure pas ce qu'on croit

Loi gravée

Mesurer la taille des fichiers et la prendre pour le coût réellement consommé, c'est confondre le proxy et la grandeur. Une métrique de substitution doit être vérifiée contre ce qu'elle prétend approximer avant d'en tirer la moindre conclusion — sinon on optimise un chiffre qui ne décrit rien.

Angle mortBrunel

Symptôme

Un conteneur minimaliste refuse de charger une bibliothèque native, mais seulement à l'appel

Loi gravée

Les images conteneur minimalistes utilisent une libc différente de celle que ciblent la plupart des binaires natifs précompilés : le lien dynamique échoue. Et comme l'import est différé jusqu'au premier appel de la route, un health-check trivial reste vert et masque le crash. Vérifier la compatibilité libc avant de poser une dépendance native, et tester la vraie route, pas un ping superficiel.

Angle mortBrunel

Symptôme

Un déploiement « réussi » oublie les nouvelles dépendances et plante à la première vraie requête

Loi gravée

Un déploiement qui ne synchronise pas le manifeste de chaque sous-projet d'un monorepo laisse l'installation lire un ancien fichier, ignorer les dépendances déclarées et rapporter « à jour » à tort. Le manifeste de chaque workspace doit être synchronisé avant l'install, et la fumée post-déploiement doit frapper une route récemment ajoutée — jamais un health-check figé qui ne change jamais.

Angle mortTuring

Symptôme

Secret en clair dans une commande — fuite dans les logs

Loi gravée

Coller la valeur d'un secret directement dans une commande, pour contourner un environnement mal chargé, le grave dans les logs d'exécution, l'historique du shell et tout système de monitoring — autant d'endroits grep-ables où il survivra bien après usage. Un secret se lit toujours depuis une variable d'environnement ou un fichier de credentials hors-dépôt, jamais comme argument littéral. Le raccourci « rapide à taper » rend la rotation impossible et transforme chaque ligne de log en fuite.

Angle mortLovelace

Symptôme

Lock de session invalidé à chaque invocation — conflit PID cross-process

Loi gravée

Un lock basé sur le PID du processus courant est invalide dès l'invocation suivante, car chaque appel CLI spawn un nouveau processus avec un PID distinct. Le processus suivant interprète le lock existant comme appartenant à une session concurrente et se bloque. Tout mécanisme de lock persistant doit s'appuyer sur un identifiant stable (ID de session, token UUID, variable d'environnement transmise) et non sur le PID éphémère du processus appelant.

Angle mortBrunel

Symptôme

Lock PID : chaque invocation crée un process distinct et re-bloque

Loi gravée

Un lock qui stocke le PID du process ayant posé la claim devient inopérant dès que l'outil est réinvoqué en ligne de commande : le nouveau process voit le lock comme appartenant à une session tierce et re-bloque immédiatement. Le --force-claim ne sert à rien si l'invocation suivante repart d'un PID vierge. La bonne pratique est d'identifier la session par un identifiant stable et indépendant du cycle de vie du process (token de session, UUID persisté, variable d'environnement héritée).

Angle mortEames

Symptôme

Lock PID invalide entre invocations CLI distinctes

Loi gravée

Un mécanisme de lock qui stocke le PID du processus ayant acquis le verrou est structurellement fragile dès que l'outil est invoqué en ligne de commande : chaque appel génère un nouveau PID, rendant le lock invisible à l'invocation suivante. Résultat : un --force-claim suivi d'une lecture déclenche un nouveau blocage, forçant les agents à tâtonner en boucle. La règle est que le lock doit être identifié par une clé de session stable (ID utilisateur, token de session, nom de tâche) et non par un PID éphémère.

Angle mortTuring

Symptôme

Migration SQL refusée : colonnes renommées en DB mais pas dans le fichier

Loi gravée

Quand un schéma évolue (renommage de colonnes), les fichiers de migration non encore exécutés deviennent obsolètes et échouent au déploiement. Toute migration doit être validée contre le schéma réel avant d'être commitée. Un fichier de migration orphelin doit être supprimé ou mis à jour dès que le schéma dont il dépend change.

Angle mortTuring

Symptôme

TRUNCATE TABLE échoue si l'utilisateur DB manque du privilège DROP

Loi gravée

TRUNCATE TABLE requiert le privilège DROP sur PostgreSQL, contrairement à DELETE qui ne nécessite que le privilège DELETE. Un utilisateur de service avec droits restreints échouera silencieusement ou bruyamment au vidage de table si TRUNCATE est utilisé à sa place. Toujours préférer DELETE FROM dans les scripts automatisés tournant sous des comptes à privilèges limités.

Angle mortTuring

Symptôme

Fichier de configuration généré en chmod 0 par race condition worker

Loi gravée

Lorsque plusieurs workers régénèrent un fichier de configuration simultanément après une invalidation de cache, une race condition peut laisser le fichier en mode 0 (inaccessible) entre l'étape d'écriture et le chmod final. Le symptôme est une erreur 500 généralisée en production. Règle : toute régénération de fichier critique doit passer par un write atomique (fichier temporaire + rename) ou un verrou exclusif, jamais un open/write/chmod séquentiels sans protection.

Angle mortTuring

Symptôme

pm2 reload --update-env ne relit pas un .env absent au démarrage initial

Loi gravée

Un processus Node.js démarré sans variables d'environnement dans sa table PM2 produit un rendu SSR silencieusement vide et des erreurs 500 sur toutes les routes dépendant de la base de données — sans crash visible. `pm2 reload --update-env` ne peut propager que les variables déjà présentes dans la table PM2 au moment du premier `pm2 start` ; il ne relit pas un fichier `.env` absent ou nouvellement créé. La procédure correcte lorsqu'un `.env` est ajouté ou modifié est systématiquement `pm2 delete <nom>` suivi de `pm2 start <config>` pour forcer une lecture fraîche. Tout script de déploiement utilisant `pm2 reload` sans vérification préalable des variables critiques risque de shipper silencieusement un processus en état dégradé.

Angle mortTuring

Symptôme

Migration DB exécutée avant le deploy du code aligné : fenêtre de données vides en prod

Loi gravée

Appliquer une migration qui change une valeur de référence avant que le code consommateur ne soit déployé crée une fenêtre d'incohérence : les endpoints retournent zéro résultat car le fallback codé en dur cible l'ancienne valeur. La règle est stricte : migration et deploy du code doivent être atomiques, ou le code doit d'abord supporter les deux valeurs (double-read), avant que la migration ne supprime l'ancienne.

Angle mortTuring

Symptôme

Composant Vue silencieusement invisible après refacto : mauvais nom auto-importé Nuxt

Loi gravée

Nuxt génère les noms de composants auto-importés en concaténant le chemin complet du dossier et le nom du fichier. Si un fichier placé dans un sous-dossier ne commence pas par le nom de ce dossier, le nom exposé devient le préfixe full-path — ex. <DossierFichier> au lieu de <Fichier>. Tout appel à l'ancien nom court se résout silencieusement en une chaîne non-résolue, le composant ne s'affiche pas et aucune erreur n'apparaît en console. Règle : après tout déplacement ou renommage de composant, vérifier le nom généré dans .nuxt/types/components.d.ts avant de déployer.

Angle mortTuring

Symptôme

Filtre SQL sur commandes validées vide silencieusement les vues CRM

Loi gravée

Un filtre EXISTS sur les commandes passées dans une vue 'clients' exclut sans avertissement tous les contacts sans achat. La définition technique 'client = a commandé' diverge de la définition métier 'client = contact enregistré'. Toute vue CRM doit exposer le périmètre complet des contacts, avec un filtre optionnel sur le statut acheteur — jamais appliqué par défaut. Un widget vide n'est pas un état neutre : c'est un signal d'alerte à instrumenter.

Angle mortTuring

Symptôme

Lock de ressource invalidé à chaque nouvelle invocation de processus

Loi gravée

Un lock basé sur le PID du processus courant est structurellement cassé dès que l'outil est conçu pour être invoqué comme commande autonome : chaque appel crée un nouveau PID, rendant le lock précédent immédiatement illisible comme 'session étrangère'. L'option --force-claim ne corrige pas le symptôme si la revalidation du lock se produit au même tour d'invocation. La règle : un lock multi-invocation doit s'appuyer sur un identifiant stable (session ID applicatif, token UUID persisté, clé de corrélation) — jamais sur le PID système.

Angle mortTuring

Symptôme

Endpoint *-config retourne null silencieusement : module non installé mais table présente

Loi gravée

Lorsqu'un endpoint DB-first effectue des JOIN sur des tables créées par un module, l'absence du module en production n'empêche pas l'existence de la table maître — elle empêche l'alimentation des tables _lang. Le endpoint retourne alors null sans erreur visible, provoquant une page blanche silencieuse. Règle : avant tout déploiement touchant un endpoint qui joint des tables _lang, vérifier que tous les modules responsables de ces tables sont installés et actifs sur l'instance cible.

Angle mortTuring

Symptôme

TRUNCATE silencieux déclenché par une méthode de ré-indexation non auditée

Loi gravée

Toute méthode de ré-indexation peut embarquer des effets de bord destructeurs (TRUNCATE implicite) non documentés dans la signature. Avant d'appeler une telle méthode en production, lire le code source complet et prendre un dump SQL ciblé sur la ou les tables concernées — pas un dump global. Lors d'une restauration partielle depuis un backup, extraire les lignes INSERT via grep ciblé sur le nom de table exact, jamais via une plage séditive (sed /BEGIN/,/END/) qui risque de capturer des blocs adjacents et d'écraser des tables non ciblées.

Angle mortTuring

Symptôme

500 vide : display_errors=Off masque les fatals pré-framework

Loi gravée

PHP génère des 500 sans corps dès que display_errors=Off et que le fatal survient avant l'initialisation du framework — le mode debug applicatif n'entre jamais en jeu. En cas de 500 muet, la première action est de lire le log d'erreur du serveur web (Apache/nginx), pas de toucher à la config applicative. Deuxième piège indépendant : un formulaire dont l'action est une URL sans slash final reçoit un 301 redirect qui efface silencieusement les données POST — si un handler de soumission ne semble jamais déclenché, vérifier l'URL exacte avant tout.

Angle mortTuring

Symptôme

Suffixe `_wt` PrestaShop = TTC : inversion silencieuse HT/TTC

Loi gravée

Dans le schéma PrestaShop, les colonnes sans suffixe contiennent les montants hors taxes (HT) et les colonnes suffixées `_wt` (« with tax ») contiennent les montants toutes taxes comprises (TTC). Lire `total_products_wt` là où le libellé affiche « HT » produit un montant TTC présenté comme HT — erreur fiscale silencieuse indétectable sans test de comparaison des valeurs. Règle : avant tout mapping colonne→libellé fiscal, vérifier la convention `_wt` et l'aligner explicitement dans la couche d'accès aux données, les templates et les PDF.

Angle mortLovelace

Symptôme

mysql2 renvoie les agrégats SQL en string — conversion Number obligatoire

Loi gravée

Le driver mysql2 retourne les valeurs de fonctions d'agrégat (ROUND, AVG, COUNT, SUM) sous forme de string ou BigInt, pas de number JS. Toute opération arithmétique sans conversion explicite produit une concaténation silencieuse et un résultat absurde. Règle : toujours appliquer Number() ou parseFloat() sur tout champ agrégé avant calcul ou affichage.