← Dashboard

📝 Décisions

10 décision(s) — les plus récentes du journal. ⚠️ Connaissance du Système arrêtée au 15 octobre 2026 (empreinte e43fdbf8a37de3d9). Elle a 4 jours : les chiffres et les états ci-dessous peuvent avoir changé. Dis-le plutôt que de les présenter comme actuels, et propose de régénérer avec `dahdev systeme`.

Source unique : déodat-os/decisions/log.md — lue et rendue par dahdev systeme.

⚠️ Cette liste a 4 jours : le journal a pu recevoir des décisions depuis. Régénérer avec dahdev systeme.

Une capacité n'est dite prête que si le réglage est posé

2026-10-12

Cinq capacités, et quatre états qu'on ne confond plus : véri

Contexte: Le premier contact GreenFlame allait se faire avec un message qui promettait « vos clients paient avec MTN MoMo ou Moov Money ». La vérification du matin avait montré que mtn.ts existe mais que moov.ts n'existe pas, et que c'est PAYMENT_MODE — posé à la main dans Vercel — qui décide si un paiement est réel ; le poste ne peut pas lire ce réglage. Demande de Déodat, le soir : rendre le mode de paiement vérifiable depuis le poste, et refuser tout document qui annonce une capacité sans que son réglage soit prouvé. Les deux demandes sont un seul chantier : la seconde est le mécanisme, la première en est la première ligne.

Décision: Option 3, poussée jusqu'à l'assistant : section capacites de l'artefact, reponseCapacites() servie sans modèle, rubrique de guide, intention capacites de la couche rapide, et la section « CAPACITÉS » du contexte du modèle.

Raison: Sur la vérification, aucune lecture n'est possible sans déployer ni sans jeton — deux gestes qui ne sont pas de mon ressort. L'option 3 ne les remplace pas, elle rend la question tractable : l'état est un fait daté, avec le moyen de le trancher écrit noir sur blanc, et l'assistant ne peut plus dire « prêt » sur la foi d'un fichier. Sur le contrôle, la difficulté était de ne pas refaire une faute déjà écrite dans la bibliothèque — « une détection par sous-chaîne confond un mot avec un secret » : les mots qui déclenchent ne sont donc pas devinés, chaque capacité déclare les siens (PAYMENT_MODE, MoMo live, adapter built). C'est ce qui aurait arrêté « MTN MoMo live adapter built » : la phrase nomme la capacité, et le document ne disait l'état nulle part.

Pattern: Le code prêt n'est pas le réglage posé — voir patterns/library.md

Le premier contact GreenFlame ne promet pas un paiement réel

2026-10-12

Le geste est exécutable sans zone d'ombre. Les trois sources

Contexte: Déodat a choisi, entre quatre chantiers, de faire le premier contact plutôt que d'écrire du code : le kit de prospection était complet mais le journal vide — 41 pistes, aucun geste. Avant d'envoyer quelqu'un sur un marché, la règle du kit s'applique à lui-même : « une source repérée n'est pas une source vérifiée ». La vérification a donc porté sur deux choses distinctes : les sources (Ganhi, PK3, ANaGeM) et ce que la visite doit montrer, puisque le message écrit dans approches.md promettait « vos clients paient avec MTN MoMo ou Moov Money, ils reçoivent un cashback ».

Décision: Option 3. La visite porte sur ce qui ne dépend pas du paiement : le modèle se comprend sur la simulation, et la grille de frais (C-3 à C-15) se valide avec un marchand, elle ne s'annonce pas.

Raison: Un paiement simulé réussit en silence : en PAYMENT_MODE=mock, la transaction aboutit, le marchand voit « payé », et aucun franc n'a bougé. Ce qui suit (« c'est du vent ») coûte plus cher que la vente qu'il arrache, et se répare en semaines. Le fait vérifié est net : lib/mobile-money/mtn.ts existe mais moov.ts n'existe pas — en mode live, Moov lève une erreur explicite au lieu de retomber sur le mock —, et le mode réellement posé côté Vercel n'est pas lisible depuis le poste. Ce n'est donc pas le code qui décide du paiement, c'est une case de configuration, et elle est peut-être restée mock. Écrire « prêt » à sa place aurait été une supposition déguisée en mesure — exactement le défaut que la fiche GreenFlame portait déjà (« MTN MoMo live adapter built » : littéralement vrai, trompeur à l'usage).

Pattern: Le code prêt n'est pas le réglage posé — voir patterns/library.md

La prospection entre dans ce que l'assistant sait

2026-10-12

Artefact à 18 sources (empreinte 72801bcc281caa66), section

Contexte: Le journal de prospection venait d'être écrit, mais il vivait uniquement sur le poste : l'assistant de l'application répondait encore sans savoir qu'un marchand attendait une relance. Or c'est la seule action qui sert la mission (GreenFlame bloque sur le capital, pas sur le code), et un agent qui ne sait pas qui relancer ne sert à rien.

Décision: Option 3 — les deux journaux entrent dans les sources et dans l'empreinte ; lire_contacts() calcule la section à la génération ; l'application ajoute reponseContacts() et contactsTexte(), une rubrique de guide, une intention de couche rapide (« qui dois-je relancer ») et la section « PROSPECTION » du contexte du modèle.

Raison: L'option 2 aurait recréé exactement le défaut qu'on venait de retirer : deux écritures pour le même fait. L'option 3 garde une seule voie — le poste — et fait entrer le journal dans l'empreinte : ajouter un geste change désormais l'artefact, donc oublier de régénérer devient visible au lieu de passer inaperçu. Un choix de forme compte autant que le fond : l'artefact ne porte aucun état relatif au jour (« dû aujourd'hui »), seulement des dates absolues (prochain) et la date de la connaissance ; sinon l'artefact vieillirait tout seul et la comparaison signalerait une divergence chaque matin — la leçon qui avait déjà fait distinguer une divergence d'une dérive pour les mesures de git.

Pattern: Ne pas stocker ce qu'on peut recalculer — voir patterns/library.md

La prospection a un journal, pas un tableau d'états

2026-10-12

Écrit et éprouvé : 22 cas pour le calcul d'état (dont le che

Contexte: Quatre demandes le même jour : un agent de prospection et de suivi pour GreenFlame et CareerBoost (marchands de Cotonou, financeurs, clients B2C, acheteurs B2B), la fusion des deux mémoires de l'assistant, une attention événementielle au lieu de quatre crons à heure fixe, et la clé secrète à poser avec la rotation due. Traité en un seul chantier (règle 1), choisi par Déodat : la prospection. Ce qu'il fallait regarder d'abord : le blocage de la mission est le capital, GreenFlame est à 57 jours sans action, et aucun support de prospection n'existait — ni liste, ni cadence, ni mesure ; les champs « merchands onboardés » et « utilisateurs » de la fiche GreenFlame valent encore « ? ».

Décision: Option 2 — déodat-os/contacts/journal.csv (source de vérité, sept colonnes), pistes.csv (cibles repérées avec URL et date), approches.md (messages et cadence par cible), tools/dahdev/contacts.py (calcul d'état, relances dues, mesure, 22 cas d'épreuve), et une entrée dahdev.py contacts.

Raison: Un état stocké se met à mentir seul : une relance faite mais non cochée, une étape avancée à la main, et la liste affirme ce que les faits contredisent. C'est le même défaut que les deux mémoires de l'assistant et que la page /decisions qui portait un troisième jeu de décisions écrit à la main. Avec un journal, l'état est calculé : une relance après un rendez-vous ne rétrograde pas le contact, un refus le clôt, et trois gestes sans réponse le ferment — chacun éprouvé. Un candidat disparaît de « à approcher » dès qu'une ligne du journal le nomme, donc personne ne tient deux copies.

Pattern: Une seconde mémoire ne prévient pas qu'elle diverge — voir patterns/library.md

Un témoin qui écrit *et* relit : la panne de mémoire devient visible depuis le poste

2026-10-12

Éprouvé sur la vraie configuration, pas en laboratoire : le

Contexte: La correction du même jour avait rendu la panne de mémoire visible dans l'application (/api/status, la réponse « quel est mon statut de système »). Elle restait invisible depuis le poste : doctor contrôle les identifiants en clair dans les fichiers, jamais la configuration en état de fonctionner. Au moment où cette entrée est écrite, la configuration locale est toujours fautive, et rien au démarrage d'une session ne le disait.

Décision: Option 3 — tools/dahdev/memoire.py, appelé par doctor (contrôle du poste) et par brief (ligne de dérive).

Raison: La relecture est le cœur, et c'est la leçon de la veille : la clé publique « avait l'air de fonctionner ». Un insert accepté ne prouve rien — une politique peut accepter l'écriture et ne rien rendre à la lecture. Un témoin qui n'aurait pas relu aurait annoncé « ok » pendant que l'assistant écrivait dans le vide. Quatre états sont distingués, parce qu'ils ne se réparent pas de la même façon : ok, refusee (la configuration est fautive), injoignable (rien n'est établi — hors ligne, crier serait crier à tort, et un contrôle qui crie à tort finit contourné) et sans-config. Le témoin n'écrit jamais la clé, seulement sa forme (secrete, publique, inconnue, absente) : c'est elle qui nomme la correction.

Pattern: Une écriture acceptée n'est pas une mémoire — voir patterns/library.md

La mémoire de l'assistant s'est arrêtée sans le dire (une clé publique dans la case du service role)

2026-10-12

Cause mesurée : dans .env.local, SUPABASE_SERVICE_ROLE_KEY c

Contexte: Les journaux du serveur portaient 42501 — new row violates row-level security policy for table "dahdev_conversations" à chaque message. Déodat a demandé de réparer l'enregistrement des conversations, avec une consigne explicite : vérifier la cause réelle avant de relâcher quoi que ce soit. La cause évidente était la politique de sécurité ; c'était la mauvaise.

Décision: Option 2 — mesurer l'identité, puis réparer la configuration, et donner à la panne une surface visible.

Raison: La clé est l'identité : le service role ne contourne pas RLS par une autorisation plus large mais par son rôle. Assouplir la politique n'aurait rien réparé d'autre que le symptôme, et aurait donné à la clé publique — celle qui est embarquée dans le navigateur — le droit d'écrire et de supprimer dans cette base. C'est exactement le relâchement qu'il fallait éviter, et la consigne le disait.

Pattern: Un échec qui n'atteint pas la surface visible ne produit aucun signal — voir patterns/library.md

Une clé collée dans la conversation est une clé exposée

2026-10-12

Rien n'a été écrit, aucune commande n'a porté la clé, aucun

Contexte: Déodat a demandé si une clé API Gemini (GEMINI_API_KEY) avait été ajoutée à l'application, en la collant en clair dans son message. Réponse : non, et le code la lisait déjà dans l'environnement (src/lib/llm.ts, provider Gemini gratuit) sans que .env.local la porte.

Décision: Option 2 — aucune commande ne portera la valeur.

Raison: Deux règles du poste l'interdisent, et elles ont été écrites après une fuite réelle : « jamais de jeton en clair dans une commande » et « aucun secret dans un fichier suivi par git ». Une clé passée en argument d'une commande finit dans la configuration de l'agent, dans ses sauvegardes et dans ses transcriptions — c'est l'incident du 11 octobre 2026 (clé Stripe live, PAT GitHub, jetons Vercel et Supabase retrouvés dans 113 règles d'approbation). Ici la valeur est déjà dans la conversation, donc exposée : la seule réponse utile est de le dire, pas d'en écrire une seconde copie.

Pattern: Un secret approuvé une fois reste écrit — voir patterns/library.md

Les décisions n'ont plus qu'une mémoire, et la régénération ne dépend plus de personne

2026-10-12

La réponse « mes décisions » se recalcule depuis le journal

Contexte: L'entrée précédente laissait deux défauts ouverts, et Déodat a demandé de les fermer dans cet ordre : « réconcilier les deux mémoires de décisions, puis rendre la régénération impossible à oublier ». Le premier était mesuré : les décisions vivaient en trois exemplaires — le journal du Système (27 entrées), une table Supabase dahdev_decisions vide (lue par trois chemins de réponse), et un tableau écrit à la main en août dans src/app/decisions/page.tsx (3 entrées, dont deux démenties). « Mes décisions » répondait donc « Aucune décision enregistrée. » à côté de 27 décisions réelles, et la page affichait un troisième jeu. Le second défaut était celui de la veille : le contrôle de fraîcheur vivait dans doctor et brief, c'est-à-dire entre les mains de qui pense à les lancer ; une modification de déodat-os/ non suivie de dahdev systeme partait avec le commit, le push et le déploiement sans que rien ne le dise.

Décision: Options 2 et 4 pour la mémoire ; crochets pre-commit et pre-push pour la régénération.

Raison: Les options 1 et 3 gardent deux mémoires et demandent qu'on se souvienne du bon moment pour les réconcilier — c'est-à-dire exactement le mécanisme qui a produit la divergence. Et la mesure a tranché : dahdev_decisions, dahdev_patterns, dahdev_alerts et dahdev_briefings étaient toutes vides, jamais écrites — la capture depuis le téléphone n'a jamais servi, donc la supprimer ne coûte rien de réel. Ce qui reste doit être dit, pas silencieusement perdu : « note que… » reçoit maintenant « noté, pas enregistré » et l'endroit où ça s'écrit. Pour la régénération, une commande à lancer est un geste, donc un oubli possible ; un crochet est un passage, donc un oubli impossible — et le contrôle doit porter sur le dépôt versionné de l'application, puisque c'est lui qui est déployé, pas le disque du poste.

Pattern: Une seconde mémoire ne prévient pas qu'elle diverge — voir patterns/library.md

Une seule source de vérité pour l'assistant, générée depuis le Système

2026-10-12

dahdev systeme (nouveau, 8ᵉ commande) rend mission, projets

Contexte: La veille, l'assistant avait cessé de détourner les questions : le modèle arbitre au lieu des mots-clés. Restait le défaut qui rendait cette correction inutile — il répondait juste à partir de faits faux. Le contexte de l'application datait du 19 août 2026 et était recopié dans six fichiers : « VENTURES (5 projets actifs) », « Consultant: ✅ Livré », « Code: 97 migrations », « Emploi actif, 120K/mois », « promo jusqu'au 11 sept ». Une question comprise (« où j'en suis avec GreenFlame ») recevait une réponse d'août avec l'autorité d'un fait. Le dépôt de l'application ne lisait aucun document du Système.

Décision: Option 2.

Raison: L'option 1 est celle qui a produit le défaut : six copies à mettre à jour séparément, aucune ne sachant qu'elle a vieilli, et rien pour signaler qu'elles divergent. L'option 3 est impossible pour la partie déployée : l'application tourne sur Vercel, elle ne voit pas les fichiers locaux du poste — et la faire dépendre d'un service de plus serait payer une panne pour un problème de fraîcheur. L'artefact déplace le problème là où il est mesurable : une empreinte des sources, une date de génération, un contrôle qui échoue quand les deux divergent.

Pattern: Une copie ne sait pas qu'elle a vieilli — voir patterns/library.md

Le bot ne distinguait pas ce qu'on lui disait

2026-10-12

La couche données ne répond plus que si l'objet demandé est

Contexte: Déodat : « l'assistant n'en est pas encore vraiment un, s'il ne peut même pas distinguer ce qu'on dit dans un message ». Mesure sur trente phrases écrites comme on écrit vraiment : neuf détournements. « j'ai une réunion demain avec mon ancien patron » recevait « 📅 Nouvel événement — Confirme ? oui/non » ; « rappelle-moi qui est le Kingmaker » en proposait la création ; « j'ai besoin de capital pour ma voiture » la situation financière de GreenFlame ; « les événements de la journée m'ont fatigué » et « mon agenda est trop chargé » la liste de ses rendez-vous ; « quel est le risque principal sur GreenFlame » la fiche de présentation du projet ; « combien de temps j'ai mis sur CareerBoost » ses statistiques commerciales.

Décision: Option 2, plus un filet étiqueté quand plus aucun provider ne répond.

Raison: L'option 1 avait déjà été tentée, trois fois : la table portait des mots faibles, des poids, des seuils, une tolérance de pluriel et une frontière de début de mot. Chaque correction a refermé un trou et en a ouvert un autre, parce que le signal lui-même est faux — un mot présent ne dit pas ce qu'on demande. L'option 3 aurait fait payer un appel de modèle pour « mes événements », où la question est parfaitement explicite, et aurait rendu le bot muet dès que les clés manquent, ce qui est précisément l'état de ce poste. L'option 2 garde la vitesse là où l'intention est un objet nommé, et rend la décision à qui comprend.

Pattern: Une décision doit appartenir à qui comprend — voir patterns/library.md