-
6.14
Chantier 19 bis : aucun fichier de passage d'un cabinet ne reste hors de chez lui
Le chantier 19 effaçait tout ce qui est RANGÉ par cabinet ; des temporaires y échappaient, dont des copies de fichiers de clients (44 copies d'import de Ket et 270 résidus relevés sur le poste, effacés).
-
6.13
Tests propres : plus aucun test ne laisse de fichiers sur le poste
37 178 fichiers orphelins s'étaient accumulés sur le poste de dev : des tests effaçaient les lignes de leurs cabinets d'essai, pas leurs fichiers. - ServiceSuppressionEntreprise::fichiersDuCabinet(id) : la liste de TOUS les fichiers d'un cabinet, lue en base — source unique de la purge et des tests. - tests/Support/FichiersDesCabinetsDeTest::effacer() en tête du nettoyage des 7 classes désignées (AnalyserFichierPourSaisieTool, AssistantIaFichier, KetDocumentGeneration, KetFichierClassement, KetSaisieDepuisFichier, RepriseParPaliers, ProductionIntermediaireRubrique). - Garde-fou permanent : l'…
-
6.12
Chantier 19 : supprimer un cabinet efface tout ce qui est à lui, fichiers et lignes
Décision de Serge (avant l'ouverture) : la suppression d'un cabinet ne laisse rien de lui. - SUR LE DISQUE, sept emplacements (avant : logo et pièces seulement) — fichiers joints au chat de Ket et documents qu'il a produits (retrouvés en base AVANT la purge), dépôts et exports de l'échange (<id>/), cache audio de la voix (<id>/). Chaque emplacement est déclaré UNE fois en paramètre (app.dossier_des_fichiers_de_ket, _documents_de_ket, _de_l_echange, _de_la_voix_de_ket), que lisent Vich, le code qui y écrit et la purge. - EN BASE, les tables rattachées INDIRECTEMENT (sans entreprise_id) restaie…
-
6.11
Chantier 18, commit 6 : une pièce ne sort par le relevé public que si elle est partagée
Option (c) de Serge : case « Partagée avec le client » sur la pièce (Document), DÉCOCHÉE par défaut — toutes les pièces existantes naissent non partagées (migration). - Relevé public /soa/{token} : la liste d'une police ne montre que les pièces partagées (SoaPoliceDocumentsCollector::piecesPartagees) ; le téléchargement refuse une pièce non partagée par la même réponse qu'un lien invalide (pieceServieAuClient). Le courtier, lui, voit toujours tout (collectFor). - Écran : la case dans le dialogue d'une pièce (aide + phrase dans la colonne d'explication) ; dans les listes, la mention « Partagée…
-
6.10
Après la purge : identifiants de commit à jour, public/uploads/ ignoré en bloc, logos verrouillés par construction
- L'historique a été réécrit le 2026-10-22 (33 fichiers déposés commités par le passé, dont quatre dumps de base, retirés de toutes les branches) : les identifiants de commit cités dans les skills, le README de déploiement et bin/deploy.sh sont remplacés par les nouveaux (table de correspondance de git filter-repo ; 26 références). - .gitignore : TOUT public/uploads/ est ignoré, verrous (.htaccess) et .gitkeep exceptés — un nouveau sous-dossier ne serait plus suivi. - Décision de Serge : la liste de refus du verrou des logos est un écart accepté (LiteSpeed), À CONDITION que le téléversement s…
-
6.09
Chantier 18 : corrections de la seconde revue
- B1 : cinq gabarits écrivaient l'adresse du logo en dur (images/entreprises/…) — relevé public et interne, aperçus de note, analyse de bordereau. Ils passent par vich_uploader_asset ; un test refuse toute adresse de logo écrite en dur. - C1-C3, app:documents:deplacer : une copie non conforme est retirée de la destination (elle ne bloque plus la relance) ; une empreinte illisible est un échec ; le rapport dit ce qui a VRAIMENT été supprimé ; les fichiers que la base ne désigne pas sont comptés et nommés, laissés en place. - C4 : un retour arrière au-delà du chantier 18 rend les pièces introuv…
-
6.08
Chantier 18, commit 5 : règles inscrites, liste de contrôle de production
- Skill jsbrokers-dev §4 : les fichiers déposés se servent, ne se publient jamais — app.dossier_des_pieces hors de public/, DocumentFichier::chemin(), ReponseDePiece pour toute réponse, NomAleatoire (public), tests/Support/DossierDesPieces, et l'E2E des fichiers sur une instance Apache qui LIT les .htaccess (l'instance habituelle les ignore). §8 : état du chantier 18 (reste le commit 6, case « Partagée avec le client »). - deploiement/README.md : la recette du chantier 18 (rapport du déplacement, disque, aucune URL ne sert une pièce, route contrôlée, logo, sauvegarde de var/uploads chez l'héb…
-
6.07
Chantier 18, commit 4 : les pièces quittent la racine web, les logos ont leur dossier
Décisions de Serge : - PIÈCES (Document) dans var/uploads/documents (app.dossier_des_pieces), HORS de public/ : aucune URL ne les atteint, seules les routes contrôlées les servent (ReponseDePiece). - LOGOS publics (montrés par <img>) mais dans public/uploads/logos (app.dossier_des_logos), séparés des images de marque suivies par git ; .htaccess : images seulement, sandbox, nosniff. Le refus du SVG au téléversement existait (chantier 13) et reste testé. - NOMS ALÉATOIRES pour les nouveaux dépôts (NomAleatoire : 128 bits + extension) ; le nom lisible reste en base. Les fichiers existants garden…
-
6.06
Chantier 18, commit 3 : un fichier déposé ne s'ouvre jamais comme une page de Joseara
Une seule réponse, ReponseDePiece, sert désormais TOUS les fichiers déposés : pièce de l'espace de travail (unitaire et archive), relevé public, et les trois routes de fichiers de Ket. Décisions de Serge : - affichage dans le navigateur seulement s'il est demandé ET si le type RÉEL (lu dans le contenu, pas l'extension) est PDF, PNG, JPEG, GIF ou WebP ; tout le reste en Content-Disposition: attachment, et jamais servi sous un type HTML, SVG ou XML ; - X-Content-Type-Options: nosniff et Content-Security-Policy: sandbox sur chaque réponse, archives ZIP comprises ; - nom d'origine nettoyé (contrô…
-
6.05
Chantier 18, commit 2 : l'emplacement d'une pièce a une source unique
Le dossier des pièces déposées est le paramètre app.dossier_des_pieces, lu par le mapping Vich et par la purge d'un cabinet ; le code n'y accède que par DocumentFichier::chemin(). Quatre chemins « public/uploads/documents » écrits en dur (bordereau : affichage et soumission de l'analyse, PDF d'analyse, commande d'enrichissement) y passent. Rien ne change encore d'emplacement : c'est ce qui permettra de déplacer le dossier hors de public/ en une ligne.
-
6.04
Chantier 18, commit 1 : public/pdfs et sa génération de démonstration supprimés (code mort, décision de Serge)
La route /admin/entreprise/pdf/{id} n'était appelée par aucun écran ; son gestionnaire n'écrivait qu'un « Ceci est juste un exemple » dans public/pdfs/{id}.txt — un dossier servi directement, sans .htaccess versionné. Retirés : route, gabarit, message et gestionnaire, leur routage Messenger, trois traductions, la purge de ce dossier à la suppression d'un cabinet, le test qui exigeait le dossier, et ses mentions dans bin/deploy.sh, .gitignore et le README de déploiement. Les fichiers déjà présents en production seront supprimés par Serge.
-
6.03
Skill jsbrokers-dev : chantier 17 (Tâches) fait, chantier 18 (fichiers déposés hors de public/) inscrit, avant l'ouverture
No further details provided for this update.
-
6.02
Tâches : une frontière de visibilité, posée par le serveur à chaque porte (décisions de Serge)
Un invité ne voit une tâche — et ses retours et pièces — que si son exécutant ou son auteur appartient à son cercle : lui, son responsable, ses assistants directs (un niveau). Les tâches sans exécutant restent communes ; l'administrateur du cabinet (propriétaire ou délégué) voit tout. Hors frontière : 404, jamais un 200 vide ni un refus nommé. Recalculé à chaque requête. Source unique : TacheVisibiliteScope ; lecteur : InviteCourant.
-
6.01
Administrateur du cabinet = le propriétaire ou le délégué (décision de Serge)
« Effacer les filtres » ne retire « Mon portefeuille » que pour un administrateur. Celui-ci n'est plus déduit d'un cumul de droits sur toutes les rubriques (import, validation des congés… rendaient la qualité inatteignable) : c'est le propriétaire ou le délégué « gestionnaire des invités ». Seul le propriétaire supprime le cabinet et achète des tokens.
-
6.00
Listes : une liste vide dit pourquoi, et propose le bon geste (chantier 6)
Le serveur détermine la cause d'une liste vide (CauseDeListeVide) et rend l'état vide, au premier affichage comme à chaque recherche (`etatVide` de la réponse) : — « Aucune donnée » : rien ici ; — « Aucune fiche dans votre portefeuille » + « Afficher tout le cabinet », proposé à TOUS (la chip est un confort de lecture, option A confirmée par Serge) ; — « Aucune fiche ne correspond à vos filtres » + « Effacer les filtres », qui ne retire « Mon portefeuille » que pour un administrateur du cabinet (WorkspaceAccessResolver::estAdministrateurDuCabinet). Les boutons portent les clés à retirer, choi…
-
5.99
Skill jsbrokers-dev : « Mon portefeuille » reste un confort (option A), cloisonnement entre chargés de compte en CIBLE éventuelle
Décision de Serge du 2026-10-21. Et plus de fichier tenu hors des commits : une modification qui n'est pas la nôtre se montre, Serge décide.
-
5.98
Console : le bloc « Supervision » du tableau de bord est testé avec un défaut ouvert
Le 2026-10-08, ce bloc (/console/dashboard/block/supervision) répondait 500 en production — et seulement là : il ne rend sa liste que lorsqu'un défaut est OUVERT, ce que la suite ne semait jamais pour lui. Le test tient désormais les deux états : sans défaut, et avec un défaut ouvert, dont le type doit apparaître dans la liste rendue.
-
5.97
Éditeur riche : Tab sort de l'éditeur, plus de piège clavier (WCAG 2.1.2)
Quill attachait Tab à l'insertion d'une tabulation et à l'indentation des listes : au clavier, on entrait dans l'éditeur sans pouvoir en sortir. Ses raccourcis Tab sont retirés (SANS_TABULATION, texte-riche-regles.js) : Tab et Maj+Tab passent au champ suivant ou précédent, liste comprise. Pas de bouton d'indentation (décision de Serge) : la barre n'en avait pas, et le format n'est pas autorisé.
-
5.96
Dialogues (suivi du chantier 2) : un seul gestionnaire, saisie sans geste protégée
Bordereau : l'analyse injectée dans l'espace de travail installait un second dialog-manager — chaque ouverture créait deux dialogues identiques, donc un risque de double enregistrement. Le second reste désormais inerte (gestionnaire-dialogues.js) ; l'unification reste au chantier 3.
-
5.95
Dialogues et messages : Échap ferme sans rien perdre, une erreur reste (chantier 2)
Échap (règles echap-dialogue.js, un seul gestionnaire dans dialog-manager, en phase de capture, d'après la pile des modales) : ferme d'abord ce qui est ouvert dans le dialogue (autocomplétion, menu, picker, liste « +N »), puis le dialogue du dessus seulement ; rien pendant une saisie Quill, un envoi en cours (Fermer verrouillé), une touche maintenue, ni dans « Aller à la page ». Un formulaire se ferme par dialog-instance#close(), qui demande « Abandonner ? » quand l'empreinte du formulaire a changé — prise au premier geste, création comprise, Quill et fichiers compris ; la question se ferme p…
-
5.94
Recherche des Tâches : plus d'e-mail après F5 (chantier 16, suite)
La boîte de confirmation, incluse sur TOUTES les pages, portait un champ mot de passe caché. Le gestionnaire de mots de passe d'Edge/Chrome le remplissait à chaque chargement et versait l'identifiant dans le dernier champ texte qui le précède : la recherche des Tâches du tableau de bord. `autocomplete="off"` n'y peut rien, et la valeur est cachée au script jusqu'au premier geste — le filet `:autofill` ne pouvait pas l'effacer.
-
5.93
Onglets : chacun retrouve SES filtres, et personne ceux d'un autre (chantier 15)
Routage par onglet. Une réponse lente (chargement ou rafraîchissement d'une liste) partait sous le nom de l'onglet AFFICHÉ à son arrivée : les filtres des Avenants se mémorisaient sur les Tranches, et le F5 les y rejouait (défaut 3, même cause que la fuite). Une réponse appartient désormais à l'onglet qui l'a demandée ; une recherche va à l'onglet qu'elle nomme (routage-onglet.js). Un contexte provisoire (onglet en chargement) ou un filtre restauré pas encore reposé ne se mémorise plus.
-
5.92
Console : le remboursement d'une vente ne part jamais sans confirmation
Décision de Serge (2026-10-20) : verrouiller si c'est important pour la sécurité des données. Ça l'est : un remboursement est irréversible — l'achat passe REFUNDED, les tokens sont retirés du compte du client, un AVOIR est émis (document intangible) et le client est notifié.
-
5.91
Recherche : aucun filtre ne s'applique sans avoir été tapé (chantier 16)
Chrome et Edge prenaient les champs de recherche anonymes pour un identifiant et y collaient l'adresse e-mail de l'utilisateur : la liste des tâches du tableau de bord se filtrait sur ce texte, à son insu (vu en production par Serge le 2026-10-20). Inventaire : 29 champs de recherche ou de filtre (28 gabarits, 1 JS), aucun entièrement protégé.
-
5.90
Fichiers déposés : jamais exécutés sur joseara.com (chantier 13)
Deux dossiers publics reçoivent des fichiers d'utilisateurs, servis par le serveur web — donc hors PHP et hors de la politique de sécurité de l'application. Un .html ou un .svg piégé y aurait été ouvert depuis joseara.com, ses scripts lisant la session du cabinet.
-
5.89
Icônes : les 31 nommées dans le code sont figées dans le dépôt, plus d'Iconify au rendu
Une icône absente de assets/icons est téléchargée depuis api.iconify.design AU MOMENT DU RENDU : si Iconify ne répond pas, la page tombe en 500 (vu deux fois en test le 2026-10-19 : « Could not resolve host: api.iconify.design », lucide:users et mdi:account-arrow-right-outline). IconInvocationsVendoriseesTest ne lisait que les GABARITS ; les noms fournis par le CODE (canevas de liste et de formulaire, IconExtension, IconController) lui échappaient. Décision de Serge (2026-10-20) : figer les icônes dans Joseara.
-
5.88
CSP (6/6) : la politique de sécurité est APPLIQUÉE
Le navigateur refuse désormais d'exécuter tout script que Joseara n'a pas prévu : même si une donnée piégée échappait un jour à l'échappement, elle ne s'exécuterait pas.
-
5.87
Réseau : une lecture coupée en route est retentée une fois
Production, 2026-10-19 : des requêtes coupées en plein vol (ERR_CONNECTION_RESET, ERR_CONNECTION_CLOSED) sur des adresses différentes à chaque chargement — un onglet restait vide, le suivi de Ket échouait. Le serveur n'avait rien refusé : la connexion était coupée. Décision de Serge (2026-10-20) : livrer la reprise.
-
5.86
Recherche : un critère inconnu de la rubrique est écarté, plus de liste en 500
Supervision de production (2026-10-20, 7 occurrences depuis le 19) : « Class App\Entity\Tranche has no field or association named __echeance_avenant__ ». Le chip d'échéance des Avenants était parti dans une requête sur les Tranches ; la porte « champ simple » du moteur passait le nom tel quel à Doctrine — erreur de sémantique, liste entière en 500.
-
5.85
CSP (5/6) : les règles inscrites dans les skills
Les garde-fous sont en place depuis les commits 1 à 4 (PolitiqueCspTest, JournalCspTest, AdresseDuNavigateurTest, GestesSansCodeTest). Ce commit écrit les règles là où un écran neuf les trouvera :
-
5.84
CSP (4/6) : le tableau de bord sans code dans le HTML — plus aucun on…= nulle part
Les blocs du tableau de bord appelaient les fonctions window.db… d'assets/app.js par 63 attributs onclick/oninput/oncontextmenu, et app.js fabriquait 3 lignes de production avec un onclick : tout cela serait refusé par la politique appliquée.
-
5.83
CSP (3/6) : les 47 gestes génériques sans code dans le HTML
La politique de sécurité refusera tout code écrit dans un attribut (onclick="…") : c'est ce qu'une donnée piégée y glisserait. Les gestes génériques passent par des data-action.
-
5.82
Dépendances JavaScript : importmap:audit à zéro
Après le chantier 12 (Composer, 60 avis → 0), l'audit des paquets JavaScript (importmap) signalait encore 3 High, 1 Medium, 2 Low.
-
5.81
CSP (2/6) : plus aucune ressource d'un autre site
La politique de sécurité n'autorise plus aucun hôte externe : tout ce que la page charge vient de joseara.com. Les pages n'attendent plus jsDelivr (2 à 5 s mesurées au poste).
-
5.80
CSP (1/6) : politique en observation, nonce par requête, collecte des violations
Dernier filet contre une faille d'échappement : le navigateur n'exécutera que nos fichiers et les <script> inline qui portent le nonce de la réponse. En Report-Only tant que les gestionnaires on…= ne sont pas convertis (commits 3-4) et que Serge n'a pas parcouru la liste de contrôle (commit 6).
-
5.79
Recherche : on cherche le texte saisi, plus le HTML de l'éditeur
Un champ riche (24 champs, registre ChampsEnTexteRiche) est stocké en HTML. La recherche faisait son LIKE sur ce HTML : « strong » ou « <p> » trouvaient la fiche, « Relancer le client » ne la trouvait pas quand une balise coupait la phrase, « A & B » non plus (stocké &), et une image collée (base64) répondait à des lettres au hasard.
-
5.78
Publication : plus de « git pull » — deploy.sh se met à jour lui-même
L'INCIDENT (2026-10-19). `publier` s'est arrêté : « Your local changes to public/.htaccess would be overwritten by merge ». L'alias faisait `git pull` avant deploy.sh ; or le .htaccess du serveur porte TOUJOURS les blocs de l'hébergeur (remis par deploy.sh à l'étape 4) — le premier commit touchant ce fichier (381e35da) a bloqué le pull. Déblocage sûr : `cd ~/joseara && bash bin/deploy.sh` (deploy.sh sauve les blocs, fetch, reset, les remet).
-
5.77
Alertes « Untrusted Host mail.joseara.com » : la cause est traitée, plus de faux e-mails
LA CAUSE. cPanel sert mail.joseara.com depuis le dossier du site. En production, Symfony n'accepte que joseara.com et www.joseara.com (trusted_hosts : parade à l'empoisonnement d'en-tête Host). Chaque visite de mail.joseara.com (robots, clients de messagerie) levait « Untrusted Host », journalisé en ERROR : la chaîne d'alerte l'envoyait par e-mail, et le dédoublonnage ne couvre que cinq minutes — d'où l'alerte qui revenait sans cesse.
-
5.76
Recherche : un « % » ou un « _ » tapé est du texte, plus un joker — sur TOUS les champs
Le terme saisi entrait tel quel dans le motif du LIKE : « 100% » trouvait « 1000 », « A_B » trouvait « AxB ». Ce changement touche toutes les recherches du service, champs simples comme riches.
-
5.75
Symfony 7.1 -> 7.4 LTS : plus aucun avis de sécurité sur les dépendances
composer audit : 26 avis -> 0. Symfony 7.1 n'était plus maintenu : 24 avis n'y auraient jamais été corrigés (contournement d'autorisation par PATH_INFO, pare-feu, injection CRLF dans une adresse d'e-mail…). 7.4 est une LTS, maintenue en sécurité jusqu'en 2029. PHP 8.2 suffit (check-platform-reqs) ; hors Symfony, seul doctrine/dbal bouge (3.9 -> 3.10).
-
5.74
Erreurs : le texte d'une panne technique ne s'affiche plus, le refus du métier si
Treize catch relayaient $e->getMessage() à l'écran. Prouvé : l'aperçu d'un bordereau dont le classeur manque affichait « File "C:\JSBrokers_1_0_3/public/uploads/documents/…" does not exist » — le chemin du serveur, chez le courtier.
-
5.73
Dépendances : 34 avis de sécurité corrigés sans changer de version de Symfony
composer audit : 60 avis -> 26 (tous restants sur la branche Symfony 7.1, en fin de maintenance : chantier 12-B, montée en 7.4 LTS).
-
5.72
Échappement, fin : le localStorage relu n'est plus cru sur parole ; skills à jour
- Icônes : le cache persistant (jsb-icon-v1::) était réinjecté en innerHTML à chaque page, sans repasser par le serveur. Une seule porte, icone-memorisee.js (cerveau et les deux circuits de bordereau-analysis) : un SVG inerte, sinon l'entrée est effacée et l'icône redemandée. Clés inchangées : le cache existant reste valable. - Onglets : loadUrl et sourceUrl relus du stockage sont fetchés puis injectés en HTML ; url-de-confiance.js n'accepte que notre origine. - Infobulle d'attribut (tooltip_controller) : textContent. Les descriptions des canevas sont du texte (vérifié : aucune balise). - Ski…
-
5.71
Texte riche : assaini à l'écriture, lu comme du texte partout ailleurs
Tout <textarea> d'un dialogue est un éditeur Quill : ces champs stockent du HTML, qui était enregistré puis réaffiché tel quel. Un <img onerror> collé ou posté s'exécutait à l'ouverture de la fiche.
-
5.70
Reversements : corriger un versement ne perd plus sa description
Le picker de reversement n'envoie aucune description — il n'en propose pas. Le serveur la réécrivait pourtant avec ce qu'il ne recevait pas (`$donnees['description'] ?? null`) : chaque correction d'un versement par le picker (une date, un montant) effaçait la description saisie ailleurs, sans un mot.
-
5.69
Cloisonnement : un identifiant d'autrui est introuvable, ni avalé ni nommé
Chantier d'échappement, C5.
-
5.68
Tableau de bord, espace de travail, recherche : une donnée saisie n'y devient plus du HTML
Chantier d'échappement, C4.
-
5.67
Déploiement : les dépendances se comparent à vendor/, plus à l'historique git
Incident du 2026-10-17 : le déploiement de 6a2c0802 a échoué au cache:warmup (« RateLimiter component is not installed ») et le site est resté en maintenance.
-
5.66
Formulaire de contact : ni relais d'e-mails, ni porte ouverte aux robots
Chantier d'échappement, C3. Un visiteur sans compte choisit l'adresse où Joseara envoie l'accusé de réception : sans garde, le formulaire relayait sous notre signature le texte de son choix, à qui il voulait, autant de fois qu'il voulait.
-
5.65
Toasts et confirmations : un texte reste du texte, une seule fonction d'échappement
Chantier d'échappement, C2.
-
5.64
E-mails et pages du serveur : une donnée saisie n'y devient plus du HTML
Chantier d'échappement, C1.
-
5.63
Console : un sujet de ticket ou un nom de cabinet ne s'exécute plus chez l'agent
XSS stockée, du dehors vers la console Joseara — correctif d'urgence.
-
5.62
Skill jsbrokers-dev : deux valeurs en dur de plus au chantier 5
- `color:#0d2440` hors charte, en style inline, sur 11 titres et libellés du picker de facturation (_facturation_picker.html.twig). - Format de date `d/m/Y` écrit en dur sur la carte de piste (_carte_piste.html.twig:40), au lieu de format_datetime.
-
5.61
Skill jsbrokers-dev : huit chantiers de développement inscrits, sans code
Décisions de Serge du 2026-10-16, sur l'inventaire fait pour jsb-construire-rubrique :
-
5.60
Skill jsb-construire-rubrique : construire une rubrique, du cadrage à la livraison (lot 1)
Une skill qui dit dans quel ordre travailler et va chercher la règle au bon moment, sans répéter jsbrokers-dev, courtage-assurance, reglementation-rdc-arca, ergonomie-web ni charte-couleurs.
-
5.59
Skills : corrections après relecture
- Les trois en-têtes perdent `trigger when` (clé non standard) : les mots-clés de déclenchement passent dans `description`. - courtage-assurance §2 : le règlement d'une note n'a pas d'outil Ket dédié, mais il est couvert — `preparer_operations` crée le Paiement (CouvertureDesEcrans, motif écrit). « Aucun outil Ket pour l'encaissement d'une note » contredisait le manifeste. - jsbrokers-dev §4 : tout contenu dynamique est échappé (Twig par défaut, textContent en JS, jamais |raw ni innerHTML sur une donnée saisie, infobulles comprises). Incident : les infobulles du tableau de bord (300ed88a). Le…
-
5.58
Skill réglementaire : homologation DGI des factures, chantier décidé
La DGI permet de raccorder le module de facturation à sa plateforme pour homologuer chaque facture (QR code, références fiscales). Décision de Serge du 2026-10-16 : intégration de l'API DGI, chantier ultérieur. Il remplace la question « facture normalisée : à confirmer ».
-
5.57
Conservation : l'écriture du ZIP devient testable, et l'échec de la fermeture est prouvé
Le correctif « archive vide = remise échouée » (2011ff8b) n'avait pas de test : il fallait faire disparaître un fichier pendant la fermeture du ZIP.
-
5.56
Conservation : la moindre pièce comptable exige l'archive, justificatifs compris
Décisions de Serge du 2026-10-16 : un cabinet qui porte la moindre pièce comptable ne se supprime pas sans archive comptable préalable, remise au cabinet (export téléchargeable).
-
5.55
Notes : app:notes:figer dit ce qu'elle compare ; avoir multi-échéances inscrit
- app:notes:figer distingue désormais deux chiffres. Le CONTRÔLE (figé − recalculé juste avant le figeage) doit valoir 0,00 (✓) ; un écart (⚠) bloque la confirmation. Le RESTE À ENCAISSER (montant − paiements) est une information : une note partiellement réglée se fige telle quelle. À blanc, le figeage se fait en mémoire et n'est pas écrit, si bien que le contrôle se lit avant de confirmer. - courtage-assurance §7 point 7 : l'avoir sur une police à plusieurs échéances, chantier connu (écart mesuré au niveau du revenu), avec sa requête de mesure en lecture seule à relancer régulièrement. Rien…
-
5.54
Skills : règle OHADA des notes acquise (numéro, intangibilité, conservation)
courtage-assurance §5 : trois invariants acquis — numérotation continue attribuée à la validation (remplace celle du 2026-10-14), intangibilité d'une note validée (montants figés, avoir seule correction, écart à facturer / à créditer, note de reprise supprimable entière), conservation dix ans (archive avant toute purge). La règle « note réglée » vise désormais les brouillons et les notes de reprise. La mention « en attente du comptable » sur la numérotation est retirée ; le chantier 6 est fait.
-
5.53
Notes : les pièces se conservent dix ans ; archive avant toute purge
Règle OHADA (Acte uniforme relatif au droit comptable), décision de Serge du 2026-10-15 : rien ne purge une note émise. La suppression d'un cabinet entier n'est permise qu'APRÈS la remise de son archive comptable, à jour.
-
5.52
Notes : une note validée est intangible ; l'écart se facture ou se crédite
Règle OHADA, décision de Serge du 2026-10-15 : une note validée ne se modifie plus et ne se supprime pas. Toute correction passe par la note contraire (avoir), puis par une nouvelle note si besoin.
-
5.51
Notes : les montants se figent à la validation (règle OHADA)
Le montant d'une note n'était stocké nulle part : il se recalculait à chaque lecture, et une prime, un taux ou un chargement corrigés après coup changeaient une facture déjà envoyée — son PDF, son solde, son écriture au journal. Décision de Serge du 2026-10-15 : une note validée est intangible.
-
5.50
Notes : le numéro définitif naît à la validation (règle OHADA)
Pratique comptable OHADA (SYSCOHADA révisé), décision de Serge du 2026-10-15 : chaque note validée porte un numéro unique, chronologique et continu dans sa série (ND-/NC-AAAA-NNNNN, par cabinet, année d'émission et type), attribué au moment où la validation fait naître la créance.
-
5.49
Commission : l'assiette est la somme des lignes du chargement cible
Une cotation peut porter plusieurs lignes du même chargement (deux « prime nette ») : l'écran ne comptait que la première, Ket les additionnait déjà. La commission porte désormais sur leur somme, partout (10 000 + 4 000 à 15 % : 2 100, et non 1 500).
-
5.48
Commission : un type neuf s'adosse au taux du risque
Le formulaire d'un nouveau type de commission s'ouvrait sur 10 % ET le drapeau « taux du risque ». Le pourcentage du type passant avant le risque (TarifDuRevenu), le drapeau restait sans effet tant que les 10 % demeuraient. Décision du 2026-10-15 : le type neuf s'ouvre sans pourcentage, drapeau coché, et facture le taux du risque ; le courtier qui veut un taux fixe le saisit.
-
5.47
Commission : une seule règle de tarif, l'ordre des écrans fait foi
TarifDuRevenu devient la source unique du tarif d'une commission, dans l'ordre des écrans et de la facturation : taux du revenu, forfait du revenu, pourcentage du type, forfait du type, puis taux du risque si le type s'y adosse. Les écrans, la description du calcul, l'annonce de Ket et la colonne de tarif de l'export du portefeuille la lisent tous.
-
5.46
Sinistres : le sélecteur des modèles de pièces ne montre que le cabinet
Règle posée par Serge le 2026-10-15 : un sélecteur n'affiche JAMAIS les données d'un autre cabinet. La nature d'une pièce de sinistre se choisissait parmi les modèles de pièces de TOUS les cabinets (noms et descriptions) ; l'écriture refusait déjà un modèle étranger, la liste non.
-
5.45
Commission : une taxe ne sert jamais d'assiette, et le chargement cible reste du cabinet
Chantier « Assiette et source unique de la commission », étape 1 (écart 1 ; décision du 2026-10-15). L'Art. 2 du barème ARCA exprime les taux sur la prime nette, hors impôts et taxes ; un type de revenu pouvait pourtant viser la TVA ou l'ARCA.
-
5.44
Skills : Art. 2 en lecture étroite, chantier prioritaire sur l'assiette de la commission
Décisions de Serge du 2026-10-15. - reglementation-rdc-arca : la base de la commission est la prime nette seule (hors fronting, frais accessoires et taxes), pratique du marché ; le réglage par défaut est conforme — à confirmer auprès de l'ARCA en cas de contrôle. - courtage-assurance : chantier PRIORITAIRE « Assiette et source unique de la commission » en tête des chantiers (invariant « une source unique par montant ») : taxe interdite comme chargement cible, une seule règle de priorité (l'ordre des écrans fait foi), commission sur la somme des lignes du chargement cible ; historique à tranch…
-
5.43
Skill réglementaire : assiette de la commission (Art. 2) et chantiers ARCA
Vérification en lecture seule, rien corrigé.
-
5.42
Skill réglementaire : barème ARCA 005/24 en référence, constat des 43 risques semés
- references/bareme-arca-005-24.md : transcription du Règlement n° 005/24 (taux minima et maxima Vie et IARD, règles d'application), fournie par Serge ; à relire contre le texte officiel avant tout usage dans le code. - §6 : le barème est référencé ; il n'est pas implémenté (seuls les maxima sont semés, sans minima, contrôle de fourchette ni régimes des Art. 3 à 6). - Constat, sans correction : les 43 taux de risques_defaut.json égalent un à un les maxima du barème de 2024 (aucun hors fourchette, correspondance 1 pour 1) ; aucun indice d'un barème antérieur, sans pouvoir exclure des valeurs c…
-
5.41
Skill courtage-assurance : trois chantiers ultérieurs, numérotation à confirmer
- Chantiers ultérieurs signalés le 2026-10-15 (non décidés) : badge « Mon portefeuille » incomplet ; trois chemins de documents manquants sous « Mon portefeuille » (55 jointures sur un plafond de 61) ; plan Ket en deux temps refusé à l'analyse. - À confirmer : numérotation des notes sans trou (Serge consulte son comptable) — ne rien modifier avant sa réponse.
-
5.40
Notes : la page de démonstration /admin/note/test est supprimée
Elle ne servait qu'à elle-même : la route admin.note.test, son gabarit components/note/editor.html.twig et le contrôleur Stimulus invoice_editor, que rien d'autre n'utilisait. Elle portait un sélecteur de monnaie CDF/USD factice, contraire à la monnaie de travail unique.
-
5.39
Monnaies : une seule monnaie de travail par cabinet, aucun montant converti
Décisions du 2026-10-14 et du 2026-10-15. La prémisse « convertir l'existant » est tombée : aucun montant ne porte sa monnaie, et chaque formulaire affichait la monnaie d'affichage à côté du champ pendant la saisie — l'existant y est donc déjà. Rien n'est converti.
-
5.38
Mon portefeuille : ce qui descend d'une piste suit son responsable
Décision du 2026-10-14. Cotations, polices, tranches, paiements de prime, tâches, retours et documents rattachés à une piste suivent désormais le RESPONSABLE de la piste (ResponsableDePiste : gestionnaire du portefeuille du client, sinon créateur de la piste), comme la piste elle-même. Avant, une piste montée pour un client sans portefeuille était dans « Mon portefeuille » de son créateur, sans sa cotation ni sa police. Ce qui ne dépend que du CLIENT (client, sinistres, document du client) garde la règle du gestionnaire. La règle ne fait qu'ajouter : personne ne perd ce qu'il voyait.
-
5.37
Constante : 325 méthodes mortes supprimées ; Ket ouvre le PDF de la note
Décisions du 2026-10-14. Le dernier grep a démenti la prémisse du nettoyage : la chaîne getIdPoste() n'était pas seulement morte, elle servait l'ancien état TCPDF (EtatsController), prouvé en 500 pour tout destinataire (Article::getMontant() et getIdPoste() n'existent plus), et l'export « note_pdf » / « bordereau_pdf » de Ket y menait.
-
5.36
Notes : un numéro unique dans le cabinet, et chronologique
Décisions du 2026-10-14. La référence d'une note était 'N' . time() : ni ordre d'émission, ni unicité, aucun contrôle.
-
5.35
Notes : une note réglée ne se supprime pas et ne perd pas de ligne
Décisions du 2026-10-14. Une note qui a reçu au moins un paiement est une pièce comptable acquise : elle s'annule par la note contraire (une note de débit par une note de crédit, et inversement), jamais par effacement.
-
5.34
Skills métier : les réponses du 2026-10-14 intégrées
- Numérotation des notes : référence générée par le produit, unique et chronologique par cabinet, champ éditable (unicité contrôlée). - Monnaie unique : l'existant saisi dans une autre monnaie sera converti dans la monnaie d'affichage. - Branche VIE : ARCA 2 % confirmé, pas de TVA — des défauts, éditables par cabinet. - Principe : le produit définit des comportements PAR DÉFAUT, jamais des valeurs imposées.
-
5.33
Skills métier : courtage-assurance et reglementation-rdc-arca
courtage-assurance : glossaire terme → entité, cycle de vie piste → police → facturation → encaissement → rétrocession, tableau type de mouvement → dates → chemin → prime, invariants métier et comptables avec preuves (fichier:ligne) et tests ; invariants CIBLES décidés le 2026-10-14 (numérotation des notes — prioritaire —, monnaie unique, « Mon portefeuille » étendu, note réglée non supprimable) avec état actuel et écart ; chantiers décidés.
-
5.32
Skill jsbrokers-dev : les règles de développement du dépôt
BIC Cristal, cloisonnement entre cabinets (AppartenanceAuCabinet, 404 sans nommer), source unique et parité écran ↔ Ket, « le serveur écrit, le navigateur clone », procédure de test (PHPUnit dossier par dossier, une suite à la fois, mocksess, node, E2E), livraison et mode Plan. Règle d'usage : tout plan qui touche le métier consulte d'abord courtage-assurance (et reglementation-rdc-arca) et cite l'invariant qu'il préserve.
-
5.31
Listes : la dernière demande fait autorité, pas la dernière réponse
Deux rafraîchissements du même onglet partaient en parallèle et s'affichaient dans l'ordre d'ARRIVÉE. Rejeu bridé (CPU ×6, Slow 3G) de Liste → Cartes → Liste → Cartes à 200 ms : la vue finissait en Liste alors que le dernier clic demandait Cartes (2 rejeux sur 3).
-
5.30
Cookies : une seule façon de les écrire et de les lire
controllers/cookies.js : cookieAEcrire (valeur encodée, tout le site, un an, SameSite=Lax) et lireCookie (décodée ; REPLI sur le préfixe « 9111986 » qu'ajoutait l'ancien saveCookie). La vue Cartes s'y appuie au lieu de son propre code.
-
5.29
Ancien tableau de bord : la page autonome est retirée
L'action `index` d'EntrepriseDashbordController (page à formulaire de dates, JSBTableauDeBordBuilder) n'était plus servie par aucun lien et répondait 500 ; l'espace de travail et ses blocs la remplacent. Retirés : l'action, le constructeur de tableau, CriteresRechercheDashBordDTO, RechercheDashBordType, templates/admin/dashbord/** et le menu segments/menu_entreprise_dashbord (inclus nulle part). La route répond désormais 404 ; les tests de bascule de cabinet le constatent, et vérifient toujours que rien n'est écrit.
-
5.28
Vue Cartes : la Σ des Transformées se calcule par paquets, et s'arrête au plafond
La colonne additionnait la commission de TOUTES les polices souscrites du filtre, par getIndicateursGlobaux — qui calcule aussi tout le reste. Mesuré sur 1 000 polices : ≈ 7,0 s la réponse de la vue Cartes.
-
5.27
Pistes : « Mon portefeuille » = les pistes dont je suis le responsable, partout pareil
Le responsable d'une piste : le gestionnaire du portefeuille de son client, sinon son créateur. La carte le disait ; la liste, Ket et la vigie ne retenaient que le gestionnaire — une piste montée pour un client sans portefeuille portait le nom de son créateur sur sa carte et disparaissait de « Mon portefeuille » de ce même créateur.
-
5.26
Avenants : un seul chemin par type — le mouvement de la police adopte la piste dérivée
Renouvellement, prorogation, annulation, résiliation : la piste dérivée et sa proposition, montées et négociées à part, ne se doublent plus. La carte du pipe (« Renouveler », « Proroger », « Annuler la police », « Résilier la police »), le bouton de ligne des cotations et Ket ouvrent le MOUVEMENT de la police de base, qui les ADOPTE :
-
5.25
Avenants : une souscription sans prime n'a pas de sens, et le serveur le garantit
Le premier avenant d'une cotation exige une prime chiffrée quand le type de la piste crée, reconduit ou étend la garantie (souscription, prorogation, renouvellement). L'incorporation, l'annulation et la résiliation en sont exemptées ; un avenant suivant reste toujours permis. Une table, une garde (DefautsDeSouscription), pour tous les chemins :
-
5.24
Note : la création par le dialogue ne tombe plus en 422 « Référence obligatoire »
Créer une note depuis son dialogue échouait à chaque fois. La référence, « Générée automatiquement », est un champ DÉSACTIVÉ : il n'est jamais soumis. La colonne est NOT NULL, et ControllerUtilsTrait::handleFormSubmission contrôlait les champs obligatoires AVANT le beforePersist qui posait la référence. Le défaut précède le chantier de la vue Cartes : il a été reproduit à l'identique au commit d67fbd91 (worktree temporaire).
-
5.23
Pistes : « Voir les transformées » et « Voir dans la liste » disent où ils mènent
Deux liens qui mènent à la vue Liste portent désormais l’icône de la bascule « Liste » (action:vue-liste), devant leur libellé. Au repos : un lien cobalt au soulignement fin, cible de 44 px. Au survol : une pastille --bg-cobalt-subtle, le texte en --cobalt-dark, l'icône qui avance de 2 px ; anneau de focus cobalt au clavier ; aucun mouvement sous prefers-reduced-motion. Jetons de la charte uniquement. Dans la colonne terminale, le lien se centre sur l'axe du texte qu'il suit.
-
5.22
Pistes et collections : ce qui est enregistré se voit, et le pipe se lit d'un survol
Dix retours d'usage sur la vue Cartes et les collections, chacun diagnostiqué avant correction.
-
5.21
Pistes : la vue Cartes — le pipe commercial en colonnes, avec la prochaine action
La liste principale des Pistes s'ouvre désormais sur une vue Cartes (bascule « Vue : Cartes | Liste ») qui répond à une seule question : où en est chaque piste, et quelle est la prochaine action ?
-
5.20
Pistes : quatre étapes du pipe, un chip qui les montre, et un avenant souscrit pareil partout
- Les étapes, déduites des cotations (aucune n'est stockée) : À coter → En cotation → Offre chiffrée → Transformée ; « En cours » réunit les trois premières. Source unique : PisteTransformationScope (ETAPES, etapeDe, un seuil de prime chiffrée — Σ chargements ≥ 0,005, soit ROUND(Σ, 2) > 0, que DQL ne sait pas écrire). - SQL : JSBDynamicSearchService::predicatsDEtapePiste() (T souscrite, C prime chiffrée, K au moins une cotation), en EXISTS corrélés ; le chip les combine. - PHP : indicateurs etapePipe / etapePipeLibelle / etapePipeNiveau de chaque ligne. - Chip « Statut » : À coter · En cotati…
-
5.19
Vue Liste : sept irrégularités corrigées, dont deux chips qui se vidaient en silence
- Chips « En cours » (Pistes) et « En attente » (Cotations) : le filtre était un `NOT IN (SELECT IDENTITY(...) FROM Avenant …)` borné à aucun cabinet. Une police sans cotation ou une cotation sans piste, n'importe où sur la plateforme, y mettait un NULL et vidait les deux chips dans TOUS les cabinets. Réécrits en EXISTS corrélés ; une cotation sans piste non souscrite redevient « en attente », comme le dit sa fiche. - Premier rendu de la liste : `isPrincipalTab` testait un préfixe qu'aucun gabarit ne produit — l'annonce initiale était du code mort, et la sélection ne revenait pas après F5 (co…
-
5.18
Un identifiant venu de la requête ne désigne plus rien hors du cabinet ouvert
mayAccessEntity() ne juge que le TYPE d'entité ; aucun SQLFilter ni Voter ne bornait les find() au cabinet. Cinq formes d'un même défaut restaient ouvertes :
-
5.17
Facturer la commission : dire ce qui reste à encaisser, et sur quelle note
Une commission entièrement portée par une note mais réglée en partie (11,60 facturés, 8,00 encaissés) était annoncée « 3,60 à collecter » par la liste, la fiche et Ket, tandis que la fenêtre de facturation répondait « Rien à facturer », sans un mot du reste dû. Elle avait raison sur le fond : émettre une seconde note serait une double facturation. Mais elle taisait la créance et n'offrait aucune suite.
-
5.16
Clôturer sans avoir une échéance sans rien à créditer, et tous les lecteurs prouvés
- Nouvelle voie de régularisation. Une échéance « à régulariser » sur laquelle rien n'a été facturé n'avait aucune issue : l'avoir n'a rien à créditer. « Clôturer sans avoir » (avec confirmation) la fait passer « Régularisée », sans note ; « Rouvrir la régularisation » revient en arrière, et l'abandon du mouvement restitue l'état d'avant comme pour le reste. Clôture refusée (409) dès qu'une note de commission ou un avoir existe. - Indicateur regularisationVoie (avoir | cloture | avoir_emis | cloturee) : l'action « Régulariser par note de crédit » n'est offerte que si une commission a été fact…
-
5.15
Trois défauts d'interface relevés au contrôle dans le navigateur
- Boîte de mouvement : l'aperçu initial (date par défaut) et celui de la date saisie partaient presque ensemble ; si le premier répondait en dernier, il recouvrait le second et l'aperçu ne disait plus ce que le champ allait écrire. Seule la dernière demande s'affiche désormais. - Barre d'outils : initialize() passe deux fois (Stimulus, puis connect()) et posait deux écouteurs app:context.changed, dont disconnect() n'ôtait que le second. Le survivant touchait une barre détruite (TypeError « reading 'afficher' »). Une seule fonction liée. - Fenêtre de facturation en note de crédit : elle parlai…
-
5.14
Déploiement : le rattrapage des échéances n'est plus que simulé
bin/deploy.sh ne lance plus app:echeances:regulariser en mode réel. Après les migrations, il joue seulement --rattrapage --dry-run, en affiche le récapitulatif (police, échéance, prime, acte en cause) et donne la commande à lancer pour appliquer. Le mode réel se lance à la main, après lecture. En répétition à blanc (migrations non jouées), la simulation est seulement annoncée. Un échec de la simulation est signalé sans interrompre le déploiement.
-
5.13
Régulariser une échéance sans objet, et rattraper les résiliations déjà enregistrées
- Actions sur une échéance « à régulariser » (barre, clic droit, volet) : « Régulariser par note de crédit » ouvre la fenêtre de facturation en type crédit, sur ce qui a déjà été facturé — une fois l'avoir émis, l'échéance se lit « Régularisée » ; « Déclarer due quand même » la remet dans le recouvrement (prime acquise à l'assureur), réversible par « Remettre à régulariser ». Gestes refusés (409) s'ils ne correspondent plus à l'état réel. - Fenêtre de facturation : paramètre type=credit, transmis au rechargement et à l'envoi. Corrige au passage la perte des échéances au changement de destinat…
-
5.12
Une échéance « à régulariser » sort du recouvrement, partout, et reste visible sur sa fiche
Un seul point d'application : TrancheIndicatorStrategy. Ses montants ne changent pas, mais ce qu'on en réclame tombe à zéro (solde de prime, solde de commission, commission et rétro exigibles) ; le statut devient « À régulariser » (« Régularisée » une fois la note de crédit émise), l'urgence un badge neutre, et la fiche explique pourquoi et quoi faire — avec la commission déjà facturée, nommée.
-
5.11
Une annulation ou une résiliation met « à régulariser » les échéances qu'elle rend sans objet
Après une résiliation en cours de période, le recouvrement continuait de réclamer la prime de l'année entière. Modèle et règle (les lecteurs suivent dans le commit suivant) :
-
5.10
Un sinistre bloque l'abandon d'un mouvement, et la boîte de mouvement tient sur téléphone
- Abandon d'un mouvement (règle unique LiensProteges::refusDeSuppression, écran et Ket) : refusé aussi quand l'avenant qui en est issu porte un sinistre — même référence de police, survenu pendant sa période. Un sinistre doit toujours porter une police existante. Un sinistre de la police de base, sur sa propre période, ne bloque rien. - Boîte de mouvement : le libellé du champ date est visible, au jeton de la charte des titres de champ (.field-label, #343a40, contraste ≈ 11:1). - Socle des boîtes : sous 576 px, le pied passe à la ligne — l'aide sur toute la largeur, les boutons dessous. Sur u…
-
5.09
Tableau de bord : le portefeuille en vigueur et la production de l'exercice ne se confondent plus
Les totaux ne lisent plus le statut de renouvellement pour dire une date : une police échue jamais renouvelée restait comptée à vie, et une police renouvelée s'additionnait à son successeur.
-
5.08
Abandonner un mouvement n'efface jamais de l'argent, et la boîte de mouvement gagne en lisibilité
- Règle unique LiensProteges::refusDeSuppression() : supprimer l'opportunité dérivée d'un mouvement est refusé, avec un message qui nomme l'avenant et les écritures, dès qu'un avenant qui en est issu porte un encaissement de prime, une note émise ou un encaissement de note. Sinon l'abandon est permis, même pour un renouvellement abouti. Appliquée par SuppressionEnCascade::planifier() (annonce et exécution, écran et Ket), et vérifiée AVANT toute dissociation à l'écran (409) et dans l'abandon de Ket, pour qu'un refus ne laisse aucun lien coupé en mémoire. Ket et l'écran disent désormais la même…
-
5.07
Un renouvellement ne crée plus d'avenant né expiré, et le statut de la police suit ses mouvements
Le signalement « un avenant renouvelé reste Échu » : la police de base sortait bien des échus, mais son successeur, calculé au lendemain d'une échéance vieille de 18 mois, naissait lui-même expiré et y entrait à son tour.
-
5.06
Une conversation prend le titre de sa première vraie question
« CONV#78 » cède la place, dès l'envoi de la première vraie question, à un titre qui s'en inspire — et l'utilisateur peut toujours le renommer ensuite. Les deux griefs qui avaient fait abandonner l'ancien titre (80 premiers caractères, une phrase entière dans l'onglet) sont tenus : le titre est débarrassé de ses formules de politesse et tient en 40 caractères, « … » compris.
-
5.05
Ket accueille une conversation vide par un bonjour, et ne déroule plus le programme du jour
Chaque nouveau fil s'ouvrait sur le programme du jour complet — jusqu'à sept tableaux avant même la première question. Une conversation vide s'ouvre désormais sur un accueil bref, à la manière des assistants grand public : « Bonjour, Serge » (« Bonsoir » de 18 h à 4 h 59, dans le fuseau APP_TIMEZONE nommé explicitement), une phrase tirée au hasard, et jusqu'à trois suggestions qui PRÉ-REMPLISSENT le champ sans rien envoyer ni écraser un brouillon.
-
5.04
Le contrôle du .htaccess ne confond plus une panne d'outil avec une différence
LE CORRECTIF DE LA VEILLE A REFUSÉ À TORT. Il comparait le fichier sans blocs à celui du dépôt par « diff » sur deux substitutions de processus. Celles-ci passent par /dev/fd, ABSENT sous CloudLinux (CageFS isole chaque compte) : diff a échoué (« /dev/fd/63: No such file or directory »), et son échec a été lu comme une différence — « modifié HORS des blocs de l'hébergeur », alors que le fichier ne l'était pas.
-
5.03
Le déploiement garde les blocs que cPanel écrit dans .htaccess, et refuse un PHP trop ancien
LE 2026-10-08, « publier » S'EST ARRÊTÉ NET : cPanel avait ajouté son bloc de gestionnaire PHP dans public/.htaccess, fichier versionné, et le garde-fou « fichier suivi modifié sur le serveur » refusait tout. Le bloc disait bien plus grave : il imposait PHP 8.1 au SITE, quand l'application exige 8.2 — ce que le contrôle du PHP de la ligne de commande (8.2.28) ne pouvait pas voir.
-
5.02
La pastille d'un onglet du relevé paraît dès l'ouverture de la fiche
Le nombre de mouvements d'un onglet Prime, Commission, Rétrocommissions ou Taxes ne s'affichait qu'après un clic sur l'onglet : la pastille arrivait avec la liste. Trop tard — elle doit dire ce que la fiche contient avant qu'on aille voir.
-
5.01
Un excédent se nomme « Trop-versé » ou « Trop-perçu », et le rafraîchissement du relevé est prouvé
UN SOLDE NÉGATIF SE LIT EN MOTS. « Solde -60,00 » demandait de savoir que le signe moins voulait dire un excédent. La synthèse NOMME désormais ce qui a été payé en trop, montant en positif : « Trop-perçu » quand le cabinet ou l'assureur a reçu plus que dû (prime, commission), « Trop-versé » quand c'est le cabinet qui a trop payé (rétrocessions, taxes).
-
5.00
La fiche d'une tranche montre sa prime, sa commission, ses rétrocessions et ses taxes
LE GESTIONNAIRE VOIT TOUT, SANS RIEN TOUCHER. La fiche Tranche ne portait que Principal, Paiements de prime et Documents. Elle gagne quatre onglets en lecture seule — Prime, Commission, Rétrocommissions, Taxes — chacun une synthèse (dû, exigible, payé, solde) et la liste des mouvements, total au pied. « Paiements de prime » est fondu dans « Prime » : les signalements y sont des lignes, à côté des factures client et des bordereaux.
-
4.99
Un dialogue ne propose plus que les gestes que le serveur accepte
LES BOUTONS SUIVENT LES DROITS. « Ajouter », « Modifier », « Supprimer » d'une collection, comme « Facturer la commission » ou « Signaler un paiement de prime », s'affichaient à tous : seul le serveur refusait, au clic. Un gestionnaire de compte, qui doit tout voir sans toucher à la comptabilité, se voyait offrir des gestes interdits.
-
4.98
Le formulaire d'une fiche perd ses gouttières, et le menu contextuel sa plomberie en double
LE FORMULAIRE PREND LA GRAMMAIRE DU WORKSPACE. La colonne du formulaire gardait une marge, un cadre gris arrondi autour des onglets, et des cartes de champ séparées par seize pixels de vide — quand les lignes d'une liste du workspace se touchent et ne se séparent que par un filet. La colonne n'a plus de marge ; l'entête « Fiche client », l'alerte et les intertitres deviennent des bandeaux pleine largeur ; le cadre des onglets disparaît ; les cartes de champ deviennent jointives, séparées par un filet d'un pixel, sans ombre ni coins arrondis. Les formulaires de rôles suivent.
-
4.97
Une action ne se défait plus à l'« Enregistrer » suivant, et le titre nomme la fiche
« Retirer du portefeuille », lancé depuis la fiche d'un client, s'appliquait en base — mais le dialogue gardait l'ancien portefeuille dans son formulaire, et l'« Enregistrer » suivant le réécrivait. Le retrait était défait sans un mot. Aucune action ne prévenait le dialogue qu'elle venait de changer la fiche qu'il affiche, et le serveur acceptait de réécrire une fiche sans savoir ce qu'elle était devenue depuis son ouverture.
-
4.96
Le dialogue d'une fiche prend les gouttières du workspace
La barre d'actions du dialogue était une carte blanche posée sur un fond gris, au-dessus de deux autres cartes — la colonne des attributs et le formulaire —, chacune séparée de ses voisines par seize pixels de gris. Le workspace, lui, ne pose pas de cartes : ses colonnes se touchent, et ses bandeaux (titre, onglets, barre d'outils) sont blancs, avec une gouttière de huit pixels et un filet sous eux. Le même logiciel parlait deux langues selon qu'on regardait une liste ou une fiche.
-
4.95
Les actions d'une fiche ont leur barre, pleine largeur, celle du workspace
Dans le dialogue d'une entité, les actions de la fiche vivaient dans une barre sombre maison, coincée en haut de la colonne des attributs calculés : quatre cents pixels pour neuf boutons, dont le dernier sortait du cadre. Elle disparaissait avec la colonne quand la fiche n'avait aucun attribut calculé, ne regroupait aucune famille, et ne ressemblait en rien à la barre du workspace qui propose pourtant LES MÊMES actions.
-
4.94
Un sinistre ne porte plus que sur une police réellement souscrite
La référence de police d'un sinistre était un champ de texte libre : on pouvait y écrire n'importe quoi, et rien ne le relevait jamais. Un dossier finissait rattaché à une police inexistante — on s'en apercevait en réclamant à l'assureur, quand il était trop tard pour corriger la saisie.
-
4.93
Les pistes et les sinistres d'un client se lisent et se créent depuis sa fiche
La fiche d'un client montrait ses contacts et ses documents, mais ni ses pistes ni ses sinistres. Pour voir les affaires qu'on lui avait ouvertes, il fallait quitter sa fiche, changer de rubrique, et l'y rechercher — celui-là même qu'on venait de quitter. Deux onglets de plus, et une entrée « Créer un sinistre » au clic droit de la rubrique, suppriment ce détour.
-
4.92
La collection d'un autre cabinet cesse d'être lisible, et un onglet hors périmètre disparaît
Deux trous d'accès sur le même chemin, trouvés en écrivant le banc des onglets de la fiche client.
-
4.91
Le mouvement réduit neutralise enfin les cinq squelettes de chargement
La seule règle `prefers-reduced-motion` du projet ne couvrait que `.jsb-onglet` : le shimmer des squelettes tournait malgré la préférence du système, sur les cinq squelettes existants comme sur celui du total qui vient de naître (WCAG 2.3.3).
-
4.90
Le total d'une collection se lit au pied dès la création, et un squelette couvre son calcul
On saisissait quatre chargements de 1 000 / 100 / 160 / 20 sans jamais voir 1 280 : le pied de total n'apparaissait qu'une fois la fiche parente enregistrée. La règle était délibérée — le serveur ne connaît pas les lignes qui attendent dans le tampon du navigateur, et un total qui ment est pire qu'un total absent. On ne lève pas la règle, on lève sa cause.
-
4.89
La requête de la liste de collection cesse d'être perdue par quarante-quatre contrôleurs
`handleCollectionApiRequest()` accepte une `Request` depuis toujours, mais un seul de ses quarante-cinq appelants la renseignait — ConditionPartageController. Partout ailleurs elle valait `null`, et `?page=` comme `?ids=` étaient lus sur un objet absent puis ignorés EN SILENCE : une pagination qui ne paginait pas, un rattachement invisible tant que la fiche parente n'était pas enregistrée.
-
4.88
Un compte n'a qu'une seule invitation par cabinet
Tout le cloisonnement repose sur `CabinetActif`, qui résout l'invité par `findOneBy(['utilisateur', 'entreprise'])`. Avec deux lignes, Doctrine en rend UNE, arbitrairement — selon l'ordre physique, le plan d'exécution, le cache.
-
4.87
Une entité d'un autre cabinet n'est plus lisible, ni modifiable, ni supprimable
Le produit ne vérifiait que le TYPE. `mayAccessEntity($entityClass, …)` juge si le périmètre de rôles couvre « les Assureurs » ; il ne dit rien de l'assureur #34615. L'instance, elle, était chargée par `->find($id)` — un identifiant venu de l'URL ou du corps de la requête.
-
4.86
Les dix derniers champs de relation rejoignent le rendu commun
Dix FormTypes répétaient le même `<div><strong>…</strong><div style="color: #6c757d; font-size: 0.85em; padding-left: 2px; margin-top: 2px;">…</div></div>` — un style inline hors charte, copié dix fois, et dix occasions de diverger.
-
4.85
Un champ de relation renseigné affiche enfin le nom, et rien d'autre
Le champ « Assureur » occupait sept à dix-huit lignes une fois rempli : le nom, puis `Email: N/A | Tél: N/A`, puis cinq tuiles chiffrées empilées — prime, commission, deux taxes, rétro. Elles restaient là en permanence, alors que le choix était fait.
-
4.84
On n'ouvre pas le cabinet d'un autre, et une page de consultation n'ouvre rien du tout
Deux chemins écrivaient `Utilisateur::$connectedTo`, inégalement gardés, et tous deux persistaient aussitôt. C'est la clé de voûte : `setFiltreEntreprise()` et tout ce qui lit le gardien suivent cette colonne. Qui l'écrit décide de ce que l'utilisateur voit.
-
4.83
Le contrat des événements voit enfin les émetteurs qui reçoivent le nom en paramètre
Ce test ne connaissait que deux façons d'émettre : `broadcast('app:…')` et `new CustomEvent('app:…')`. Il en manquait une troisième — une aide qui construit l'événement à partir d'un de ses paramètres :
-
4.82
Les 48 fichiers de tests JavaScript ont enfin un point d'entrée
Ils n'en avaient aucun. Aucune occurrence de `node --test` dans le dépôt, et `node` n'existe pas sur le serveur de déploiement : `bin/deploy.sh` ne pouvait pas les lancer. Cinq cent cinquante-trois tests écrits, jamais rejoués.
-
4.81
Un cabinet n'est ouvert que si l'on y a encore une invitation
Tout le cloisonnement du produit repose sur `Utilisateur::$connectedTo`. Cette colonne était lue partout et vérifiée nulle part : personne ne s'assurait qu'une `Invite` existait encore pour le couple (utilisateur courant, cabinet ouvert).
-
4.80
Le filtre d'autocomplétion suit le cabinet OUVERT, et non ceux où l'on a un accès
Le test de cloisonnement opposait deux comptes étrangers l'un à l'autre. Un filtre qui se tromperait de QUESTION — « à quels cabinets cet utilisateur a-t-il accès ? » au lieu de « quel cabinet est ouvert ? » — y passait au vert, puisque l'attaquant n'a aucun accès au cabinet voisin. Les deux lectures ne divergent que sur un compte qui appartient aux deux cabinets.
-
4.79
Deux endpoints d'autocomplétion ne servent plus les données des autres cabinets
Sur les vingt champs portant #[AsEntityAutocompleteField], DIX-HUIT scopaient par FormListenerFactory::setFiltreEntreprise(). Les deux autres fuyaient, et aucun des deux n'était employé par le moindre formulaire : l'attribut suffit à inscrire un champ au registre, et EntityAutocompleteController sert /autocomplete/{alias} PAR ALIAS, sans jamais regarder si un formulaire s'en sert.
-
4.78
Une collection se lit enfin d'un coup d'œil
Une rangée affichait un titre entre guillemets, un montant en gras noyé au milieu des métadonnées, et un total qui flottait dans l'entête sans jamais désigner ce qu'il additionnait. Rien n'était faux ; rien ne se lisait.
-
4.77
Le client d'une piste ne se redemande plus quand on le connaît déjà
« Créer une piste » part d'un clic droit SUR un client : le geste l'a nommé, et l'entête du dialogue le rappelle. Le formulaire le redemandait pourtant, carte déployée avec les chiffres du client et une croix pour l'effacer — il proposait de défaire ce qu'on venait de faire.
-
4.76
Le bandeau des onglets ne laisse plus défiler le contenu à côté de lui
En défilant, le haut d'une carte — ou la barre d'outils de l'éditeur riche — apparaissait au-dessus de la barre des onglets, qui semblait alors chevaucher ce qu'elle était censée cacher.
-
4.75
Chaque collection d'une fiche devient un onglet
Un dialogue empilait ses champs puis ses collections, chacune repliée derrière un accordéon : la fiche Invité en enchaînait sept, et lançait sept requêtes pour n'en regarder qu'une. Désormais « Principal » porte les champs ordinaires, et chaque collection a son onglet — la grammaire des deux barres du workspace, le même composant, le même repli « + N ».
-
4.74
Les onglets d'une rubrique ressemblent enfin à ceux du workspace
Le workspace avait deux barres d'onglets et deux grammaires pour les dessiner. Celle des rubriques ouvertes : des onglets encadrés, coins hauts arrondis, posés sur un bandeau gris, l'actif en blanc coiffé d'un liseré cobalt. Celle d'une rubrique — Principal, Contacts, Pistes, Sinistres… : des onglets soulignés, sur fond blanc, sans bandeau, dans une autre taille de police et d'icône.
-
4.73
Une note issue d'un bordereau se reconnaît dans la liste
Rien ne la distinguait d'une note composée à la main : mêmes montants, même statut, même ligne. Or les deux ne se corrigent pas de la même façon — l'une se reprend dans son bordereau, l'autre ligne à ligne. Il fallait ouvrir la note pour savoir d'où elle venait.
-
4.72
Le compte où encaisser ne s'ouvre plus vide
Le formulaire de paiement exige un compte bancaire. Il le préremplissait depuis la note — mais seulement si la note en portait un. Une note composée à la main, ou issue d'un bordereau, n'en porte aucun : le champ s'ouvrait vide, et l'enregistrement échouait sur « Ce champ est obligatoire » au terme d'une saisie déjà faite.
-
4.71
Filtrer les notes par état de règlement, d'un clic
Pour savoir ce qui restait à encaisser, il fallait lire la colonne « Statut » ligne à ligne. Sept rubriques ont une pilule de chips ; Notes n'en avait aucune. Elle en a une : Impayées · Partielles · Réglées · Sans montant · Toutes, et elle s'ouvre sur les impayées.
-
4.70
Signaler le règlement d'une note, là où on la regarde
Émettre une note n'est que la moitié du geste : elle part pour être PAYÉE. Or l'encaissement ne se saisissait qu'en ouvrant la note et en descendant dans l'accordéon « Paiements liés » — deux détours, et seulement si l'on y pensait. Rien ne le proposait ni depuis la fenêtre qui vient d'émettre la note, ni depuis la rubrique où on la relit trois semaines plus tard.
-
4.69
Ouvrir une piste au client qu'on a sous les yeux
Décider d'ouvrir une affaire à un client depuis sa rubrique obligeait à changer de rubrique, à créer une piste à blanc, puis à y rechercher ce même client à l'autocomplétion. On ressaisissait ce qu'on venait de quitter.
-
4.68
Un onglet contextuel ne montre plus que les enfants de son parent
L'onglet « Pistes » d'un client affichait les pistes de TOUT LE CABINET. Ses notes, ses sinistres et ses partenaires associés aussi. Pas au premier affichage — c'est ce qui a rendu le défaut invisible si longtemps — mais dès la page 2, une recherche, un « Réinitialiser » ou un rafraîchissement après enregistrement.
-
4.67
La commission facturée entre en comptabilité, et le règlement éteint la créance
Émettre une note ne produisait RIEN. Pas une écriture, pas un produit, pas une créance — il n'existait même aucun compte où la loger : l'actif du bilan ne comptait que la trésorerie et la TVA récupérable. Une commission réclamée à un assureur n'apparaissait donc ni au journal, ni au grand livre, ni au bilan. Le cabinet ne pouvait pas lire ce qu'on lui devait.
-
4.66
Facturer une part de la commission, et finir la fenêtre qui la réclame
La fenêtre de facturation était le calque de celle du reversement, mais elle n'en avait repris que la moitié : ses boutons partaient sans icône, ses champs sans pastille, et son montant n'était pas modifiable. Or on ne réclame pas toujours tout le dû — un assureur n'en reconnaît parfois qu'une part, ou un acompte a été convenu.
-
4.65
Facturer une commission depuis l'écran, jusqu'au PDF — et cesser de la facturer deux fois
L'assistant préparait la note ET ses lignes en un seul plan. L'écran, lui, n'ouvrait qu'un dialogue prérempli de l'EN-TÊTE : le courtier obtenait une note vide dont il composait les lignes une à une, dans un dialogue enfant, après avoir enregistré le parent. La parité jouait à l'envers.
-
4.64
Ket facture la commission qu'elle réclame chaque jour, et nomme ce qu'elle ne sait pas faire
Sa boussole porte la chaîne « prime payée → commission exigible → facturation en lot (note de débit) → recouvrement ». Son programme du jour ouvre une section « commissions exigibles » commentée « il est temps de facturer l'assureur ». Et quand un courtier a demandé le plan correspondant, elle a répondu qu'elle ne trouvait « aucune tranche de commission associée à cette police » — alors que l'écran affichait « Prime payée, commission due · Commission exigible — 11,60 USD ».
-
4.63
Le dialogue de facturation cesse de lister le portefeuille des autres cabinets
Sur les vingt champs d'autocomplétion du projet, dix-sept filtraient depuis toujours par `FormListenerFactory::setFiltreEntreprise()`. Deux ne le faisaient pas, et ce sont exactement les deux qu'on ouvre pour composer une ligne de facture : le REVENU à facturer et la TRANCHE de prime correspondante. Leur `query_builder` était nu — `createQueryBuilder('r')`, `createQueryBuilder('t')`, sans la moindre clause WHERE.
-
4.62
Ket cesse de faire attendre devant deux modèles libres
Le courtier lisait dans le chat : « Mon moteur a atteint sa limite de débit pour la minute en cours. Relancez-la dans 21 secondes. » Au même instant, la console des fournisseurs affichait les trois modèles à 100 % de fenêtre libre et concluait « Peut répondre maintenant ». Les deux écrans disaient vrai. Aucun ne décrivait la situation.
-
4.61
La console cesse de dire « prêt » quand Ket ne peut pas répondre
Un agent lisait « gemini · prêt · minute 100 % » pendant que Ket répondait au courtier « limite de débit pour la minute en cours, relancez dans 47 secondes ». Les deux écrans disaient vrai ; aucun ne disait la même chose, et rien ne faisait le lien.
-
4.60
La mesure cessait de mesurer quand le quota coupait la parole à Ket
La première passe complète a rendu 25,7 %. Le chiffre était faux, et de beaucoup : sur 232 messages, 46 s'étaient terminés en « budget atteint », la boucle arrêtée faute de débit disponible sur la minute, avant même que les outils soient appelés. Le bilan les comptait en « outil attendu absent ». Il reprochait à Ket la vitesse du rejeu.
-
4.59
La passe complète tient la distance, et cesse de compter mes pseudonymes
Deux défauts du harnais, découverts en tentant enfin la passe que le plan réclamait depuis le début.
-
4.58
La console cessait de dire vrai sur les trousses de Ket
L'écran des réglages de Ket annonçait la porte de sortie de la trousse minimale comme déclarée en « Lecture, Écriture ». Elle ne l'est dans ni l'une ni l'autre. Et il comptait ses 417 octets dans le poids des deux trousses — sur un écran dont tout l'intérêt est d'annoncer à l'administrateur ce que la plateforme envoie vraiment, et ce qu'il économise en coupant un outil.
-
4.57
La mesure de justesse imprime sa marge, et invalide mes propres conclusions
Sur deux jours, la même famille de vingt cas a rendu 57,1 %, puis 38,9 %, puis 47,1 %, puis 46,7 %. J'ai lu ces écarts comme des effets de mes changements. J'ai même annulé un lot sur cette base.
-
4.56
Le rejeu cesse de poser les questions hors de leur conversation
« Dans cela, affiche uniquement celles dont les primes sont échues. » Rejouée dans une conversation vide, cette question ne désigne rien. Ket demandait une clarification — elle avait raison — et la mesure comptait un mauvais choix d'outil. On mesurait sa patience, pas son jugement.
-
4.55
Le départage des impayés est annulé : la mesure ne l'a pas validé
Règle du chantier : un paquet qui fait baisser le choix d'outil est ANNULÉ, pas arbitré. Elle s'applique ici, à ma propre correction.
-
4.54
Une description qui recommande un autre outil le fait gagner : on inverse le sens
Le rejeu du corpus a mesuré ce qu'aucun test ne voit. Sur les questions de classement, le lot précédent a fait passer le bon choix d'outil de 71,4 % à 86,7 %. Sur les impayés, il l'a fait TOMBER de 57,1 % à 38,9 %.
-
4.53
Un « merci » ne transporte plus soixante-quatre kilo-octets d'outils
Un message sur trois n'appelle aucun outil. Beaucoup ne sont qu'un « ok », un « merci », un « très bien » — et ils emportaient malgré tout les 64 Ko de déclarations de la trousse de lecture, pour une réponse d'une phrase.
-
4.52
Ket cesse de transporter à chaque message ce qui ne lui sert qu'après
Les descriptions d'outils mélangeaient trois choses : à quelle question l'outil répond, ce que son résultat contient champ par champ, et comment lire ce résultat. Seule la première aide à CHOISIR. Les deux autres ne servent qu'après l'appel — le modèle a le résultat sous les yeux quand il rédige — et partaient pourtant à chaque message, appel ou non.
-
4.51
Les outils de Ket portent enfin des noms qu'elle n'écorche plus
Quatre outils changent de nom. Aucun octet gagné — ce lot vise l'autre défaut : Ket appelle un outil qui n'existe pas dans 5,3 % des cas, et chaque nom écorché coûte un aller-retour complet pour rien.
-
4.50
La mesure cesse de reprocher au nommage ce que la compréhension a refusé
Le rejeu du corpus annonçait « 40 % de bons choix d'outil » sur les questions d'impayés. Le chiffre était faux, et il accusait la mauvaise pièce.
-
4.49
« Combien de clients ? » et « la liste des clients » n'ont plus qu'un outil
Ket disposait de deux outils pour la même question. Compter et lister demandaient au serveur exactement la même chose — mêmes droits, même périmètre, mêmes filtres, même requête — et seule la taille de page les distinguait. Leurs deux routages étaient d'ailleurs identiques à trois lignes près.
-
4.48
Ket cesse de décrire trois fois le même filtre, et d'en oublier un morceau
Les trois outils de liste — rechercher_entites, compter_entites et ouvrir_rubrique — expliquaient au modèle les mêmes filtres, chacun dans ses propres mots. Trois textes pour une seule règle, et ils avaient déjà divergé :
-
4.47
Les questions déjà posées à Ket deviennent le juge de ses prochains outils
On savait dire ce que Ket coûte. On ne savait pas dire si elle choisit le bon outil — et sans ce chiffre, raccourcir une description reviendrait à dégrader son jugement sans le voir.
-
4.46
Quand Ket se trompe de nom d'outil, on le sait enfin — et en production
Ket appelle parfois un outil qui n'existe pas : « analyser_portefeuille » pour « analyse_portefeuille », « consultar_guide » en espagnol. Un filet rattrape ces écorchures depuis hier, et il laissait une trace — mais sur le mauvais canal. En production, ce canal n'écrit que les erreurs : la ligne n'était jamais enregistrée. En développement, elle se noyait dans un journal de près de quatre gigaoctets. La seule mesure du rattrapage était donc indisponible partout où le rattrapage sert.
-
4.45
Chaque phase de Ket peut avoir son modèle, et donc sa fenêtre de quota
Les trois phases d'un message partageaient un seul modèle, donc un seul compteur de 250 000 jetons par minute — et trois messages suffisaient à le saturer. C'est la cause directe des « moteur saturé » que l'utilisateur voit. Or Google tient sa fenêtre PAR MODÈLE : une phase posée sur le sien cesse de disputer son débit aux autres.
-
4.44
La compréhension de Ket cesse de tomber quand son modèle est débordé
Quatre messages sur dix partaient sans que personne ait compris la demande, et la cause n'était pas dans la phase : elle en était ABSENTE. Le moteur bascule depuis toujours sur un modèle de secours au premier 503 et continue de répondre ; la phase de compréhension, elle, n'avait aucune chaîne — elle échouait, simplement.
-
4.43
Un nom d'outil écorché ne coûte plus un tour entier à Ket
Mesuré sur les journaux au 2026-09-25 : 10 appels d'outil sur 188 — 5,3 % — visaient un nom qui n'existe pas, et TOUS étaient des quasi-homonymes du vrai. `analyser_portefeuille` pour `analyse_portefeuille` (quatre fois), `rechercher_entite` au singulier, `consultar_guide` en espagnol le temps d'un appel. Chacun rendait « introuvable », et le modèle devait repartir pour un tour de quarante mille jetons d'entrée — sur un palier gratuit où trois messages par minute saturent déjà le quota.
-
4.42
Ket cesse d'armer sa propre trousse d'écriture en parlant
Le filet lexical qui décide de la trousse relisait les TROIS DERNIERS MESSAGES DU FIL, tous rôles confondus — donc les réponses de Ket. Or le prompt lui ORDONNE de parler d'enregistrer, de modifier, d'ouvrir un formulaire : sans cela elle répondrait « je ne peux pas », ce qui est faux. Elle armait donc l'écriture toute seule, en se relisant, et le gardait trois tours. Un filet fait pour lire une intention d'UTILISATEUR se lisait lui-même.
-
4.41
Le rapport de jetons cesse de conclure avant de mesurer, et dit enfin pourquoi Ket se replie
Trois angles morts fermés dans l'instrumentation, avant de toucher à ce qu'elle mesure.
-
4.40
Ket écoute la phrase jusqu'au bout : un souffle repris ne fait plus deux questions
En mode Live, une phrase dite en deux souffles partait en deux questions, et Ket répondait au premier bout. Sur Chrome et Edge de bureau, la reconnaissance tient sa session ouverte et rend un résultat définitif à CHAQUE pause de diction : le code ne recousait les morceaux que si la session s'était refermée toute seule — la signature d'Android, qui n'arrive jamais sur un ordinateur. Le tampon de recouture existait donc depuis le début, désarmé précisément là où il fallait.
-
4.39
Ket cesse de nier un paiement que l'écran affiche : la date du règlement voyage avec la tranche
Un courtier demande quel jour son client a réglé sa prime, et combien de jours se sont écoulés depuis. L'écran affiche la tranche en « Prime payée, commission due » ; le signalement — 102 $, réglés le 20/09/2026 — figure noir sur blanc dans son formulaire d'édition. Ket répond qu'aucune date n'est associée à ce règlement, puis conseille d'aller vérifier la saisie.
-
4.38
Le bandeau cesse de nommer un modèle qui n'a jamais répondu
Vérification sur trois échanges RÉELS en dev — pas sur un objet fabriqué à la main — de ce que les coulisses affichent. Deux constats, dont un seul était un défaut.
-
4.37
Ket ouvre ses coulisses : qui a répondu, avec quels outils, en combien de temps
Le bandeau sous la bulle de Ket annonçait « 35 714 jetons IA » et rien d'autre. Un chiffre qu'on subit : ni ce qui l'a produit, ni ce qu'il a servi à faire. Le moteur, lui, savait déjà tout — modèle qui a répondu, outils appelés, ventilation entrée / sortie / cache — mais n'en disait rien à personne hors du journal d'exploitation.
-
4.36
La console nomme le modèle qui répond vraiment, et Ket se tait quand elle n'a rien à rappeler
DEUX MENSONGES DU MÊME GENRE : dire quelque chose plutôt que rien, là où rien était la seule réponse juste.
-
4.35
Les deux rubriques de Ket n'en font plus qu'une, et l'écran cesse de perdre l'utilisateur en route
« Réglages de Ket » et « Fournisseurs de Ket » étaient deux entrées voisines du même menu. Un agent qui cherchait pourquoi Ket répond mal devait deviner laquelle ouvrir — et la réponse dépendait de la cause, qu'il ignorait justement. Qui répond POUR Ket est un réglage comme les autres : c'est un onglet de « Configuration de Ket ».
-
4.34
Les réglages de Ket tiennent dans un écran, et un filtre annonce qu'il travaille
CINQ BLOCS EMPILÉS FAISAIENT UNE PAGE DE CINQ MILLE PIXELS. L'historique, tout en bas, avait toutes les chances de n'être jamais consulté — et c'est précisément lui qu'on ouvre quand on se demande « pourquoi Ket ne le fait plus ». Ils deviennent des onglets, avec le compte en pastille : on voit d'un coup d'œil qu'il y a 52 outils, 4 seuils, 41 règles et 2 changements.
-
4.33
Les outils de Ket se coupent depuis la console, ses seuils se règlent, et ses règles portent enfin un numéro
TROIS LOTS EN UN, parce qu'ils ne valent que pris ensemble : un interrupteur sans journal ne se défait pas, un seuil sans borne ne se corrige pas, et une règle sans identifiant ne se cite pas.
-
4.32
Chaque outil de Ket porte sa pastille, et un alias muet ne passe plus
SUR CINQUANTE-DEUX LIGNES, C'EST L'ICÔNE QUE L'ŒIL ATTRAPE AVANT LE NOM. La liste n'en avait aucune : on lisait cinquante-deux libellés alignés, sans repère pour retrouver du regard celui qu'on cherchait.
-
4.31
La console montre enfin ce que Ket sait faire, et ce que chaque outil coûte
CE QUE PERSONNE NE POUVAIT VOIR. Cinquante-deux outils partent au fournisseur à chaque échange, et leur liste n'existait nulle part ailleurs que dans src/Ai/Tool. Le support répond pourtant tous les jours à « Ket sait-elle faire X ? », et le commercial construit son argumentaire sur la réponse : aller la chercher dans le code n'est pas un chemin praticable pour eux.
-
4.30
Un agent ne peut plus se faire super-administrateur, et un compte sans département cesse d'avoir tous les droits
QUATRE TROUS DE DROITS, DONT DEUX MENAIENT À LA RACINE.
-
4.29
Une bascule de secours se compte, et un message ne peut plus durer trois minutes
L'ÉTAPE A avait borné un APPEL à 45 secondes. Elle n'avait borné ni le MESSAGE, ni notre capacité à voir ce qui s'était passé pendant.
-
4.28
On cesse de payer un outil qui ne peut que refuser, et les trois outils de liste nomment enfin le bon paramètre
UN PARAMÈTRE QUI N'EXISTE PAS. rechercher_entites, compter_entites et ouvrir_rubrique — les trois outils les plus appelés — annonçaient au modèle un paramètre « statutPaiement » pour filtrer les tranches. Il s'appelle « axes ». Le modèle qui suivait la description émettait donc un argument ignoré en silence, et la liste rendue ne coïncidait plus avec les chips de la rubrique : précisément la parité que ces descriptions promettent. Rien ne pouvait le voir — le schéma est valide, l'appel part, l'outil répond. Seul le rapprochement entre la prose et le schéma le révèle, et c'est désormais un tes…
-
4.27
Une police ne peut plus finir avant d'avoir commencé, et les règles de combinaison valent aussi pour Ket
DEUX TROUS, ET LE SECOND EST LE PLUS LARGE.
-
4.26
« commission » n'arme plus la trousse d'écriture
Le filet lexical qui décide de la trousse n'avait aucune frontière de mot : il lisait un verbe d'action À L'INTÉRIEUR d'un mot du métier. « mission » se trouvait dans COMMISSION — le mot le plus fréquent du courtage —, « édit » dans CRÉDIT, « change » dans ÉCHANGE, « traite » dans TRAITEMENT, « accord » dans ACCORDÉE et « marque » dans REMARQUE. Comme le filet s'applique aux TROIS derniers messages, un seul « commission » contaminait trois tours.
Joseara