Un chauffeur arrive à la guérite du port à 5 h du matin. Pour entrer, il lui faut un code d'accès, délivré après une demande déposée sur le portail du port. Si la demande n'a pas été faite, il attend. Si elle a été faite avec la mauvaise plaque, il attend aussi, et le conteneur qu'il transporte attend avec lui.
Pour MOVELT, une entreprise de transport qui achemine des conteneurs vers le Port de Kribi, ces demandes étaient saisies à la main par une assistante, souvent la nuit. Le portail n'a pas d'API. Le seul moyen d'y déposer une demande, c'est son formulaire web.
J'ai construit l'agent qui le remplit à sa place. Ce qui suit n'est pas un tutoriel Playwright : remplir des champs, c'est la partie facile. C'est ce que j'ai appris sur la partie difficile, décider quand ne pas agir.
D'abord, définir ce qu'on cherche vraiment
Le cahier des charges parlait d'« accélérer les demandes d'accès ». Ce n'était pas le vrai problème. La validation par la capitainerie prend le temps qu'elle prend, et ne dépend pas de nous.
Le vrai problème, c'était la saisie nocturne. Une personne réveillée pour recopier des plaques et des numéros de conteneur dans un formulaire. Le critère de succès est donc devenu : plus personne ne saisit la nuit.
Ça paraît une nuance. C'est ce qui a orienté tous les arbitrages suivants. Quand on hésite entre aller plus vite et être plus sûr, on sait ce qu'on choisit.
Le déséquilibre qui décide de tout
Dans la plupart des logiciels, une erreur coûte un ticket de support. Ici :
- une demande non déposée coûte un appel à l'assistante, qui la fait à la main ;
- une demande fausse bloque un camion à la guérite.
La première erreur est récupérable. La seconde ne l'est pas. Tout le reste de la conception découle de ce déséquilibre : l'agent préfère s'arrêter que deviner.
Lire en HTTP, écrire avec un vrai navigateur
Deux opérations, deux outils.
Pour lire l'état des demandes, la page de liste du portail est du HTML rendu côté serveur. Une requête HTTP avec la bonne session suffit. Pas de navigateur, presque rien à exécuter : on peut la consulter toutes les deux minutes sans coût.
Pour écrire, c'est-à-dire déposer une demande, on passe par un vrai Chromium piloté par Playwright. La tentation serait de reproduire la requête POST du formulaire à la main : plus rapide, plus léger. Mais elle casse au premier changement du site, et ce site n'est pas le nôtre. On ne sera pas prévenu de ses mises à jour. Un navigateur qui remplit le formulaire comme un humain survit à beaucoup plus de changements qu'une requête fabriquée.
Toute la connaissance du portail (sélecteurs, libellés, repères de page) vit dans un seul module. Le jour où le port modifie son formulaire, il y a un seul endroit à corriger.
Reconnaître la page avant de toucher quoi que ce soit
Premier réflexe de l'agent : vérifier qu'il est bien là où il croit être. Chaque bloc attendu du formulaire est cherché avant le moindre clic. S'il en manque un, l'agent abandonne, avec le nom du bloc manquant dans le message d'erreur.
C'est moins anodin qu'il n'y paraît. Une page de maintenance, une session expirée qui renvoie vers l'accueil, une nouvelle version du formulaire : dans tous ces cas, un script naïf remplirait les mauvais champs.
Deuxième découverte : l'ordre de remplissage compte. Sur ce formulaire, choisir les dates déclenche un chargement qui peuple les listes de chauffeurs et de véhicules. Remplir le chauffeur avant la date, c'est le chercher dans une liste encore vide. Aucune documentation ne le dit : on l'apprend en regardant le site travailler.
Relire avant de cliquer
Avant le clic final, l'agent relit le formulaire entier et le compare à la demande d'origine. Chauffeur, véhicule, dates, conteneur : tout doit correspondre. Au moindre écart, il abandonne. Jamais de soumission partielle.
Et il prend une capture d'écran juste avant le clic, conservée avec le HTML de la page. Sur un système auquel on n'a pas d'accès en lecture directe, c'est la seule façon de comprendre un échec après coup. C'est aussi la pièce qu'on produit si la guérite conteste la demande.
Trois verdicts, pas deux
Après le clic, la question naturelle est : est-ce que ça a marché ? On s'attend à deux réponses, oui ou non. Il y en a trois :
- acceptée : la demande existe au port ;
- refusée : elle n'existe pas, on peut corriger et recommencer ;
- indéterminée : la page n'a pas répondu comme prévu.
La troisième est la dangereuse. La demande existe peut-être. La relancer automatiquement risquerait d'en créer une seconde, et le chauffeur se présenterait avec un code qui n'est pas le bon. Donc on ne relance jamais un indéterminé. On alerte un humain, qui vérifie.
C'est la même logique que pour les paiements Mobile Money : l'état « je ne sais pas » existe, et le traiter comme un échec est la façon la plus sûre de payer deux fois.
Le mode « à blanc » par défaut
Le chemin complet de soumission est éprouvé en production. Pourtant, par défaut, l'agent remplit, relit, et ne clique pas. Il faut lever explicitement ce garde-fou en production.
Pourquoi ? Parce qu'une installation neuve (un poste de développement, un serveur de test) qui se mettrait à déposer de vraies demandes au port sans qu'on l'ait voulu serait le pire défaut possible du système. Un défaut doit être sûr. Être utile, c'est le travail de la configuration.
Même logique pour la concurrence : tant qu'on ne sait pas comment le portail réagit à deux sessions simultanées sur un même compte, une seule demande à la fois par compte. On parallélisera quand on l'aura mesuré.
Le cahier des charges se trompe, le site a raison
Deux découvertes ont contredit les spécifications, et les deux auraient envoyé des chauffeurs pour rien.
La colonne qui devait contenir le code n'en contient pas. D'après les spécifications, il suffisait d'attendre qu'une colonne se remplisse pour récupérer le code d'accès. Les captures ont montré qu'elle contenait le numéro de la demande, présent dès le dépôt. Le vrai signal arrivait un quart d'heure plus tard, ailleurs : une colonne d'état qui passe à « autorisé ». Si on avait suivi le document, chaque demande aurait été annoncée validée dès son dépôt.
Un refus que personne n'avait prévu. En plus du refus de la capitainerie, le portail peut renvoyer un refus d'un autre acteur. Le cahier des charges ne le mentionnait pas. Sans lui, un chauffeur serait parti pour une entrée qui ne viendrait jamais.
La leçon : quand on automatise un système qu'on ne contrôle pas, la seule spécification fiable est le système lui-même. Les captures réelles, anonymisées, sont devenues les fixtures des tests.
Recopier les coquilles
Une règle qui fait sourire, puis qu'on adopte : dans notre base, le nom de chaque chauffeur et la plaque de chaque véhicule sont recopiés caractère pour caractère depuis le portail. Avec ses fautes de frappe.
Si le portail écrit un nom avec une lettre en trop, nous aussi. C'est la seule façon de comparer nos écrans à son formulaire sans interpréter. Corriger l'orthographe chez nous créerait un écart qu'il faudrait réconcilier à chaque demande. Recopier transforme toute divergence en anomalie visible.
Signaler, ne pas bloquer
Les numéros de conteneur suivent la norme ISO 6346, avec un chiffre de contrôle. L'application le recalcule pendant la saisie. Quand il ne correspond pas, elle propose une correction, sans refuser. C'est le chauffeur qui a le conteneur sous les yeux, pas nous.
Deux surprises en écrivant les tests :
- les six numéros d'exemple des maquettes échouaient tous au contrôle, y compris celui du cahier des charges ;
- le contrôle est solide sur les chiffres, faible sur les lettres. Selon le numéro, 22 à 31 des 325 paires de lettres possibles donnent le même chiffre de contrôle à une position donnée. Une faute sur le préfixe peut donc passer inaperçue.
Le chiffre de contrôle est un filet, pas une preuve. Le vrai garde-fou reste la vérification au terminal.
L'humain reprend la main sans honte
Quand l'agent s'arrête, la supervision prend le relais. Deux issues sont prévues, et aucune n'est un échec :
- abandonnée : la supervision retire la demande à l'agent. À l'écran, c'est gris, pas rouge, parce que personne n'a raté quoi que ce soit ;
- reprise manuelle : la supervision dépose la demande elle-même sur le portail et saisit le code obtenu, qui redescend au chauffeur sans recopie.
Ce détail de couleur compte. Une interface qui affiche en rouge chaque arrêt prudent de l'agent apprend à ses utilisateurs à le désactiver.
Ce qu'il faut retenir
Automatiser un site sans API, c'est faisable, et souvent la seule option. Mais le code qui remplit le formulaire est le plus petit morceau du système. L'essentiel :
- Définis le vrai succès avant d'écrire une ligne.
- Lis en HTTP, écris avec un navigateur, et isole la connaissance du site dans un seul module.
- Reconnais la page avant d'y toucher, relis avant de cliquer, capture au moment du clic.
- Trois verdicts : ne relance jamais ce que tu ne sais pas qualifier.
- Un défaut sûr : à blanc tant qu'on n'a pas dit le contraire.
- Le site fait foi, pas le document qui le décrit.
- Un arrêt de l'agent n'est pas une faute : conçois la reprise humaine comme un chemin normal.
Un agent qui ne s'arrête jamais n'est pas fiable. Il a juste de la chance, pour l'instant.
Le projet MOVELT est décrit ici. Tu as un processus qui repose sur la saisie manuelle dans un portail sans API ? Écris-moi : on commencera par chercher ce que l'automatisation ne doit surtout pas faire.