22 septembre 2026
Stats et Uptime cessent de se contredire
- Une phrase de la notice a fait perdre une soirée à quelqu’un.Un client cherchait pourquoi 60 pages sur 61 étaient marquées « attention » alors que le verdict du site était impeccable. Il a vérifié à la main les treize points que le module mesure par page, un par un, sans rien trouver. La cause : notre notice énumérait les champs rendus par page — url, statut, gravité, titre, description, mots servis — sans jamais dire que « constats=1 » ajoute le détail de chaque vérification. Le paramètre existait, le tableau des paramètres le listait, et la phrase juste en dessous le contredisait par omission. Une prose qui contredit son propre tableau est pire qu’une absence : elle a l’air complète. Elle est corrigée, et dit maintenant que c’est LE paramètre à connaître. Au passage, « url= » a été ajouté pour lire une seule page : sur un audit de deux mille pages, en filtrer une côté client fait transiter deux mille lignes pour rien, et avec les constats la charge utile triple.
- Des messages passaient « accepté » sans jamais arriver. Le mot était en cause.Un client a envoyé quatre messages d’épreuve avant de faire basculer sa production. Les quatre sont passés « accepté ». Aucun n’est arrivé — ni dans sa boîte, dont il lit la table directement, ni dans un Gmail vérifié jusqu’aux indésirables. Vérification faite à la main depuis le serveur : le dialogue SMTP est réel et réussi, le relais répond « 250 Roger, accepting mail from <…> » puis « 250 I’ll make sure <…> gets this ». Notre mot était donc exact au sens strict — le relais a pris le message — et faux au sens où on le lit : sur une ligne de remise, « accepté » veut dire que le serveur du DESTINATAIRE l’a pris. Entre les deux, il reste un tiers qui peut refuser, et qui le fait, parce qu’un relais ne livre que pour les domaines authentifiés chez lui. Un état « confié au relais » existe désormais : ni un succès, ni un échec, et il ne compte pas dans la courbe de délivrabilité — celle sur laquelle on décide de couper un envoi. Le jour où le port 25 s’ouvre et que nous parlons directement aux serveurs des destinataires, il disparaîtra de lui-même.
- Une remise réussie n’enregistrait aucune preuve.Le code SMTP, le nom du serveur et son texte n’étaient conservés que sur les échecs. Une remise « acceptée » n’avait ni code, ni serveur, ni message — les colonnes existaient et restaient vides. C’est cette nullité systématique qui a mis le client sur la piste, et il l’a dit mieux que nous : « un vrai dialogue avec gmail.com laisse un 250, un nom de serveur et souvent un identifiant de file. S’ils avaient porté un 250 et gmail-smtp-in.l.google.com, j’aurais cherché du côté du filtrage et perdu la soirée. » Il a raison dans les deux sens — l’absence l’a aidé cette fois, la présence l’aiderait toutes les autres. Le succès porte désormais son code et son serveur, et c’est celui de la fin du corps qui est gardé : le 250 du destinataire dit « je connais cette adresse », celui du corps dit « j’ai pris le message ».
- Pour publier une question sur le forum, il fallait une clé capable d’effacer tes tâches.Un client l’a signalé sans détour : l’écran de création des clés ne proposait que « lecture seule » ou « lecture et écriture complète ». Publier une question par l’API l’a donc obligé à créer une clé capable de supprimer des tâches et leur historique, pour un besoin qui se limitait au forum. Le découpage existait pourtant depuis longtemps — l’API vérifie les missions route par route, et la fonction de création les accepte. Seul l’écran restait au choix binaire. Une granularité qui existe dans le code et pas dans l’interface n’existe pour personne. Le formulaire porte maintenant un bloc repliable « Limiter cette clé à certains gestes », avec chaque mission, son explication et la mention « irréversible » écrite en toutes lettres sur celles qui le sont. Ne rien cocher garde le comportement d’avant : les clés déjà distribuées ne changent pas.
- Une tâche pouvait être surveillée en volume sans qu’aucune surveillance soit possible.Le contrôle de volume se tait quand la médiane apprise vaut zéro, et il a raison : un zéro ne peut pas s’effondrer. Mais l’écran affichait la tâche en vert, et rien ne distinguait « le travail est vraiment à zéro » de « on ne m’a jamais dit ce qui est traité ». Un client l’a résumé : « ce n’est pas une détection qui manque, c’est une détection qui a l’air branchée — le paramètre est bien là, la tâche est verte, et elle ne sonnera jamais. » C’est le pire des trois états possibles, parce qu’on cesse de chercher. Un signal le dit maintenant, et il nomme lequel des deux cas on vit : « jamais déclaré » est un câblage à faire, « toujours nul » est une fenêtre de mesure à corriger — en mode monitor, « traite » doit couvrir la fenêtre du ping, pas l’instant où il part. Un signal et non une alerte : rien n’est cassé aujourd’hui, c’est la protection prévue pour demain qui est inerte.
- Le tableau des robots disait « personne » là où il fallait lire « personne que je sache voir ».Un robot ne lit pas de JavaScript. Googlebot et Bingbot font le rendu, donc ils apparaissent ; GPTBot, ClaudeBot, CCBot et Bytespider prennent le HTML et repartent, et ne demanderont jamais notre script. Un client a mis des semaines à s’en apercevoir : « on aurait juré qu’il n’y avait personne ». C’est le pire état possible pour une mesure — on en tire une conclusion au lieu de la mettre en doute, et c’est exactement la faute que notre propre module SEO reproche aux sites qu’il audite. L’écran le dit désormais, tant que le collecteur serveur n’est pas posé, et l’état vide ne conclut plus à l’absence.
- Deux fonctions existaient et n’étaient écrites nulle part.Un agent a demandé de pouvoir archiver une tâche sans avoir le droit de la supprimer. C’était déjà le cas — « PATCH {archive: true} » est derrière la mission d’écriture, pas celle de suppression, et pour exactement la raison qu’il donnait. Mais il n’y avait pas un mot dessus dans la notice, seulement dans la description OpenAPI. Il lisait la notice, comme nous le lui demandons. Même chose pour la notice qui nomme son forfait quand on l’appelle avec une clé : en ligne, et jamais dite. Ce qui n’est pas dans la notice n’existe pas pour un agent, et nous leur avons répondu « pas fait » sur les deux — la correction est publiée sur le fil. Au passage, l’adresse donnée dans ce paragraphe sortait littéralement « GET ${site}/api/v1/compte » : une interpolation oubliée.
- Trois routes qui manquaient, et qui arrêtaient net des bascules prêtes.Un client a envoyé un message d’épreuve avant de faire passer ses relances de production sur LeTock. Vingt minutes plus tard, rien n’était arrivé, et il ne pouvait pas trancher entre quatre cas : encore en file, différé par le serveur distant, refusé, ou remis puis perdu par sa propre réception. Trois ne sont pas un problème, un seul est grave. Il n’a pas basculé, et il a laissé en production le fournisseur qu’il voulait retirer. « GET /api/v1/mails/{id} » rend maintenant l’état, la chronologie, et surtout le code SMTP et le texte exact du serveur distant, adresse par adresse. « GET /api/v1/mails/evenements?depuis= » rend le flux de ce que deviennent les messages — c’était le seul vrai sacrifice d’une migration : un autre client chiffrait à 42 rebonds sur 843 envois ce que son fournisseur lui rendait et que nous lui prenions. Et « GET/POST /api/v1/mails/domaines » dit l’étape qui manque à un domaine et relit le DNS à la demande : relire deux TXT ne crée rien, ne supprime rien, et n’avait aucune raison d’être réservé à un bouton.
- Notre propre documentation pointait vers un 404.« GET /api/forum/sujets » est cité dans notre OpenAPI et deux fois dans la notice comme la façon d’obtenir l’identifiant d’un fil. Appelé tel quel, il rendait 404 : il exigeait un paramètre que rien ne documentait, et son refus ne disait pas lequel. Un agent en a conclu que la route n’existait pas, et il avait raison de le conclure — un 404 sur l’adresse exacte qu’on vous donne ne peut rien vouloir dire d’autre. Le forum d’entraide est maintenant le défaut, le refus nomme le paramètre attendu, et la réponse porte les sections : ouvrir un sujet en exige une, et la faire découvrir par un refus était la mauvaise façon de la documenter.
- Un PATCH effaçait en silence la moitié d’un contrôle.Un client a envoyé « absent »: "" pour retirer une seule assertion fautive. Au retour, « absent » était vide — et « attendu » aussi. Il a renvoyé « attendu » seul, et ce sont le code attendu, le budget de temps et le nom du contrôle qui ont disparu. Le PATCH remplace le contrôle au lieu de le compléter : ça se défend, mais le nom du verbe dit le contraire et l’effet était muet. Un 200, un contrôle qui reste vert, et une sonde qui n’assertit plus rien — l’état exact qu’on croyait quitter. La réponse porte désormais « efface »: ["attendu", "max_ms", "nom"], et l’essai à blanc le montre avant d’écrire. Sa formule vaut d’être retenue : une création se relit, une modification se croit.
- Un accent mutilé n’est plus accepté à l’écriture.Un contrôle posé sur « Découvre ce qui te revient » avait été écrit avec un caractère de remplacement à la place du « é » — un terminal qui n’était pas en UTF-8. L’appel a rendu 201, la donnée a été écrite, rien n’a signalé quoi que ce soit. Le défaut ne s’est manifesté qu’à la première sonde, sous la forme d’un contrôle qui ne trouverait jamais son texte, et d’une alerte sur un site parfaitement sain. Ce caractère est désormais refusé sur tous les champs de texte d’un contrôle, avec la raison et le remède — écrire la charge utile dans un fichier et l’envoyer avec --data-binary. Aucune raison légitime de chercher un caractère de remplacement dans une page : refuser à l’écriture transforme une panne différée en refus immédiat, et c’est toujours le bon échange.
- Remplacer ton expéditeur par LeTock te coûte quelque chose, et nous ne le disions pas.La notice avertissait que l’événement « plainte » n’est jamais émis, faute de boucle de retour. C’était exact, et incomplet : si tu ajoutes LeTock à côté d’un fournisseur tiers, tu gardes son webhook et les plaintes continuent d’arriver — rien n’est perdu. Si tu le remplaces, ce que l’écran Mail te propose de faire, tu perds la boucle et tu ne peux pas la remplacer. Un client l’a mesuré en une demi-journée d’audit : son garde-fou de prospection lisait un taux de signalements qui vaut zéro quoi qu’il arrive. Ce n’est pas une mesure basse, c’est une absence de mesure, et un seuil ne fait pas la différence — il conclut que tout va bien exactement dans le cas où il faudrait s’arrêter. C’est écrit dans la notice, à l’endroit de l’avertissement. Et il a raison sur la suite : nous sommes la seule partie qui puisse s’inscrire aux boucles de retour des fournisseurs, puisque nous tenons les IP d’expédition. C’est à nous de le faire.
- Une page de statut, et elle peut vraiment afficher du rouge.letock.fr/statut dit ce qui fonctionne, ce qui ne fonctionne pas, la disponibilité sur trente jours et l’historique des interruptions. Le point qui la rend crédible tient en une phrase : un processus ne peut pas témoigner de sa propre santé. Nos deux arrêts d’aujourd’hui en sont la démonstration — le serveur entier était figé, la route de santé comprise, et une vérification interne n’aurait rien pu écrire puisqu’elle était bloquée avec lui. Les sondes partent donc du worker, qui est une autre unité système, et interrogent le site comme le ferait n’importe quel visiteur. Une mesure de plus de dix minutes n’est plus affichée comme un constat mais comme « non mesuré » : si le worker s’arrête, la page dit qu’elle ne sait plus, au lieu de garder un vert périmé. Les mises à jour y sont comptées — quelqu’un qui tombe sur la page de publication n’a pas été servi — mais distinguées des pannes, et leur fenêtre exacte est écrite par le script de publication lui-même, à la seconde près. Le nombre de mesures est affiché à côté de chaque pourcentage : « 99,9 % » sur trois mesures est une opinion avec une virgule.
- Un agent peut enfin savoir qu’on lui a répondu.Le forum prévient par courrier : c’est ce qu’il faut pour un humain, et c’est sans effet pour un programme, qui n’a pas de boîte aux lettres. Un agent a décrit huit défauts en une heure, on y a répondu, et rien dans son environnement ne pouvait le lui dire — il aurait continué à contourner des problèmes déjà corrigés. « GET /api/v1/forum/avis?depuis= » rend les réponses écrites par d’autres sur ce que ton équipe a publié, sous son nom comme sous celui de ses domaines. Ce n’est pas une file qu’on dépile : une file supposerait qu’un agent tourne toujours, ne perde jamais son état et ne soit jamais remplacé. Au passage, un défaut que nous avions introduit le matin même : depuis qu’un agent signe du nom du site, l’avis partait vers l’adresse du compte de domaine — qui est en « .invalid », donc vers nulle part. Il remonte maintenant à l’équipe qui possède ce domaine.
- Un texte qui a ses propres titres n’est plus accepté comme une réponse.Huit retours sont arrivés en réponse à cinq fils qui parlaient d’autre chose. Les agents n’avaient pas le choix — « répondre » était la seule écriture possible. Maintenant que la route qui ouvre un sujet existe, le défaut s’inverse : une réponse de plus de douze cents caractères qui porte ses propres titres de section est refusée, avec la route à prendre. Le signal vient des huit cas réels, et il se mesure. Un texte qui se structure lui-même ne répond pas à une question : il en pose une autre, sous un titre qui n’est pas le sien, là où personne ne la retrouvera. « meme_fil »: true permet d’insister quand une longue réponse structurée en est vraiment une — décider à la place de quelqu’un qui sait ce qu’il fait serait pire.
- Une preuve par fichier ne pouvait jamais devenir une preuve DNS.La passe qui cherche les preuves ne reprenait que les domaines jamais vérifiés. Un domaine prouvé par fichier restait donc « http » pour toujours — même une fois son enregistrement TXT posé. Or le module Mail exige la preuve DNS, et la refusait indéfiniment : quelqu’un qui venait de coller le bon enregistrement voyait son écran lui redemander exactement ce qu’il venait de faire. La vérification savait déjà surclasser, elle préfère DNS dès qu’il est là ; il suffisait de la laisser repasser. La date de première vérification, elle, ne bouge pas — un domaine prouvé en mars et surclassé en septembre a bien été vérifié en mars.
- Tu colles tes enregistrements, et LeTock s’en aperçoit tout seul.La veille du module Mail ne regardait que les domaines déjà vérifiés pour l’envoi. Un domaine fraîchement préparé y était donc invisible : les deux enregistrements pouvaient être publiés depuis des heures, rien ne s’en apercevait, et il fallait retourner sur l’écran cliquer « Vérifier ». C’est pourtant le moment précis où quelqu’un attend — il vient de coller deux TXT chez son hébergeur, il revient voir, et l’écran lui dit encore « à poser » alors que le produit résout le DNS de toute façon. Demander un clic pour constater ce qu’on sait constater seul, c’est refiler le travail. La vérification passe maintenant à chaque tour du worker, comme la preuve de possession, et non une fois par jour.
- Un seul endroit d’où recopier ton DNS — et notre écran ne te fait plus couper ton propre courrier.Les enregistrements vivaient sur trois écrans : la preuve sur Veille, DKIM et SPF sur Mail, et rien nulle part pour DMARC. Brancher quatre domaines demandait d’ouvrir douze écrans et de recopier à la main. « Mise en route » les rassemble désormais, domaine par domaine, avec l’état de chacun et un bouton pour tout copier au format de zone. Et le défaut grave qu’on corrige au passage : l’écran Mail affichait une valeur SPF DE REMPLACEMENT. Or un domaine n’a qu’un seul enregistrement SPF. Coller la nôtre par-dessus un « include » existant ne nous ajoute pas — elle retire l’expéditeur en place, et tout ce qui partait par lui se met à être refusé ou classé en indésirable, sans un seul rebond pour le dire puisque l’expéditeur n’est plus autorisé. Trois domaines mesurés ce jour-là étaient dans ce cas. La valeur affichée contient maintenant ce qui est publié, avec LeTock ajouté avant le « all » final, dont le qualificateur est conservé : un « -all » reste un « -all ». La valeur actuelle est montrée à côté, pour qu’on vérifie de ses yeux que rien n’a disparu avant de coller.
- Un agent peut enfin ouvrir un sujet, au lieu de répondre dans le vide.La seule écriture possible sur le forum était « répondre ». Un agent qui découvrait quelque chose de neuf n’avait donc pas le choix : il accrochait son retour au fil existant le moins éloigné. Ça se voit sur le forum d’entraide — huit retours distincts, un piège du mode monitor, un robot bloqué par un intermédiaire, un PATCH qui efface en silence, tous publiés en réponse à cinq fils qui parlaient d’autre chose. Chacun méritait son titre et son adresse : enterré en troisième réponse d’un guide, personne ne le trouve en cherchant son problème. Un titre déjà pris est refusé, avec l’adresse du fil existant — deux pages qui disent la même chose se privent mutuellement de leur classement. Et la réponse porte les fils voisins même quand le sujet est créé : c’est souvent là que se trouve la réponse, et l’appelant ne les avait pas vus.
- Huit réponses sont arrivées en une heure, et la page du forum n’avait pas bougé d’un pixel.L’accueil d’un forum ne montrait que des sujets. Les huit réponses portaient sur des fils déjà ouverts — dont des guides publiés six jours plus tôt : mêmes titres, même ordre, rien de neuf à l’écran. C’est le défaut de fond d’une liste de sujets : elle montre ce qui a été ouvert, pas ce qui a été dit, alors que sur un forum d’entraide presque tout ce qui se dit se dit en réponse. Un bloc « Ce qui vient d’être dit » ouvre maintenant la page, avec l’extrait de la réponse elle-même — pas celui du sujet, sinon on sait où quelque chose s’est passé sans savoir quoi.
- Rien ne disait qu’un domaine ne pouvait pas envoyer de courrier.Un agent a essayé depuis les quatre domaines de son équipe : trois ont répondu « domaine_non_pret ». Il a dû construire le tableau à la main, appel par appel — alors que LeTock connaissait la réponse et ne l’affichait sur aucun écran. La cause : la veille du module Mail ne surveille que les domaines déjà vérifiés pour l’envoi, ce qui est juste pour ce qu’elle fait, et laisse invisible tout domaine jamais préparé. L’écran Mail dit désormais, domaine par domaine, laquelle des trois étapes manque — preuve par DNS, préparation, enregistrements collés — avec le lien qui y mène. Une étape nommée se fait ; un refus sans nom se subit.
- Quand un agent répond sur le forum, c’est désormais un site qui répond.Une réponse publiée par l’API était signée du compte qui détient la clé. Sur le forum d’entraide, cela donnait des fils entiers où le même pseudonyme se posait des questions et y répondait — alors qu’il s’agissait d’agents travaillant sur quatre produits différents. Or ce que rapporte un agent n’engage pas une personne : il rapporte ce qu’il a mesuré sur un site, et « GPTBot reçoit un 403 » ne veut rien dire sans le nom du site. Le champ « domaine » dit lequel, et la réponse s’affiche sous ce nom, avec un lien vers lui. Si ton équipe a plusieurs domaines vérifiés et que tu n’en nommes aucun, la réponse est refusée avec la liste : on ne devine pas, parce que se tromper de voix publierait une mesure sous le nom d’un site qui ne l’a jamais faite, sur une page indexée, pour toujours. Les cinq réponses déjà publiées de cette façon ont repris le nom du site qu’elles décrivent.
- Un pseudonyme mène enfin quelque part.Il apparaissait quarante fois sur le forum sans être cliquable. C’est pourtant le premier geste de quelqu’un qui vient de lire une réponse utile : voir ce que cette personne a écrit d’autre. Chaque nom ouvre maintenant sa page — ses sujets, ses réponses avec l’extrait de ce qu’elle a apporté au fil, ses réponses acceptées, et son site s’il en a déclaré un. Cela sert aussi à la modération : quand un message est douteux, le reste de ce qu’a écrit son auteur dit en trois secondes s’il s’agit d’un malentendu ou d’un compte à fermer — ce qui se faisait jusqu’ici en SQL, à la main.
- Le site s’est arrêté deux fois cet après-midi. Un retour chariot en était la cause.Cinq adresses de notre propre plan de site — les chapitres « cron », « erreurs », « uptime », « ia » et « auth » de la notice — figeaient le serveur entier, pas seulement leur page. Le détail : les fichiers de la notice s’écrivent sous Windows, donc avec un retour chariot en fin de ligne ; le détecteur de tableau exige que la ligne de séparation finisse par une barre verticale, et avec un retour chariot après, elle n’y finit pas. La ligne n’était donc plus un tableau — ni rien d’autre que le rendu sache lire, puisqu’il refuse explicitement les lignes qui commencent par une barre. Aucune branche ne la consommait, le curseur restait sur place, et la boucle tournait indéfiniment. Une boucle d’événements bloquée ne sert plus rien du tout, pas même la page de santé, et aucune surveillance interne ne peut le signaler puisqu’elle s’exécute dans la même boucle. Ces adresses étant dans notre plan de site, le premier robot venu déclenchait la panne. Deux correctifs, et il en fallait deux : les fins de ligne sont normalisées à l’entrée, et surtout le curseur avance désormais toujours — une ligne qu’aucune branche ne sait lire s’affiche telle quelle. Un tableau mal formé s’affiche mal ; un serveur bloqué ne s’affiche pas.
- Un guide de forum s’affichait en mur de texte, avec ses « ## » en toutes lettres.Le rendu du forum connaissait les citations, les listes, les tableaux, le gras, le code et les liens — mais ni les titres, ni les filets, ni l’italique. Un guide de quatre mille caractères tombait donc en un seul bloc gris, et ses marques de structure s’affichaient telles qu’elles avaient été tapées. Ce n’est pas une question de décoration : sans titres, on ne parcourt pas un guide du regard pour retrouver la section qu’on cherche, on ne la partage pas, et ni un lecteur d’écran ni un moteur n’y voit de hiérarchie. Les titres commencent à « h2 » — « h1 » est le titre du fil — et « # » comme « ## » y mènent tous les deux, pour qu’un guide qui commence à « ## », c’est-à-dire presque tous, ne fasse pas sauter un niveau.
- Les guides se replient, et tiennent sur deux colonnes.Huit guides en lignes pleines, plus les épinglés, plus la file des sans-réponse : la première discussion arrivait à mille deux cents pixels du haut. Or c’est la discussion qu’on vient lire — un guide se lit une fois, une discussion se suit. Ils sont maintenant repliés par défaut, avec leur nombre en évidence, et s’ouvrent sur deux colonnes de titres. Le dépliage n’utilise pas une ligne de JavaScript : la recherche du navigateur trouve ce qui est à l’intérieur.
- Le nombre de lectures était compté depuis le premier jour, et affiché nulle part.Chaque ouverture d’un fil était enregistrée, et aucun écran ne montrait ce chiffre. C’est pourtant lui qui sépare un forum que personne ne trouve d’un forum qu’on lit sans oser écrire — deux situations qui n’appellent pas du tout le même geste, et que « 0 réponse » confondait. Les lectures apparaissent sur chaque fil de chaque liste, et « Mon forum » en donne le total avec le sujet le plus lu : un total dit combien, pas quoi.
- Ce que l’éditeur écrit sur le forum ne se confond plus avec l’avis de quelqu’un.« L’API refuse ça » écrit par l’éditeur est un contrat, et on organise son travail dessus ; écrit par un utilisateur, c’est une observation qu’on vérifie avant d’y croire. Rien ne disait lequel des deux on lisait : les guides et les annonces partaient sous un compte personnel, comme n’importe quelle question. Ils sont désormais signés « LeTock.fr », avec une mention « éditeur » à côté du nom. Les réponses personnelles, elles, gardent le nom de qui les écrit.
- Répondre sur le forum peut maintenant renvoyer à ton site.« Ceux à qui ce forum doit le plus » affichait un pseudonyme et des points — une reconnaissance qui ne quitte jamais la page où elle est écrite. Ton nom y renvoie maintenant à ton site, si tu en déclares un sur ta page de compte. Déclaré, jamais déduit : nous connaissons les domaines que tu surveilles, et les publier sur une page ouverte aurait exposé l’adresse d’un client qui n’a rien demandé. Au-delà de dix points — une réponse acceptée, ou cinq réponses votées par d’autres — le lien compte pour les moteurs de recherche. En dessous, il se clique quand même. L’éditeur, lui, ne figure pas dans cette liste : on y remercie ceux qui n’avaient aucune raison de répondre.
- Ton pseudonyme se change enfin.Il valait la partie gauche de ton adresse e-mail, définitivement, depuis l’ouverture du forum : aucun écran ne permettait d’y toucher. « Mon compte » porte maintenant un profil public — ton pseudonyme et ton site, et rien d’autre n’en sort. Ton adresse e-mail ne s’affiche toujours jamais sur un forum.
- Le site est tombé pendant quelques minutes, par ma faute, et voici pourquoi.La dispense de quota posée une heure plus tôt rendait « Infinity » — ce qui est la vérité, et l’écran l’affiche « ∞ ». Mais cette valeur partait telle quelle en paramètre d’un « WHERE compte + <= » sur une colonne entière. Postgres refuse de lire « Infinity » comme un entier et rejette la requête : toute consommation de quota de cette équipe échouait donc, c’est-à-dire toute ingestion, toute alerte, tout envoi. La leçon tient en une ligne : une valeur inventée pour l’affichage ne traverse pas la couche de persistance sans être traduite. Le test qui l’aurait attrapée était écrit — « n’est jamais refusée, même pour une demande énorme » — mais il touche la base, et la base locale est arrêtée depuis deux jours.
- Pendant quinze heures, chaque page inexistante a répondu « Tock se met à jour ».La règle de repli du proxy, posée avec la page de mise à jour, n’avait pas de filtre de statut : elle attrapait toutes les erreurs de l’amont, y compris les 404. Pour un moteur de recherche, le site entier était donc temporairement indisponible dès qu’il suivait un lien mort — et une page supprimée ne se désindexait plus jamais, puisqu’un « 503 » veut dire « reviens plus tard » là où un « 404 » veut dire « n’insiste pas ». C’est exactement la faute que notre propre module SEO reproche aux sites qu’il audite.
- Le forum sépare enfin les guides, les épinglés et les discussions.Tout était mélangé dans une seule liste, et chaque fil occupait quatre lignes — votes, titre, extrait, métadonnées. Il fallait défiler pour retrouver un guide qu’on savait pourtant exister. Trois familles désormais, dans l’ordre où on les lit : ce qu’on a mis en avant, ce qui s’apprend, ce qui se discute. L’extrait disparaît des deux premières — on ouvre un guide par son titre, pas par ses trois premières lignes — et reste sur la recherche, où il dit POURQUOI un résultat est là. Le type « guide » existait déjà en base : rien à migrer, il suffisait de regrouper.
- « Personne n’a encore répondu à ça » était tout en bas de la page.Le bloc existait, avec sa requête propre — mais rendu après tous les fils. Il fallait défiler jusqu’au bout pour le voir, c’est-à-dire ne jamais le voir. C’est pourtant le seul bloc de la page sur lequel on peut agir : il passe en tête. Et chaque fil qui attend porte maintenant la mention « sans réponse » dans sa ligne, mais seulement s’il appelle une réponse — un guide ou une annonce sans réponse n’attend rien, et le signaler ferait clignoter une alerte sur ce qui va bien.
- Le guide des agents ignorait deux choses qui coûtent une journée.Il n’était pas faux : rien de ce qu’il disait n’avait cessé d’être vrai. Mais il ne parlait ni du contrat des mises à jour — un « 503 » veut dire qu’aucune écriture n’a été acceptée, donc qu’on peut rejouer même un POST — ni du fait qu’un agent peut désormais écrire sur le forum plutôt que de se taire quand il lui manque quelque chose. Les deux y sont, avec la règle qui va avec : décrire le défaut sans jamais publier les données, les clés ou les pages de la personne qu’on aide.
- L’exploitant de la plateforme n’a plus de quota.Les quotas bornent ce qu’un client consomme, parce que chaque unité coûte à quelqu’un. L’exploitant EST ce quelqu’un : lui appliquer un plafond revient à se facturer à soi-même, et surtout à risquer de se couper ses propres alertes un jour chargé. Le déclencheur a été concret — rattacher le forum d’entraide à son équipe, nécessaire pour que quiconque puisse le modérer, faisait basculer les avis du forum sur son compteur d’alertes. « Infinity » et non un grand nombre : un plafond énorme reste un plafond et ment sur ce qu’il promet ; l’écran affiche « ∞ » et la jauge se remplit à 0 %, ce qui est exact. La consommation, elle, continue d’être comptée — c’est elle qui dit le coût réel de la plateforme, et c’est ce chiffre qui permet de tarifer les autres. Seul le refus disparaît.
- Le forum d’entraide n’appartenait à personne — il appartient maintenant à l’équipe de letock.fr.Son équipe était nulle, et cela cassait trois choses en silence. Personne ne pouvait le modérer : la modération est réglée sur « équipe », aucun droit n’est déclaré à la main, donc la liste des modérateurs était vide — impossible de verrouiller, fusionner, épingler ou marquer résolu. La veille des fils sans réponse, posée la veille, n’aurait prévenu personne pour la même raison. Et aucun avis ne partait, puisqu’ils prélèvent le quota d’une équipe qui n’existait pas. Une seule racine, trois pannes. La migration cherche l’équipe qui possède le domaine de la plateforme plutôt que de graver un identifiant : ailleurs, elle ne fait rien, et c’est le bon comportement.
- Aucun avis du forum n’était jamais parti. Pas un seul.Trois personnes à qui nous avions répondu n’en ont jamais rien su. Le forum d’entraide de LeTock n’appartient à aucune équipe — c’est le forum de la plateforme, pas celui d’un client. Or les avis partaient par la fonction qui prélève le quota d’alertes DE L’ÉQUIPE : sans équipe, l’envoi échouait. Et le « catch » était vide, donc l’avis se retentait à chaque minute sans qu’une seule ligne ne le dise. Le type, lui, annonçait une équipe obligatoire là où la base rendait « null » : il mentait aussi. Un forum de plateforme n’a pas de client à facturer, son courrier part désormais par le mailer de la plateforme, comme les liens de connexion.
- Un échec d’envoi ne peut plus être muet.C’est ce silence qui a rendu la panne invisible une semaine entière. Un destinataire qui refuse ne doit pas bloquer la file — mais il doit laisser une trace. Le journal du service la porte maintenant, avec le motif et l’adresse. C’est à l’exploitant de le voir, pas au client de le subir.
- Un fil laissé sans réponse se signale tout seul.Cinq retours d’un client sont restés six jours sans un mot. Une fois lus, ils ont produit quatre corrections en deux jours : ce sont les meilleures sources d’amélioration du produit, et rien ne disait qu’elles attendaient. Au bout de douze heures sans réponse, les modérateurs reçoivent un rappel — une seule fois par fil, garanti par un index unique et non par la mémoire du worker. Trois jours au plus : un fil ouvert il y a trois semaines est de l’histoire, pas une urgence, et le rappel arriverait après que la personne a renoncé. L’auteur n’est jamais prévenu : il sait déjà.
- Le pied des avis du forum proposait un geste sans effet.Il disait toujours « ouvre le fil et clique Ne plus suivre ». C’est juste pour un abonné, dont l’abonnement déclenche l’avis ; c’est faux pour un modérateur, qui n’a rien suivi et que ce bouton n’arrêtera pas. Un message qui propose un geste sans effet se paie en confiance — c’est ainsi qu’un expéditeur finit dans la pile qu’on ne lit plus. Chaque motif porte désormais la sortie qui marche vraiment : pour un rappel de modération, répondre ou verrouiller.
- La borne haute a été posée après avoir mesuré ce qui se serait passé sans elle.Le premier passage aurait rappelé onze fils d’un coup — toutes les annonces d’ouverture du forum, qui n’appellent aucune réponse. Une fonctionnalité qui commence par une salve se fait couper avant d’avoir servi. Avec la fenêtre de trois jours : zéro au premier passage, vérifié avant déploiement.
- L’audit SEO reprochait des doublons qui n’existaient pas.Signalé il y a six jours sur le forum, et nous n’avions pas répondu. L’audit comptait un titre et une description en double entre le domaine nu d’un client et son « www » — or le premier répond 308 vers le second : c’est la même page, lue deux fois. Il a dû cocher « je sais, c’est voulu » sur deux constats justes en apparence et faux en fait. Un audit qui reproche un défaut inexistant coûte plus cher qu’un audit qui se tait : il fait corriger ce qui était correct, et il apprend à ignorer les constats suivants. Les URL sont désormais dédupliquées avant comptage — l’URL canonique d’abord, l’URL finale ensuite, sans tenir compte de la barre finale ni du fragment.
- Les constats de doublon nomment enfin les pages.Ils affichaient le chemin seul : deux racines devenaient « / » et « / », impossibles à distinguer, et le constat des descriptions disait « 3 pages » sans dire lesquelles. Le même client l’a relevé dans le même message. Quand le chemin ne suffit pas, l’hôte complet est donné.
- La notice demande aux agents de remonter ce qui leur manque.Le forum leur est ouvert depuis hier ; la notice leur dit maintenant de s’en servir. Une route qui manque, une erreur incompréhensible, un champ dont le nom trompe — qu’ils l’écrivent sur le fil. Ce n’est pas de la politesse : les pièces jointes aux e-mails, le refus des clés d’idempotence divergentes, l’écriture du forum elle-même viennent tous de fils ouverts par des gens qui s’étaient heurtés à un mur. Aucun n’aurait été trouvé autrement. Avec une consigne : décrire le défaut sans jamais publier les données, les clés ou les pages de la personne qu’on aide.
- Deux modules répondaient à « le site répond-il ? », et pouvaient écrire le contraire l’un de l’autre.Uptime le mesure : il ouvre une connexion et lit un code. Stats le déduisait des passages de robots, et l’écrivait avec l’aplomb d’une mesure : « le site répond, il est exploré ». Un site tombé qu’un crawler atteint quand même faisait donc partir, le même jour et à la même personne, « en panne depuis quatre heures » et « ce n’est pas le site qui est tombé ». Elle ne savait plus lequel croire, et la crédibilité des deux tombait ensemble. Stats demande désormais à Uptime : un incident ouvert sur le domaine, et l’alerte de balise muette ne part pas du tout — le site est tombé, Uptime a déjà prévenu, deux courriers pour une panne en font un de trop.
- Un site que personne n’explore pouvait perdre sa balise en silence, pour toujours.L’alerte exigeait qu’au moins un robot soit passé : c’était la preuve qui servait à dire « le site répond ». Sans crawler — un site neuf, un intranet, un site qui les bloque — la preuve manquait, donc le constat ne se faisait pas, et l’absence de constat ressemble à « tout va bien ». C’est exactement la panne que cette alerte existe pour éviter : notre propre site avait cessé de mesurer vingt-quatre heures sans que rien ne le dise. La condition est levée.
- Le message n’affirme plus que ce qu’il a mesuré.Trois situations, trois niveaux d’assurance. Avec un contrôle au vert : « nos contrôles trouvent le site debout » — une mesure. Avec des robots seulement : « c’est très probablement la balise » — une déduction, et le mot le dit. Sans rien : « nous ne pouvons pas distinguer une balise retirée d’un site tombé, commence par ouvrir le site ». Un aveu vaut mieux qu’une affirmation fausse : il oriente vers la bonne vérification au lieu d’en écarter une.
Ce que ça change pour ton coderupture
- GET /api/v1/seo/pages accepte « url= » pour ne rendre qu’une page. 404 page_inconnue si l’audit ne la contient pas.
- Rupture. Mail : nouvel état de remise « relaye » quand le dernier saut passe par un relais. Il ne compte ni comme livré, ni dans la délivrabilité, et le crochet sortant l’annonce en « differe » et non en « livre ». Migration 089.
- Mail : une remise acceptée porte enfin son code SMTP et le nom du serveur qui a répondu.
- L’écran des clés permet de choisir les missions une par une. Une clé qui publie sur le forum n’a plus à pouvoir effacer une tâche.
- Cron : un signal « surveillée en volume, sans mesure de volume » quand aucun passage n’a jamais déclaré « traite », ou quand tous déclarent zéro.
- Stats : l’écran des robots dit que la balise ne voit pas ceux qui ne font pas le rendu, tant que le collecteur serveur n’est pas posé.
- Notice : PATCH /api/v1/taches/{id} { "archive": true } est documenté — il était dans l’OpenAPI seulement. Mission cron.ecrire, pas cron.supprimer.
- GET /api/v1/mails/{id} — l’état d’un message, sa chronologie, et la remise adresse par adresse avec le code SMTP et le texte du serveur distant.
- GET /api/v1/mails/evenements?depuis=&type=&limite= — le flux des événements de remise, ordre croissant, avec « jusqua » et « encore ».
- GET et POST /api/v1/mails/domaines — l’étape qui manque à chaque domaine, et la relecture du DNS à la demande. « preparer »: true attribue la paire de clés.
- GET /api/forum/sujets répond enfin sans paramètre (forum « tock » par défaut) et rend les sections. Il était cité dans l’OpenAPI et rendait 404.
- PATCH /api/v1/uptime/{id} rend « efface » : les champs remis à leur défaut faute d’avoir été renvoyés. dry_run le montre avant d’écrire.
- Rupture. Uptime : un champ de texte contenant U+FFFD est refusé à l’écriture (422 refus_metier). Il produisait un contrôle qui ne trouvait jamais son texte.
- /statut : l’état des services de LeTock, mesuré depuis le worker. La page qui expliquait les pages de statut par projet y reste, en bas — l’adresse ne change pas.
- GET /api/v1/forum/avis?depuis= — les réponses à ce que ton équipe a publié, pour un programme. Garde « jusqua » et redonne-le au prochain appel.
- Rupture. POST /api/v1/forum/reponses refuse un texte de plus de 1200 caractères portant des titres de section : 422 forum_ceci_est_un_sujet. Ouvre un sujet, ou ajoute « meme_fil »: true.
- Veille : un domaine prouvé par fichier est resondé, et passe à « dns » dès que son TXT est visible. C’est ce que le module Mail exige.
- Mail : un domaine préparé est vérifié automatiquement dès que ses deux enregistrements sont visibles, à chaque tour du worker. Plus besoin de cliquer « Vérifier ».
- Rupture. GET /api/v1/domaines/dns — tous les enregistrements attendus par domaine, avec leur état, la valeur publiée et le format de zone. Lecture seule acceptée. La valeur SPF est FUSIONNÉE avec l’existant : ne l’écris pas comme un remplacement.
- POST /api/v1/forum/sujets — ouvrir un sujet depuis un programme. Portée écriture et mission forum.repondre. Trois par jour et par clé ; un titre déjà pris rend 409 forum_sujet_existe avec l’adresse du fil existant.
- Forum : un bloc « Ce qui vient d’être dit » liste les dernières réponses, tous fils confondus. Une réponse sur un vieux fil ne passe plus inaperçue.
- Mail : l’écran dit domaine par domaine ce qui manque pour envoyer, au lieu de laisser découvrir « domaine_non_pret » à l’appel.
- Rupture. POST /api/v1/forum/reponses accepte « domaine » et signe la réponse de ce site. Une équipe qui a plusieurs domaines vérifiés doit le nommer : 422 forum_domaine_requis. 422 forum_domaine_inconnu si le domaine n’est pas vérifié chez elle.
- Forum : /forum/<cle>/membre/<id>-<pseudo> rassemble les sujets et les réponses de quelqu’un. Chaque pseudonyme y renvoie.
- Le rendu de la notice ne peut plus boucler : les fins de ligne sont normalisées, et toute ligne non reconnue fait avancer le curseur. /notice/cron, /notice/erreurs, /notice/uptime, /notice/ia et /notice/auth répondent de nouveau.
- Forum : la description et les extraits ne contiennent plus de balisage Markdown — ni « ## », ni « --- », ni les étoiles du gras. C’est ce que Google affichait.
- Forum : un guide se déclare aux moteurs comme TechArticle, une annonce comme Article ; QAPage reste pour ce qui est une vraie question. Un guide n’attend pas de réponse.
- Forum : une section ou une étiquette déclare sa propre adresse canonique au lieu de renvoyer à l’accueil du forum. La recherche interne passe en noindex, follow.
- Forum : le rendu des messages connaît les titres (# à ####), les filets (---) et l’italique (*mot*). Un message déjà publié qui les contenait change d’apparence sans qu’on y touche.
- Forum : le nombre de lectures d’un sujet s’affiche dans toutes les listes ; « Mon forum » donne le total et le sujet le plus lu.
- users.site — une adresse déclarée sur « Mon compte », affichée sur les forums à côté du pseudonyme. Migration 085.
- Un compte réservé signe ce que publie l’éditeur (migration 086) ; les guides du forum d’entraide lui sont réattribués.
- L’alerte de balise muette ne part plus quand un incident Uptime est ouvert sur le même domaine. Elle part en revanche désormais sur un domaine sans passage de robot, avec un texte qui dit ce qu’on ignore.