Files
ASPIC/journal/ASPIC-Journal. 30. Chantier des consignes.md
T

9.8 KiB

30. Chantier des consignes — moteur générique (6 septembre 2026)

Origine. Reprise le soir même de la réflexion stratégique de la nuit précédente (Réflexions stratégiques, section 14) sur le pilotage vocal comme deuxième mode de fonctionnement, et de la session de réflexion du matin du 6 septembre sur la distinction entre règles intrinsèques (non modifiables) et consignes personnelles (modifiables par l'utilisateur) — voir Réflexions stratégiques, section 15, pour le fil conceptuel complet. Ce chantier est le premier cas concret construit sur cette distinction, volontairement circonscrit à la fiche contact (création/modification) pour commencer.

Nouvelle numérotation de version actée en tout début de session. Format à trois niveaux V<majeur>.<code>.<conception> (voir version.php, en-tête). Départ de la session : V1.13.14. <code> et <conception> anticipés dès le début de session (Jean-Marc savait déjà que les deux évolueraient) : V1.14.15.

Deux tables créées par Jean-Marc lui-même (convention habituelle) :

CREATE TABLE `ASP_Fonctions` (
  `FON_Incr`               int NOT NULL AUTO_INCREMENT,
  `FON_Nom`                varchar(30) NOT NULL,
  `FON_Nature`             varchar(12) NOT NULL,
  `FON_Description`        varchar(200) NOT NULL,
  `FON_ParametresAttendus` varchar(200) NULL,
  `FON_NomPHP`             varchar(50) NOT NULL,
  `FON_Active`             varchar(1) NOT NULL DEFAULT 'O',
  PRIMARY KEY (`FON_Incr`),
  UNIQUE KEY (`FON_Nom`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `ASP_Consignes` (
  `CNS_Incr`             int NOT NULL AUTO_INCREMENT,
  `CNS_Contexte`         varchar(40) NOT NULL,
  `CNS_Portee`           varchar(60) NOT NULL,
  `CNS_Champ`            varchar(40) NULL,
  `CNS_Type`             varchar(10) NOT NULL,
  `CNS_Nature`           varchar(12) NOT NULL,
  `CNS_Moment`           varchar(15) NULL,
  `CNS_Visible`          varchar(1) NOT NULL DEFAULT 'O',
  `CNS_Modifiable`       varchar(1) NOT NULL DEFAULT 'O',
  `CNS_Agent`            varchar(20) NULL,
  `CNS_LibelleCourt`     varchar(60) NOT NULL,
  `CNS_TexteDefinition`  TEXT NOT NULL,
  `CNS_TexteAffiche`     varchar(300) NOT NULL,
  `CNS_Fonction`         varchar(30) NULL,
  `CNS_Parametres`       JSON NULL,
  `CNS_MessageErreur`    varchar(200) NULL,
  `CNS_Active`           varchar(1) NOT NULL DEFAULT 'O',
  `CNS_DateCreation`     datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `CNS_Utilisateur`      varchar(3) NULL,
  PRIMARY KEY (`CNS_Incr`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

Conventions retenues pour les valeurs de colonnes (choisies par Jean-Marc, différentes du style sans accent utilisé ailleurs comme CVX_Type) : CNS_Type = Règle/Consigne, CNS_Nature = Traitement/Test, CNS_Moment = Saisie/Validation, booléens en O/N. CNS_Portee = 'Mise à jour' est traité comme un mot-clé signifiant "Création et Modification" par le moteur (voir plus bas), à côté de valeurs plus précises possibles plus tard ('Création' ou 'Modification' seules, pour une future règle d'immuabilité). CNS_Fonction référence ASP_Fonctions.FON_Nom (texte, pas la clé technique FON_Incr) — plus lisible pour une inspection directe en base.

moteur_consignes.php — le moteur générique. Fonction unique appliquerConsignes(PDO $pdo, string $contexte, string $portee, array &$data): ?string. Lit ASP_Consignes (jointure ASP_Fonctions sur FON_Nom = CNS_Fonction et FON_Active='O') pour le contexte/portée donnés, applique tous les Traitements actifs (silencieux, mutent $data par référence, dans l'ordre de CNS_Incr) puis tous les Tests actifs sur les valeurs déjà transformées ; retourne le message du premier test en échec (CNS_MessageErreur), ou null si tout passe. Aucune référence à un champ ou une consigne précise dans ce fichier — tout vient de la base. Limites assumées pour cette première version (documentées en en-tête du fichier) :

  • pas de distinction Saisie/Validation à l'exécution (voir plus bas, décision de Jean-Marc) ;
  • les consignes transverses à toute la fiche (CNS_Champ NULL, ex. futur contrôle de doublon) sont ignorées, à reprendre le jour d'un vrai cas ;
  • un seul message d'erreur renvoyé (le premier test en échec), pas d'accumulation.

consignes_fonctions.php — le catalogue de fonctions. Bibliothèque sans dépendance (pas de session, pas de base), une fonction par entrée de ASP_Fonctions.FON_NomPHP. Convention : un Traitement a la signature (string $valeur): string, un Test (string $valeur): bool. Trois fonctions écrites ce soir :

  • consigne_majuscule (MAJUSCULE, Traitement) — mb_strtoupper avec encodage explicite (un strtoupper() simple laisserait les accents français inchangés selon la locale serveur).
  • consigne_non_vide (NON_VIDE, Test) — trim($valeur) !== ''.
  • consigne_premiere_lettre_majuscule (PREMIERE_LETTRE_MAJUSCULE, Traitement) — "georges"→"Georges", "JOSEPH"→"Joseph", "jean-marc"→"Jean-Marc". Écrite à la main plutôt qu'avec ucwords() : cette fonction native PHP raisonne en octets, pas en caractères, et corromprait un prénom commençant par une lettre accentuée (ex. "Éric"). Implémentation par preg_replace_callback avec \p{L} (Unicode) après minuscule globale.

contacts.php débarrassé de tout contrôle en dur. Avant ce soir : $nom = trim($data['CNT_Nom'] ?? ''); if ($nom === '') { ... 400 ... } dupliqué en POST et PUT, aucun traitement de mise en forme. Après : un seul appel, appliquerConsignes($pdo, 'Contacts', 'Création'|'Modification', $data), suivi d'un retour 400 avec le message tel quel si non-null. Objectif explicite de Jean-Marc, vérifié en fin de session : ajouter ou modifier une ligne dans ASP_Consignes/ASP_Fonctions change le comportement réel de l'application sans qu'aucun fichier PHP ne soit retouché, sauf écriture d'une fonction nouvelle ou bug.

Détour et correction en cours de route : le temps réel, tenté puis abandonné. Premier réflexe de Claude (avant clarification par Jean-Marc) : coder en dur un contrôle "nom obligatoire" réactif au blur du champ côté écran, en plus du contrôle serveur — mauvaise cible, Jean-Marc voulait tester le moteur générique, pas un nouveau code écrit à la main par champ. Après clarification, Jean-Marc a lui-même arbitré l'abandon du temps réel pour cette version (latence non significative pour un seul champ, mais complexité de construction jugée disproportionnée par rapport au gain — priorité donnée à la robustesse). Tout le code de validation côté client (liste_contacts.html) a été retiré ; validerFormulaireContact() se contente d'envoyer et d'afficher tel quel le message renvoyé par le serveur.

Bug découvert (et corrigé) précisément à cause de ce retrait. En enlevant l'ancien contrôle client ("Le nom est obligatoire.", codé en dur avant ce chantier), on a révélé que le gestionnaire d'erreur de validerFormulaireContact() affichait déjà, de longue date, un message générique fixe ("Erreur : impossible de créer ce contact.") au lieu du message précis renvoyé par le serveur (data.error était lu pour la console, jamais pour le toast). Corrigé dans les trois points d'échec (PUT, POST, catch réseau) : afficherToast((data && data.error) || <message générique>, true).

Ajustements d'interface demandés par Jean-Marc après premiers tests réels :

  • Toast d'erreur centré sur le panneau de saisie (top:50%, comme #panneau-detail) et en rouge (var(--rouge-suppression), déjà utilisée pour le mode suppression) — nouvelle classe .toast-erreur, activée via un deuxième paramètre de afficherToast(message, erreur). Limité aux erreurs de validerFormulaireContact (PUT/POST/catch), les autres toasts du fichier restent inchangés.
  • white-space:pre-line ajouté à #toast (tous les toasts, pas seulement l'erreur) : un CNS_MessageErreur contenant des retours à la ligne saisis en base ne s'affichait pas correctement (HTML fond les retours à la ligne en un espace par défaut).

Test réel réussi en fin de session, sans aucune modification de code : Jean-Marc a ajouté trois nouvelles lignes dans ASP_Consignes (NON_VIDE sur CNT_Prenom et CNT_AdrVille, réutilisant la fonction déjà cataloguée) — comportement immédiatement actif après dépôt des fichiers déjà livrés, aucun fichier PHP/JS retouché. Preuve de concept validée par Jean-Marc : "On peut dire qu'on n'est pas mauvais !"

Reste à faire, noté en fin de session :

  • Concevoir un nouveau type de consigne pour les cas nécessitant un appel IA en cours de traitement (ex. retrouver le code postal d'une ville saisie sans code postal) — contrat de retour plus riche que Traitement/Test actuel (silencieux / besoin de précision / question à poser), gestion d'échec réseau, coût et latence d'un appel externe. Session dédiée à prévoir, pas un ajout mineur au catalogue existant.
  • Écran de gestion des consignes par le boss (au lieu de INSERT/UPDATE SQL à la main) — à construire après stabilisation du contrat ci-dessus, pour ne pas le refaire.
  • Réflexion sur un catalogue restreint de consignes/fonctions avancées à proposer aux futurs utilisateurs de l'agence, sans leur ouvrir la création libre de A à Z — argument commercial identifié par Jean-Marc ("Possibilité de nouvelles consignes pour vos assistants").
  • Accumulation de plusieurs messages d'erreur à la fois (le moteur ne remonte aujourd'hui que le premier test en échec).
  • Gestion des consignes transverses à toute la fiche (CNS_Champ NULL, ex. contrôle de doublon) — non traitée par le moteur actuel.
  • Distinction Saisie/Validation réellement exploitée un jour (temps réel), si le besoin redevient assez fort pour justifier la complexité écartée ce soir.