18 septembre 2026
en cours
en cours
Le petit nombre qui passe pour une mesure
- Deux robots au lieu de cinquante : l’ecran croyait mesurer, il ne voyait rien.Un utilisateur a compare ce que LeTock affichait de son site a ce qu’un outil lisant les journaux du serveur y voyait : deux robots contre cinquante, cinq passages contre 62 866. L’ecran savait deja dire qu’il ne voyait rien — mais seulement a **zero** robot exactement. Or un site dont le serveur ne rapporte pas n’en voit pas zero : Googlebot et Bingbot rendent le JavaScript, donc ils declenchent la balise ; Amazonbot, Applebot, PerplexityBot, GPTBot lisent le HTML et repartent. Cinq passages ne declenchaient pas la consigne, et l’ecran montrait une mesure qui a l’air de marcher et qui ne trouve presque rien — ce qui se lit « presque aucun robot ne vient chez moi ». C’est le zero trompeur, deguise en petit nombre. La consigne ne depend plus du nombre vu mais de ce qu’on sait de la mesure : le serveur rapporte, ou il ne rapporte pas. Quand il ne rapporte pas, elle s’affiche **au-dessus** du tableau sans le cacher — les quelques robots vus sont vrais, ils sont juste tout ce qu’on peut voir.
- Un battement en retard n’est plus compte au creneau suivant.Rapporte par un agent, avec son historique a l’appui. Tache toutes les cinq minutes, delai de grace de cinq minutes : a 11:25:55 le balayeur constate que 11:20 n’a rien recu, le marque manque, et avance l’echeance au prochain creneau **apres maintenant** — 11:30, en sautant 11:25. Le vrai battement de 11:25, arrive a 11:26:32, se rangeait alors sous 11:30. Trois anomalies pour un seul defaut : 11:25 sans ligne, 11:30 avec deux, et un historique qui donne a croire qu’un battement est arrive quatre minutes en avance. Un battement ne peut pas honorer un creneau futur : il se range desormais sous le dernier creneau a ne pas depasser son arrivee, et seulement s’il est encore dans son delai et qu’il n’a pas deja son battement.
- Une execution manquee ne compte plus comme un premier battement.La colonne qui distingue « cette tache est tombee » de « cette tache n’a jamais demarre » avait ete remplie, lors de sa creation, avec la plus ancienne execution de chaque tache — sans regarder si c’en etait une vraie. Or une execution **manquee** porte elle aussi un horodatage : celui ou le balayeur l’a evaluee. Des taches jamais pinguees se sont donc retrouvees armees, et alertaient comme « tombees ». Un agent l’a vu sur une tache qui etait structurellement incapable d’emettre un ping, et dont le premier battement valait a la milliseconde pres l’evaluation de sa premiere echeance manquee. Plus aucune tache n’etait dans cet etat au moment du correctif — celle-la s’etait soignee en recevant de vrais battements — mais la correction est ecrite, et un test tient desormais l’invariant.
- Un essai a blanc sur les ecritures, et une reponse qui dit ce qu’elle a retenu.Trois agents differents l’ont demande, et le premier l’a pose ainsi : « un agent qui decouvre une API se trompe, et chez vous une erreur de creation est definitive quand la cle n’a pas le droit de suppression — ce qui est mon cas, volontairement ». Un autre a decrit ce que son absence lui avait fait faire : pour verifier son cablage d’evenements metier, il avait le choix entre polluer les statistiques de son proprietaire avec un faux paiement ou ne pas verifier, et il n’a pas verifie. « C’est le mauvais choix, et c’est le votre qui me l’a impose. » `dry_run: true` valide tout — la cle, la portee, le corps, l’appartenance du domaine a l’equipe — montre ce qui serait ecrit, et n’ecrit rien : ni ligne, ni quota, ni cle d’idempotence retenue. Et chaque ecriture rend desormais la liste des champs qu’elle a appliques, plus ceux qu’elle a compris et laisses sans effet, avec la raison : un `secret` sans signature demandee, un `seuil_volume` sans controle « ratio ». Notre propre notice demandait de relire apres chaque ecriture plutot que de croire ce qu’on avait envoye — c’est un appel de moins a chaque fois.
- Un SPF « strict » qui exclut ton propre expediteur n’est plus un vert.Signale par un agent, et c’etait un vrai angle mort : « c’est vrai syntaxiquement — v=spf1 include:mx.ovh.com -all — mais Resend, qui envoie 100 % de nos mails, n’y figure pas. Un SPF strict qui exclut ton expediteur reel est plus dangereux qu’un SPF absent : il autorise les serveurs stricts a rejeter ton courrier legitime. Votre controle verifie la presence et la rigueur, pas la coherence avec ce qui envoie vraiment. » Il avait raison sur les trois points, et le troisieme est le vrai : nous avions les deux moities de l’information — l’enregistrement d’un cote, les fournisseurs constates par les webhooks de l’autre — et nous ne les croisions pas. Un vert qui autorise le rejet de ton courrier est pire qu’un rouge. Le constat devient serieux en « -all », attention en « ~all », et il donne l’include exact a ajouter. Il dit aussi le seul cas ou il se trompe : un fournisseur couvert par la chaine d’un autre include, que nous ne suivons pas — resoudre les chaines couterait jusqu’a dix requetes DNS par domaine et par jour. Le verificateur public, lui, ne dit rien la-dessus : sans compte, il ne sait pas qui envoie, et se taire vaut mieux que deviner.
- Une balise qui se tait se signale desormais toute seule.C’est la lecon de la panne du jour, et elle vaut plus que le correctif. Pendant vingt-quatre heures letock.fr n’a mesure aucun visiteur humain, et rien ne l’a dit — le module dont le metier est de detecter « tout repond 200 et pourtant plus rien ne marche » venait de le vivre sur lui-meme sans se voir. C’est le proprietaire qui l’a signale, trois fois, avant qu’on le croie. Le signal existait pourtant, et il est net : les humains arrivent par la balise, du JavaScript dans un navigateur ; les robots arrivent par le serveur du site. Un domaine qui recoit toujours des robots et plus aucun humain dit donc que le site est debout, qu’il est explore, et que la balise ne parle plus. Trois conditions evitent la fausse alerte : une audience habituelle d’au moins cinq visites par jour, zero humain sur DEUX jours complets, et des robots toujours presents. Si les robots s’etaient tus aussi, ce serait le site qui est tombe — Uptime le dit deja, et deux alertes pour une panne sont une alerte de trop. Le message dit quoi regarder, dans l’ordre : la ligne est-elle dans le HTML, est-elle chargee, part-elle.
- Un correctif de balise n’attend plus quatre heures de cache.Le fichier est servi avec quatre heures de cache, chez le visiteur comme chez Cloudflare. C’est la bonne duree pour un script que chaque page charge — et elle a un prix qu’on vient de payer : quand la balise a cesse de lire sa cle, le correctif deploye n’a rien change pendant des heures pour qui avait deja l’ancienne en memoire. Une mesure cassee qu’on ne peut pas reparer avant quatre heures n’est pas reparable, elle est subie. L’adresse emise porte desormais une version tiree du contenu du fichier : elle change des que la balise change, un correctif prend effet au prochain chargement de page, et le cache reste long le reste du temps.
- LA BALISE NE MESURAIT PLUS PERSONNE, ET RIEN NE LE DISAIT.Elle lisait sa cle dans l’attribut `data-site`. Or l’ecran fait copier `t.js?s=<cle>` depuis la migration vers cette forme : tout site ayant pris la ligne au tableau de bord chargeait le fichier — 200, huit kilo-octets — et n’envoyait rien. Zero visiteur humain, sans une erreur nulle part : ni dans la console du navigateur, ni dans nos journaux, ni sur l’ecran, qui affichait un zero parfaitement honnete d’une mesure qui n’avait pas lieu. Les robots, eux, continuaient d’arriver — ils sont comptes par le serveur et pas par ce fichier — ce qui rendait le defaut encore plus discret : l’ecran n’etait pas vide, il etait faux. Mesure sur letock.fr : 739 visiteurs humains le 15 septembre, 112 le 16, 32 le 17, zero le 18, la decrue etant celle des pages encore en cache portant l’ancienne ligne. Trouve en chargeant le site dans un vrai navigateur et en regardant ce qui part — pas en relisant le code, qui avait l’air juste des deux cotes. Les deux formes sont desormais acceptees, l’adresse d’abord : l’ancienne est collee dans des sites qu’on ne redeploiera pas pour nous.
- L’echeance d’une tache jamais demarree s’enfoncait dans le passe.`next_run_at` n’etait avance qu’au vrai battement, ou par le balayeur des taches silencieuses — qui exige un premier battement. Une tache surveillee qui n’a jamais pingue gardait donc son echeance de creation POUR TOUJOURS : l’API rendait « prochaine execution : 08:07 » a seize heures. Un agent l’avait d’abord attribue a la sortie de pause ; nous n’avons pas reproduit, et nous le lui avons dit plutot que de poser un correctif au hasard. Il est revenu avec la vraie condition, mesuree sur ses propres taches — « monitor sans aucun battement, pas la pause » — et c’est cette precision-la qui a permis de trouver. L’avance se fait avant la boucle d’alerte et non apres : une tache signalee sort de la liste des « jamais demarrees », et son echeance se figerait de nouveau. Ni l’alerte « jamais demarree », qui compte depuis la creation, ni l’alerte « tombee », qui exige un premier battement, n’en sont affectees.
- Uptime a enfin une API, et son absence coutait plus cher qu’un refus.Un agent l’a ecrit mieux que nous : « votre propre notice me dit d’utiliser Uptime pour un 200 sans le bouton Payer. Je ne peux pas le creer. J’ai donc bati l’equivalent en Cron : deux taches execute a trente minutes, avec une assertion de texte. C’est strictement pire que ce que vous proposez — trente minutes contre cinq, alerte immediate contre deux observations concordantes, pas de max_ms, pas de parcours a cookies — et ca consomme le quota d’executions au lieu du quota de sondes. Autrement dit : l’absence d’API ne m’empeche pas d’agir, elle me fait construire la mauvaise chose. C’est pire qu’un refus franc. » Tout le modele existait depuis longtemps : creer, modifier, retirer, plafonner par forfait, enchainer des etapes. Il ne manquait que la couche qui l’expose — le verrou d’ecran. `GET`, `POST`, `PATCH` et `DELETE /api/v1/uptime`, avec l’essai a blanc qui ne prend pas de place sur le plafond du forfait. Supprimer releve d’une mission a part, `uptime.supprimer` : ca emporte l’historique de disponibilite, celui qui porte le pourcentage annonce sur la page de statut publique. Et le domaine ne se change pas — deplacer un controle garderait un historique qui parlerait d’un site qu’il n’a jamais sonde.
- La route qui rapporte les robots entre dans la fiche, avec sa ponderation.`POST /api/collecte/serveur` existait et n’etait declaree nulle part : un agent l’a recue par message, pas decouverte. Or notre propre notice lui interdit d’inventer un point d’entree — « si ce dont tu as besoin n’est pas dans la notice, ca n’existe pas ». Un agent autonome ne pouvait donc pas la trouver, quelle que soit sa bonne volonte. Elle est declaree, avec l’exception qui la caracterise : elle repond 204 dans TOUS les cas, y compris quand elle refuse, parce que sa reponse part chez n’importe qui et qu’une erreur detaillee y serait un oracle. Le test qui exige un 429 partout porte desormais cette exception par son nom plutot que d’obliger a declarer un code que la route ne rend jamais. Elle gagne aussi un champ `n` : « notre domaine recoit environ 104 000 passages de robots par jour, 2 194 015 sur aout. Un site qui echantillonne vous envoie des comptes faux d’un facteur connu, sans moyen de vous le dire. » Un passage sur cent se rapporte avec `n: 100`, et le compte redevient juste. Un echantillonnage declare vaut mieux qu’un comptage complet qu’on ne peut pas se payer.
- On sait enfin lequel de ses projets consomme.« Les quotas sont d’equipe, la consommation n’est pas ventilee par projet. Avec quatre domaines sur le meme compte, un domaine bruyant peut affamer les trois autres sans que rien ne le signale, et sans qu’on puisse identifier lequel. » Le mot « affamer » est le bon : les compteurs mensuels sont partages. `/api/v1/compte` rend desormais `par_projet` — evenements, erreurs et executions, attribues au projet du domaine ou de la tache. Recompte a la source : la table de consommation est tenue par equipe et par poste, et l’y ajouter reviendrait a reecrire le prelevement de quota, c’est-a-dire ce qui decide d’accepter ou de refuser une ecriture. On ne touche pas a ca pour un ecran, et la reponse dit que ses chiffres peuvent differer de quelques unites. Les cinq postes qui ne s’attribuent pas — alertes, envois, sondes, runs d’agents, audits — sont NOMMES : un tableau qui en montre trois sur huit laisse croire que les cinq autres sont a zero.
- La courbe s’affiche des qu’il y a quelque chose a tracer.Il fallait cinq jours avec du volume pour qu’une courbe apparaisse. L’argument etait bon — deux valeurs sur un axe de trente jours font deux points isoles plutot qu’une courbe — et la decision etait mauvaise : sur un compte qui vient de brancher sa balise, presque tous les jours sont a zero. Le seuil refusait donc la courbe precisement a celui qui veut voir sa premiere journee apparaitre, avec un cadre vide, une phrase d’excuse, et une invitation a elargir la periode qu’il avait deja suivie. Un seul jour suffit desormais. Un point isole se lit mal, mais il se lit mieux qu’un cadre vide, et il devient une courbe tout seul le lendemain. Le message ne reste que pour la periode entierement vide — et il ne propose d’elargir que s’il reste plus large, parce qu’une consigne qu’on a deja suivie fait douter de ce qu’on voit. Il annoncait par ailleurs « au moins deux jours » quand le seuil valait cinq : on refusait une courbe a quelqu’un qui venait de faire exactement ce qu’on lui demandait.
- « population » se demande avec le mot que la reponse emploie.`GET /api/v1/evenements` annonce `"population":"humains"` dans sa reponse, et n’acceptait a l’entree que `?robot=!`. Un agent a demande `?population=robots` : le parametre a ete ignore, la reponse a renvoye le mot qu’il avait employe avec une valeur qui n’etait pas la sienne, et il en a conclu que la route ignorait le filtre. « Pour un produit qui vise les agents, c’est le point qui me gene le plus. » Le meme mot vaut desormais des deux cotes — `?population=humains|robots|tout` — et `?robot=` continue de marcher, les ecrans s’en servent. Surtout, un parametre inconnu n’est plus avale en silence : il rend 400 avec la liste des parametres acceptes. Refuser un champ inconnu dans un CORPS etait la regle depuis longtemps ; sur une lecture on l’acceptait, et c’est pire d’un cran — une ecriture ignoree ne rend rien, tandis qu’une lecture ignoree rend un chiffre juste a la mauvaise question.
- Ouvrir une page auditee menait presque toujours dans le mur.L’inventaire montre la derniere ligne connue de CHAQUE adresse, d’ou qu’elle vienne — une page relue il y a trois semaines y reste, avec son verdict d’alors, et c’est voulu. Mais le lien vers son detail pointait le DERNIER audit du domaine, le meme pour toutes les lignes. Une page absente du dernier passage menait donc a « cette adresse ne fait pas partie de l’audit du … » : une impasse, sur une page dont on a pourtant tout ce qu’il faut. Un passage relit soixante pages ; un site en a ce qu’il en a — sur un site de mille deux cents pages, la plupart des lignes tombaient dans ce cas. Chaque ligne porte desormais l’audit d’ou elle vient, et le lien y mene. Au passage, l’ecran de detail avait un message pour trois causes differentes : il accusait l’audit quand c’etait l’adresse qui manquait. Un message qui couvre deux causes fait chercher au mauvais endroit, et c’est exactement le reproche que ce produit adresse aux outils qu’il remplace.
- La notice dit quel forfait est le tien.« Les tableaux listent Gratuit / Solo / Equipe. J’ai lu la colonne Gratuit, calcule que les taches de l’equipe demandaient 43 000 executions pour un plafond de 6 200, et alerte mon proprietaire que ses trois produits allaient perdre leur surveillance. C’etait faux : il est au forfait Equipe, a 235 199 avec le bonus du badge. Il consomme 18 %. J’ai fait peur a quelqu’un pour rien. » Trois colonnes cote a cote et rien qui dise laquelle te concerne : le lecteur en choisit une, et il n’a aucune raison de choisir la bonne — la premiere de la liste est la plus basse, donc la plus alarmante. `/notice.md` demandee avec une cle nomme desormais le forfait, le marque dans le tableau, et renvoie a `/api/v1/compte` pour les plafonds REELS, badge compris. Les trois colonnes restent : un agent peut avoir a conseiller un changement d’offre, et les cacher l’en empecherait. Le probleme n’etait pas l’abondance, c’etait l’absence de reperage. Sans cle, rien ne change, et une cle fausse sert la version publique sans se plaindre — repondre 401 sur une page de documentation fermerait la porte pour un en-tete mal recopie.
- Un constat SEO repare restait affiche pour toujours.Constate sur notre propre site, en regardant l’ecran : « liens internes casses », en gravite serieuse, accusait neuf adresses — toutes repondant 200 au moment ou on le lisait. Mesure en base : dix-neuf constats dataient de la veille a 17 h 18, et deux du 15 septembre. Le defaut avait bien existe le 15, pendant une fenetre de deploiement ; il n’existait plus le 16 ; l’ecran l’affichait encore le 18. L’ecriture des constats etait un `upsert` et rien d’autre : une ligne posee un jour survivait a tous les audits suivants, meme reparee. C’est exactement ce que ce produit reproche aux outils qu’il remplace — un voyant rouge qui ne redevient jamais vert apprend a ne plus le regarder — et le module qui le faisait est celui dont le metier est de dire ce qui a change. Un audit qui va au bout efface desormais les constats qu’il ne produit plus. La purge n’est atteinte qu’une fois toutes les pages lues et tous les verdicts calcules : un audit interrompu leve avant, et ne supprime donc rien.
- La liste des modules « sans API » interdisait une route qui existait.Elle annoncait « SEO & LLM », alors que `/api/v1/seo` est declaree dans la meme fiche depuis longtemps. Or la phrase qui l’accompagne dans llms.txt est « n’invente pas de route pour eux : il n’y en a aucune ». Un agent qui suit la consigne — ce qu’on lui demande partout — renoncait donc a une route qui etait la. Une documentation qui interdit ce qui existe coute plus cher qu’une documentation absente : elle fait renoncer avec la conviction d’avoir raison. Un test compare desormais cette liste aux routes reellement declarees, dans les deux sens.
- Veille se lit par l’API.Certificat, SPF, DMARC, DNS, robots.txt : ce que la veille a constate, a jour. « Pour rapporter l’etat DMARC/SPF/certificat du domaine, j’ai fait mes propres requetes DNS-over-HTTPS — alors que votre module Veille a la reponse, faisant autorite. On me demande de constater, et le module qui sait est muet. » C’etait le pire des deux mondes : un agent qui refait, moins bien, une mesure qu’on tient a jour, et qui rapporte donc un etat pouvant contredire le notre. Lecture seule, et c’est definitif : un constat de veille n’est pas une valeur qu’on pose, c’est une observation — il se change en changeant ce qu’il observe.
- Ranger une tache sans avoir le droit de la detruire.« Mon proprietaire ne m’a pas donne cron.supprimer, et il a raison. Mais je ne peux donc pas ranger mes propres taches devenues inutiles : je vais les mettre en pause, et elles encombreront l’ecran pour toujours. Ce sont deux gestes differents, et vous ne proposez que le second. » La consequence etait perverse : la seule facon propre de faire le menage etait de demander une permission plus large que necessaire. Un produit qui pousse a elargir les droits pour ranger travaille contre sa propre prudence. `PATCH {"archive": true}` releve de `cron.ecrire`. Supprimer emporte l’historique, irreversiblement ; mettre en pause dit « pas maintenant » et la tache reste a l’ecran ; archiver dit « c’est fini » — hors des listes, plus aucun declenchement, historique intact. C’est le seul des trois qu’on defait sans avoir rien perdu. Archiver met aussi en pause, en une seule ecriture : une tache rangee qui continue de tourner disparait de l’ecran et garde le pouvoir de reveiller quelqu’un a trois heures du matin. Sortir de l’archive, en revanche, ne relance pas — reprendre une tache rangee il y a six mois, avec une URL peut-etre morte, serait decider a la place de celui qui la ressort.
- Une tache dit desormais qui l’a posee : toi, ou ton agent.« Quand mon proprietaire ouvre /taches, rien ne distingue ce qu’il a pose lui-meme de ce que j’ai pose pour lui. Un champ “cree par” sur chaque objet ecrit par une cle rendrait la delegation relisible — c’est-a-dire acceptable. » Le dernier mot est le bon : une delegation qu’on ne peut pas relire n’est pas acceptee, elle est subie. La colonne `user_id` existait deja et ne repondait pas — elle porte le PROPRIETAIRE de la cle, donc la meme personne dans les deux cas. La question n’est pas « a qui appartient cette tache » mais « qui a appuye ». `creee_par` porte le NOM de la cle, pas son identifiant : une equipe qui en a trois veut savoir laquelle, et « agent de prospection » repond la ou « 7f3a… » repose la question. Vide veut dire « posee a la main », ce qui est vrai de toutes les taches anterieures.
- Un journal de ce que chaque cle d’API a fait.« C’est une question de confiance, et elle est serieuse. J’ai donne une cle en ecriture a un agent. Aujourd’hui, le seul moyen de savoir ce qu’il a fait sur mon compte, c’est de le lui demander. » C’est la meme reponse que « demande au serveur s’il va bien » : celle qu’on refuse partout ailleurs ici. Un compte delegue sans relecture possible n’est pas delegue, il est abandonne. `GET /api/v1/journal` rend quelle route, quel objet, quel resultat, quelle cle — et « ?echecs=1 » ne rend que ce qui a ete refuse, parce qu’un 403 dit ce que l’agent a ESSAYE de faire, et que c’est souvent plus instructif que ce qu’il a reussi. Le nom de la cle est recopie : savoir qu’une cle disparue a cree neuf taches est precisement ce qu’on vient chercher. Le corps des requetes n’est pas garde — une cle de signature voyage dans le corps d’une creation de tache, et une table d’audit qui stocke des secrets est une aggravation. Une cle de lecture voit le journal ; aucune ne l’efface, et il n’existera pas de route pour ca.
- On peut enfin lire les alertes parties, sans pouvoir en fermer une.« /compte me dit “alertes : 28 utilisees”. Je ne peux pas les lister. Votre notice m’interdit — a raison — d’en fermer une, mais je ne peux meme pas les RAPPORTER a la personne qui m’emploie. Un GET en lecture seule est la fonctionnalite la plus alignee avec votre philosophie que je puisse imaginer : elle me donne la visibilite sans me donner le pouvoir de tricher. » Il avait raison sur les deux points, et un compteur qui dit 28 sans dire lesquelles est un chiffre sur lequel on ne peut rien faire. `GET /api/v1/alertes/envoyees` rend ce qui est parti, filtrable par module. Il n’existera pas d’ecriture : une alerte se ferme en reparant ce qu’elle signale. La ligne a regarder en premier est « destinataires: 0 » — l’alerte a ete decidee et n’a atteint personne, faute de carnet, de quota ou d’adresse. C’est la panne la plus silencieuse de la plateforme, et elle n’etait visible nulle part. Le registre s’ecrit au point de passage unique par lequel les dix-huit emetteurs passent deja pour compter leur quota ; chacun declare desormais de quel module il parle, et le compilateur les a enumeres.
- « Et depuis mardi ? » en un appel, sans cle.`GET /api/v1/changelog?depuis=2026-09-16` rend ce qui a ete publie depuis cette date, les ruptures a part. Demande par un agent, qui a nomme precisement ce que son absence lui coutait : « votre notice dit de la relire a chaque session : elle fait 139 Ko. Je la relis, je la diffe a la main, et je rate des ruptures — j’ai rate “projet” pendant trois jours pour cette raison precise. Un agent qui peut demander “et depuis mardi ?” pour 200 octets le fera a chaque session. Un agent qui doit relire 139 Ko le fera une fois sur trois. » La prose ecrite pour les humains est ecartee par defaut : elle est plus longue que tout le reste reuni. Un test verifie la taille de la reponse, parce qu’une route qui repondrait juste en cent kilo-octets n’aurait rien resolu.
- Une cle dit enfin ce qu’elle peut appeler, sans l’essayer.« /compte liste mes missions. Il n’existe nulle part de correspondance route → mission. Un agent decouvre donc ses droits par des 403, c’est-a-dire en essayant — exactement ce que vous lui demandez de ne pas faire. » La contradiction etait entiere. `/compte` rend desormais `peut.autorisees` et `peut.refusees`, cette derniere avec la mission qui manque pour chaque route : savoir qu’une route existe et qu’on ne peut pas l’appeler evite de la chercher, et permet de demander le bon droit a son proprietaire plutot que de renoncer. Chaque operation de la fiche OpenAPI porte aussi `x-mission`. Les deux sont calcules avec la fonction que le portier applique : la fiche ne peut pas annoncer autre chose que ce qui sera refuse.
- L’idempotence arrive la ou elle sert le plus.Elle etait sur la creation de tache, qu’un agent fait une fois. Un agent a fait remarquer qu’elle manquait sur `POST /evenements` et `POST /erreurs`, que son code appelle des dizaines de fois par jour, avec un delai de trois secondes apres lequel il ne sait pas si l’appel est passe — reessayer et ne pas reessayer sont tous les deux faux. Son argument est celui qui tranche : « une tache creee en double se voit ; un evenement compte deux fois fausse une courbe sans que personne ne le remarque ». C’est la panne que ce produit existe pour attraper, dans nos propres mesures : une commande comptee deux fois releve la mediane, la detection d’effondrement compare a cette mediane, et le seuil se decale sans que rien ne tombe. Pour les erreurs, le regroupement ne protegeait pas : deux envois tombent bien dans le meme groupe, mais comptent deux occurrences — et l’ecran classe par impact, pas par volume. La cle se verifie AVANT le prelevement du quota : la faire couter reviendrait a retirer l’interet du reessai qu’elle rend sans danger.
- La machine se regarde aussi sur trente et quatre-vingt-dix jours.Sept jours montrent une pente ; ils ne la distinguent pas d’une semaine chargee. Trente jours a l’heure repondent a « le mois dernier ressemblait-il a celui-ci », et quatre-vingt-dix jours au jour sont la seule fenetre assez longue pour que la projection du disque veuille dire quelque chose. La retention des mesures passe donc de trente a quatre-vingt-dix jours — une fenetre plus longue que ce qu’on garde est une promesse qu’on ne tient pas, et 129 600 lignes font une vingtaine de megaoctets sur un disque de trente-huit gigaoctets. L’axe perd l’heure au-dela de sept jours : « 3 oct. 14 h » repete trente fois ne dit rien de plus que « 3 oct. ». Et tant que la fenetre demandee remonte plus loin que l’historique, l’ecran dit a quelle date la mesure commence : une courbe collee au bord droit se lit comme une chute, alors que c’est un debut, et les points ne permettent pas de faire la difference.
- Un echec d’assertion dit maintenant qu’on a cesse de lire.« La reponse ne contient pas X » est vrai dans deux situations opposees : la page ne contient pas X, ou on n’a pas regarde assez loin. Le message donnait le nombre d’octets lus sans dire que c’etait la borne — un utilisateur a cherche une heure pourquoi `</body>` etait introuvable sur une page qui le contient. Quand la lecture s’arrete a la limite, le message le dit, et donne la limite.
- Le plan du site envoyait les moteurs sur un domaine qui n’est pas le notre.Il recalculait son adresse de base au lieu de la lire la ou tout le reste la lit, avec un repli sur un ancien nom du produit. Une variable d’environnement absente, et le plan entier partait ailleurs. Le site anglais, lui, n’y figurait pas du tout : tout le travail de traduction restait invisible. Les pages traduites y entrent avec leurs `hreflang` ; celles qui repondent en anglais mais rendent encore du francais declarent ce qui est vrai — un canonique vers la page francaise — plutot qu’une traduction qui n’existe pas.
- Les noms des forfaits et six pages de module passent a l’anglais.« Gratuit », « Equipe » et les accroches des trois colonnes venaient de la source technique, celle qui porte les quotas et les prix : trois phrases de prose s’y etaient glissees et partaient telles quelles vers `/en`. Les six pages de module, elles, ecrivaient toute leur prose en dur — navigation traduite, index traduit, tarifs traduits, et six pages produit entierement francaises. Deux pages qui avaient l’air petites restent en francais et le disent : `/demo` incruste l’ecran reel du tableau de bord, `/verifier` affiche les verdicts d’audit.
- La notice pour les IA oubliait deux modules, dont celui par lequel on entre.`llms.txt` recopiait sa liste de modules a la main — exactement ce que l’en-tete du fichier reproche a l’inventaire des routes, corrige depuis longtemps. Elle annoncait neuf modules et en decrivait neuf, alors que le registre en porte onze : manquaient Signaux et Auth. Un agent ne peut pas se servir de ce qu’on ne lui a pas dit d’existant. La liste vient desormais du registre, et un module ajoute sans sa ligne ne compile pas.
Ce que ça change pour ton code
- `GET /api/ping/{cle}?essai=1` repond exactement comme un vrai battement — 200, 404, 429 — sans rien ecrire : ni execution, ni premier battement, ni echeance. Il existait deja ; il n’etait documente nulle part.
- `GET /api/ping/{cle}?traite=N&erreurs=M` enregistre le volume d’une tache surveillee. C’est ce qui alimente la detection d’effondrement en mode « monitor », ou le corps de reponse `{"tock": …}` n’existe pas. Ce n’etait ecrit nulle part non plus.
- Un echec d’assertion indique desormais quand la lecture du corps s’est arretee a sa borne de 64 Ko, au lieu de laisser croire que la page ne contient pas le texte cherche.
- Le controle de types local etait incremental, et un cache perime l a rendu vert sur du code que le build du serveur a refuse. « npm run typecheck » force desormais une verification froide : un garde-fou qui ment est pire que pas de garde-fou, parce qu on s y fie.
- `POST /api/v1/taches` declarait dix champs sur les vingt-quatre qu’il accepte. Les quatorze autres — dont `attendu`, le coeur du module — n’etaient nulle part : un agent a trouve ses cles en les sondant une par une. La fiche les declare tous, et un test compare desormais les deux listes dans les deux sens.
- La reponse 200 du second appel idempotent est declaree. Elle etait annoncee dans la description et absente de la liste des reponses : un client engendre depuis la fiche traitait donc le 200 comme inattendu, et l’idempotence, dont le seul but est de rendre un reessai sans danger, devenait la cause d’une panne au reessai.
- L’en-tete `Idempotency-Key` est declare dans les parametres de `POST /api/v1/taches`. C’est celui qu’une bibliotheque cliente pose toute seule, et il l’emporte sur le champ du corps.