mirror of
https://github.com/JMP-MS/ASPIC.git
synced 2026-10-27 19:27:10 +00:00
103 lines
18 KiB
Markdown
103 lines
18 KiB
Markdown
## 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) :**
|
|
|
|
```sql
|
|
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 du 6 septembre :**
|
|
- 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. *(Premier incrément construit dès le lendemain, voir 7 septembre 2026 ci-dessous — pas encore le contrat complet à trois issues.)*
|
|
- É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.
|
|
|
|
## Suite — 7 septembre 2026 : hiérarchisation, traçabilité, et premier appel IA réel dans une consigne
|
|
|
|
**Contexte stratégique complet : Réflexions stratégiques, section 17 ([Évolutions majeures]).** Ce qui suit est le compte rendu technique correspondant.
|
|
|
|
**1. Badge de chargement IA générique.** Nouvel endpoint `consignes_apercu.php` et convention de fonction jumelle : tout Traitement marqué `FON_VisuelAttenteIA='O'` dans `ASP_Fonctions` doit être accompagné d'une fonction `<FON_NomPHP>_seraitDeclenchee(array $valeurs): bool`, sans aucun appel réseau, qui répond seule à "ce traitement se déclencherait-il réellement sur ces valeurs". `auMoinsUneConsigneIaSeraitDeclenchee()` (`moteur_consignes.php`) interroge ces fonctions jumelles pour toutes les consignes actives du contexte/portée, avant même une tentative d'enregistrement — `liste_contacts.html` n'affiche le badge `#badge-ia-actif` que si la réponse est positive, jamais de façon systématique. Rendu : rond, centré à l'écran comme un toast d'erreur, 65px de côté après plusieurs essais (130 → 50 → 25 → 65), vérifiés au rendu réel via Playwright plutôt qu'en pixels devinés. `pointer-events:none` (jamais un obstacle au clic, même principe que les autres éléments purement informatifs de l'écran de fond).
|
|
|
|
**2. Hiérarchisation des trois phases d'`appliquerConsignes()`.** Bug réel découvert par Jean-Marc : une soumission vouée à échouer sur un Test déclenchait quand même, avant l'échec, un Traitement IA coûteux — appel réseau gaspillé, sans même laisser de trace puisque l'enregistrement entier était rejeté ensuite. Le moteur exécute désormais trois phases strictement séquentielles, distinguées par le seul attribut `FON_VisuelAttenteIA` déjà présent : (1) tous les Traitements de cohérence (`FON_VisuelAttenteIA != 'O'`), (2) tous les Tests (le premier échec arrête tout, avant d'atteindre la moindre étape IA), (3) seulement si tous les Tests ont réussi, les Traitements IA (`FON_VisuelAttenteIA = 'O'`). Nouvelle fonction interne `executerTraitements(array $consignesTraitement, array &$data, array &$journalisations, array &$avertissements): void`, appelée deux fois (phase 1 et phase 3) avec un sous-ensemble différent de consignes.
|
|
|
|
**3. Traçabilité de l'agent réel dans une réalisation journalisée.** `executerTraitements()` ne fait plus confiance à un nom d'agent proposé par la fonction de traitement elle-même (source d'un bug déjà corrigé : un prénom "Mat" resté codé en dur sans aucune source) — le nom journalisé dans `_realisation.agent` vient désormais systématiquement de `$c['CNS_Agent']` (la consigne active elle-même), tronqué à 15 caractères UTF-8, avec un repli sur la valeur de la fonction puis sur `'IA'` seulement si `CNS_Agent` est vide. Même principe déjà en place côté `$agentEchec` pour un Test en échec.
|
|
|
|
**4. `REA_NomAffiche` — instantané du nom affiché, jamais recalculé à la lecture.** Nouvelle colonne sur `ASP_Realisations`, remplie une seule fois à la création de chaque ligne : côté humain, `$_SESSION['USR_Prenom']` (désormais chargé et stocké en session dès `aspic.php`, à la connexion — requête étendue `SELECT USR_Nom, USR_Prenom FROM ASP_Users`) ; côté IA, le même nom d'agent que celui journalisé dans `_realisation.agent` (voir point 3). Une première approche par jointure `ASP_Users` recalculée à la lecture a été écartée (complexité de la requête, et surtout incompatibilité avec un futur agent hébergé dans une autre base MIRADOR) au profit de cet instantané écrit une seule fois — zéro requête supplémentaire à la lecture, zéro risque de base croisée. `realisations.php` (GET et POST) et `contacts.php` (`journaliserRealisationsIA()`) mis à jour en conséquence ; `liste_contacts.html` affiche `r.REA_NomAffiche || r.REA_CodeUser || ""` et la colonne "Qui ?" a été élargie de 40 à 90px pour ne plus tronquer "Jean-Marc"/"Matilda".
|
|
|
|
**5. Renommage `ASP_Agents.AGN_Nom` → `AGN_Prenom` (à l'initiative de Jean-Marc), et une régression corrigée.** Fait pour homogénéiser avec `USR_Prenom`. Claude a validé ce renommage en affirmant "aucun impact côté code" sans avoir grep le nom de colonne dans l'ensemble du projet — cinq fichiers pré-existants du pilotage vocal (`agents.php`, `micro.js`, `contact_ia_panel.js`, `contact_ia_interprete.php`, `agent_notes.php`) référençaient encore `AGN_Nom`, cassant la liste des agents ("la liste des agents n'est pas disponible !"). Tous corrigés le même soir. Leçon actée pour la suite (voir Réflexions stratégiques, section 17) : grep systématiquement le nom d'une colonne dans tout le code avant d'affirmer "aucun impact" sur un changement de schéma.
|
|
|
|
**6. `consigne_sans_chiffre()` — nouveau Test générique.** `consignes_fonctions.php` : `return trim($valeur) === '' || !preg_match('/\d/', $valeur);`. Ajoutée au catalogue (`ASP_Fonctions`, code `SANS_CHIFFRE`) puis appliquée par Jean-Marc lui-même via trois nouvelles lignes `ASP_Consignes` (Nom, Prénom, Ville) — aucun fichier PHP/JS retouché pour l'étendre à un troisième champ, confirmant une fois de plus le principe du chantier.
|
|
|
|
**7. `consigne_completer_code_postal()` — premier vrai appel IA dans une consigne, avec trace honnête en cas d'échec.** Reprend le contrat existant (`_realisation`, clé réservée déjà en place) et introduit une nouvelle clé réservée symétrique, `_avertissement` — une simple chaîne, affichée immédiatement à l'écran par le front-end sans qu'il sache jamais d'où elle vient. Quand l'IA ne peut rien déduire d'exploitable (ville ambiguë comme Paris, découpée en arrondissements) : une `_realisation` est journalisée dans tous les cas (même en échec — corrige l'anomalie signalée par Jean-Marc : un appel IA sans la moindre trace), et `_avertissement` est en plus renvoyé au front. `moteur_consignes.php` (`executerTraitements()`) extrait les deux clés en parallèle dans un nouveau tableau `$avertissements`, remonté par `appliquerConsignes()` (nouveau paramètre `?array &$avertissements = null`) puis par `contacts.php` dans la réponse JSON (`'avertissements' => $avertissementsIA`).
|
|
|
|
**8. Affichage de l'avertissement côté écran — trois itérations dans la soirée.** D'abord un toast neutre, identique aux autres. Puis rouge et centré comme un vrai message d'erreur (`afficherToast(message, true)`), et le panneau reste en mode édition au lieu de repasser en mode vue après l'enregistrement (`afficherDetailPourId(idContact, avertissements.length ? "edition" : "vue")`) — pour que l'utilisateur puisse corriger tout de suite le champ resté incomplet, `contactCourant` étant malgré tout rafraîchi pour éviter un double POST en cas de nouvelle tentative d'enregistrement. Puis, une fois testé, le message disparaissait encore trop vite et la fiche restait visuellement "non validée" alors qu'elle était bien enregistrée : `afficherToast()` accepte désormais un 4ᵉ paramètre optionnel (`onValider`) qui, s'il est fourni, supprime la disparition automatique et ajoute un bouton "Valider" (nouvelle classe `.toast-bouton`) — le clic ferme le message et exécute `onValider`, ici un simple `basculerModeDetail("vue")` (aucune revalidation, aucun nouvel enregistrement déclenché, le contact étant déjà enregistré depuis le début de la séquence). Le texte de l'avertissement, côté `consignes_fonctions.php`, a été rendu plus impératif ("Saisissez le code postal vous-même.") en cohérence avec ce fonctionnement bloquant.
|
|
|
|
**Version.** `<code>` déjà avancé en tout début de cette session de travail (`V1.14.15` → `V1.15.15`, avant même le badge IA du point 1). `<conception>` avancé en fin de session à `17` (Réflexions stratégiques, nouvelle section 17) — version finale de la session : **V1.15.17**.
|
|
|
|
**Reste à faire, noté en fin de session du 7 septembre :**
|
|
- Le contrat de retour à trois issues (silencieux / besoin de précision / question à poser), envisagé le 6 septembre, reste à concevoir dans son ensemble — ce qui a été construit ce 7 septembre n'en couvre que deux (succès silencieux, échec avec avertissement), volontairement, en attendant un second cas concret qui justifierait la généralisation.
|
|
- Tous les points laissés ouverts le 6 septembre (écran de gestion des consignes, catalogue commercial restreint, accumulation de plusieurs messages d'erreur, consignes transverses `CNS_Champ` NULL, distinction Saisie/Validation) restent entiers, aucun n'a été traité ce 7 septembre.
|