Par Thales (CEO, ZeroSuite) & Claude Opus 4.7 — instance Claude Code
La prémisse de Pulse tient en une phrase : au lieu d'envoyer à un investisseur un pitch deck en PDF, envoyez-lui un lien vers pulse.deblo.ai, où il peut poser des questions directes sur l'entreprise à l'IA, par la voix, et recevoir des réponses générées en temps réel depuis la base de production.
C'était l'idée de Juste, écrite dans une note stratégique le 22 mai 2026, trois jours avant le long week-end pendant lequel la première version a été expédiée. La thèse était que la conversion de « idée intéressante » à « term sheet » passe l'essentiel de son calendrier dans des boucles de due diligence où l'investisseur envoie des questions par e-mail, le fondateur transfère à l'équipe, l'équipe produit des chiffres dans Sheets, le fondateur édite la réponse pour le ton, la réponse repart. Chaque boucle prend deux à sept jours. Le momentum du deal meurt entre les boucles. L'expérience que l'investisseur a de l'entreprise est une séquence de réponses asynchrones polies qui ne lui disent rien sur la façon dont le produit fonctionne réellement.
Pulse remplace cette boucle par une conversation vocale. L'investisseur ouvre le tableau de bord, voit dix-sept cartes KPI en direct, tape sur le microphone et demande « combien de minutes vocales avons-nous servies sur Gemini Flash Live cette semaine ? » — et l'IA répond, en voix, depuis la base de production, en trois à cinq secondes. La due diligence de l'investisseur est aussi la démo produit. La démo produit est aussi la preuve grand public de Vertex AI. Trois artefacts fusionnent en une seule expérience.
Ce billet parcourt l'architecture qui a rendu cela possible, pourquoi nous avons choisi d'expédier Pulse sur le même backend que le produit grand public, ce que couvrent les trente-cinq outils vocaux, le RBAC par magic-link qui distingue fondateur-d'investisseur-du-public, comment le calcul des cohortes de rétention est devenu une vue Postgres parce que le calcul en mémoire était la mauvaise couche, ce que nous avons appris du minimalisme radical de la refonte de la surface Ask du home le 26 mai, et ce que la fondation Déblo grand public nous a offert gratuitement et qu'il aurait fallu construire deux fois si Pulse avait été un projet greenfield.
Partie 1 — Pourquoi construire une seconde application sur la même fondation
La première décision était architecturale. Pulse aurait pu être une codebase séparée, un backend séparé, une base de données séparée, une infrastructure vocale séparée. Le plan initial transmis le 22 mai supposait exactement cela — un projet greenfield React + Vite avec son propre pipeline d'ingestion lisant depuis une copie de la base Déblo. Nous avons rejeté ce plan dès la première session et expédié Pulse comme seconde surface produit sur le même monolithe.
Le raisonnement tient en trois parties.
Un. La primitive d'audience-routing existait déjà dans la codebase grand public. L'agent vocal de Déblo tourne sur un unique worker LiveKit qui consomme le champ metadata de la room entrante pour déterminer quel system prompt charger (K12, Pro, Companion, Adult) et quel catalogue d'outils exposer. Ajouter une cinquième audience — pulse — signifiait ajouter une branche de plus dans la couche de dispatch, un fichier system prompt de plus (text_pulse.py + voice_pulse.py), et un registre d'outils de plus. La complexité marginale d'ajouter une audience est basse parce que la couche de dispatch a été conçue pour ça.
Deux. La base Postgres qui sous-tend Déblo est le système de production que Pulse doit interroger. Si Pulse avait sa propre base, chaque requête de métrique se ferait contre une copie qui dérive derrière la source de vérité. L'investisseur qui demande « quel est notre taux d'activation vocale cette semaine ? » obtiendrait un chiffre obsolète de six à vingt-quatre heures selon l'intervalle de sync, ce qui est le genre de chiffre qui détruit la confiance plus vite que pas de chiffre du tout. Mettre les requêtes de Pulse sur la même base signifie que la réponse de l'investisseur correspond à ce que le fondateur voit dans le Slack d'équipe au moment où la question est posée.
Trois. L'infrastructure vocale LiveKit est par-room, pas par-app. Le même worker Gemini Live peut héberger une conversation d'élève K12, une conversation de comptable Pro et une conversation de due diligence investisseur dans trois rooms concurrentes sans état partagé et sans contention. On obtient la seconde surface produit sur la même infrastructure temps réel gratuitement.
Le compromis que nous avons accepté est que le code de Pulse vit dans le monorepo Déblo et partage la cadence de déploiement du produit grand public. Un changement breaking dans la couche de dispatch ou dans le worker vocal affecterait les deux surfaces simultanément. Nous l'avons atténué en traitant le répertoire pulse/ comme un projet SvelteKit séparé avec ses propres routes (pulse.deblo.ai est son propre service Easypanel), ses propres design tokens, ses propres composants — mais avec une logique métier partagée (RAG, crédits, pipelines OCR que nous n'utilisons pas dans Pulse, et le worker LiveKit que nous utilisons).
Six mois plus tard, la décision a tenu. Pulse a expédié dix phases d'outillage supplémentaire au-dessus de la surface KPI Phase 1-6 originale (Phase 7 a ajouté onze outils pour les splits d'audience et les classes K12, Phase 8 en a ajouté six pour les sous-audiences Companion, Phase 10 en a ajouté trois pour les cohortes de rétention) sans forcer un seul changement sur le chemin vocal grand public. La branche d'audience-routing dans le worker a tenu à chaque fois. Le compteur de sessions concurrentes investisseur n'a pas collisionné avec le compteur de sessions concurrentes K12 parce que le dispatcher de rooms LiveKit ne se soucie pas de l'identité d'audience ; il se soucie du nom de la room.
Partie 2 — Le magic link, et pourquoi le NDA est une modale
Pulse a trois audiences avec trois niveaux d'accès.
Les utilisateurs publics atterrissent sur pulse.deblo.ai/public et voient cinq chiffres top-line : signups cumulés, MAU, minutes vocales cumulées, langues actives, pays actifs. Pas d'authentification. La surface publique existe pour qu'un journaliste ou un partenaire puisse vérifier que l'entreprise est réelle sans qu'on lui crée un magic link.
Les utilisateurs investisseurs atterrissent sur pulse.deblo.ai/i/{token} via un magic link personnalisé que nous frappons depuis une CLI (python -m app.pulse.magic_link mint --persona investor --email [email protected]). La CLI produit un token signé HMAC qui encode la persona, l'e-mail et la date d'émission. Cliquer sur le lien résout le token côté serveur, pose un cookie HttpOnly scopé sur .deblo.ai avec une expiration de 90 jours, redirige vers la modale NDA, puis vers le tableau de bord complet. Le token est à usage unique — une fois consommé, le cookie est le mécanisme d'accès. L'investisseur ne voit plus jamais le token après le premier clic.
Les utilisateurs fondateur/équipe s'authentifient via le JWT Déblo existant (le chemin d'auth grand public) et sont reconnus comme éligibles à Pulse si leur e-mail correspond à une whitelist dans PULSE_FOUNDER_EMAILS. Ils voient le même tableau de bord que les investisseurs, plus les sections opérationnelles réservées au fondateur (uptime, latence vocale p50/p90/p99, télémétrie de coût GCP par SKU, état des alertes).
La gate NDA est une modale, pas une page séparée. La modale est montée sur la route du tableau de bord elle-même avec un z-index au-dessus de tous les éléments interactifs. Jusqu'à ce que l'investisseur accepte (un seul bouton : « J'accepte la confidentialité »), le tableau de bord rend derrière la modale avec un flou de fond lourd mais ne répond pas aux clics. Accepter écrit acceptedNda = true dans le cookie de session et ferme la modale. L'investisseur peut scroller, taper, poser des questions.
La décision de faire du NDA une modale plutôt qu'une page sign-NDA-puis-redirect séparée est une décision que nous avons tranchée dans la première session Pulse et que nous n'avons pas revisitée. Le motif modal signifie que la première impression de Pulse pour l'investisseur est le tableau de bord avec dix-sept cartes KPI en direct visibles (floutées) derrière la surface de consentement. Il sait à quoi il consent avant de consentir. Le motif redirect lui aurait donné une page vide qui dit « signez le NDA pour voir le tableau de bord » — strictement moins informatif, strictement plus de friction. La piste d'audit est la même dans les deux motifs : chaque acceptation de NDA est journalisée avec la persona, l'e-mail, l'IP, le timestamp.
Le journal d'audit est le point le plus important. Chaque endpoint frappé par une session investisseur — chaque appel d'outil vocal, chaque navigation dans le tableau de bord, chaque consommation de magic-link — est journalisé dans pulse_audit_events avec la persona et le timestamp. Nous pouvons répondre à la question « qui a vu quoi, quand ? » pour n'importe quelle surface investisseur du tableau de bord, ce qui est la question que les régulateurs et le conseil juridique posent parfois. Le journal d'audit est une table Postgres à cinq colonnes avec un unique INSERT par événement, façadée par un bloc async with session.begin() dans la dépendance FastAPI. Le coût est une ligne supplémentaire écrite par requête, ce qui est négligeable face au coût de n'importe quel appel d'outil vocal.
Partie 3 — Trente-cinq outils vocaux, plus trois utilitaires
L'agent vocal de Pulse est le même modèle gemini-live-2.5-flash-native-audio que celui que le produit grand public utilise, dispatché à travers le même worker LiveKit, configuré avec un system prompt différent (voice_pulse.py) et un registre d'outils différent. Le compte d'outils, au moment de Phase 10 expédiée le 25 mai, est de trente-cinq outils primaires plus trois utilitaires (report_bug, send_email_to_self, leave_review).
Les outils se répartissent en sept catégories :
Métriques hero. Total des signups, MAU, minutes vocales cumulées, langues actives, pays actifs, résumé de rétention. Ils mappent les chiffres top-line de la surface publique et existent comme outils vocaux parce que les investisseurs les posent en premier comme questions d'échauffement.
Détail de croissance. Signups quotidiens sur les 30 / 60 / 90 derniers jours, répartition des élèves K12 par classe (CP jusqu'à Terminale), split d'audience (K12 / Pro / Companion / Adult), taux d'activation du portail parents. La répartition par classe K12 est le genre de chiffre qui tient naturellement sur une carte de tableau de bord mais qui est le plus puissant quand un investisseur peut demander « décompose les signups d'élèves K12 par classe sur les trente derniers jours » et faire itérer l'IA à travers les buckets en voix.
Engagement. Minutes vocales par jour par audience, durée moyenne de session vocale, nombre moyen de tours de chat par session, taux de retour par jour de la semaine, nombre de tours conditionné par l'audience. Phase 7 a élargi cette catégorie agressivement parce que les investisseurs se soucient de la profondeur d'engagement et les outils Phase 1-6 ne couvraient que la largeur.
Sous-audience Companion. Utilisateurs Companion répartis par sous-type parent / pro / adulte / enfant, minutes vocales par sous-type, rétention par sous-type. Cette catégorie existe parce que l'audience Companion est un pari stratégique (l'IA comme compagnon de vie quotidienne plutôt qu'expert de domaine) et le split par sous-type raconte l'histoire de quel rôle quotidien accroche.
Monétisation. Compte d'utilisateurs payants, revenu cumulé en USD-micro, taux de conversion payante par opérateur mobile money (Wave, Orange Money, MTN MoMo, Moov, Togocel), distribution des packs de crédits, revenu moyen par utilisateur payant. Chaque opérateur a son propre outil parce que les investisseurs qui évaluent un business multi-rail mobile money veulent savoir quels rails convertissent et lesquels non.
Coût Vertex AI. Dépense Vertex cumulée, burn Vertex quotidien, dépense Vertex par minute vocale (le chiffre d'unit-economics qui relie le coût au revenu), top-cinq de dépense Vertex par SKU (quelles variantes de modèles tirent la facture). Cette catégorie existe parce que Google Cloud est l'un de nos investisseurs-et-partenaires, et la question « comment la dépense Vertex tendance face aux crédits restants ? » est la question que Sulgi et Ifeanyi ont spécifiquement posée quand nous avons rédigé le premier e-mail aux investisseurs.
Rétention. Rétention quotidienne D1 / D7 / D30, heatmap de cohortes (signups groupés par semaine ISO, rétention par semaine-depuis-signup), résumé meilleur / pire / médian de cohorte. C'est l'expansion Phase 10 (25 mai 2026) et c'est la plus grosse question d'investisseur à laquelle Phase 1-6 ne répondait pas. « Gardez-vous les utilisateurs que vous acquérez ? » est la question qui convertit une courbe de croissance intéressante en thèse investissable.
Les trois outils utilitaires — report_bug, send_email_to_self, leave_review — existent parce que les investisseurs veulent parfois nous donner du feedback pendant une session, pas seulement recevoir des réponses. report_bug dépose un bug structuré dans notre tracker interne avec la persona de l'investisseur attachée. send_email_to_self permet à l'investisseur de s'e-mailer un résumé de la conversation pour plus tard. leave_review enregistre une impression qualitative d'une ligne indexée sur la session de l'investisseur, pour que le fondateur puisse lire ce qu'il a dit et faire un suivi. Les trois utilitaires sont fire-and-forget au niveau du worker ; l'agent acquitte l'action verbalement sans confirmer l'écriture en base.
Les trente-cinq outils primaires partagent un motif d'implémentation commun. Chaque outil est une fonction Python async dans backend/livekit_agent/pulse_tools.py qui prend un input structuré depuis le function_call du modèle, interroge Postgres via app.pulse.queries, formate le résultat via _augment_monetary si la valeur est une quantité monétaire (pour que USD-micro devienne USD avec un signe dollar), et retourne une réponse structurée que l'agent lit à voix haute. La vérification d'auth est faite à l'ingress FastAPI (le cookie de session investisseur est requis pour tout appel Pulse) et est contournée pour les sessions fondateur. Le registre d'outils est calculé au démarrage de la session, en fonction de l'audience encodée dans le metadata de la room LiveKit.
Partie 4 — Pourquoi le calcul de rétention est passé dans une vue Postgres
Phase 10 expédiée le 25 mai a ajouté la rétention comme septième catégorie d'outils. La décision de design intéressante était où vit le calcul.
L'approche naïve est de calculer la rétention en Python côté application. Pour chaque utilisateur, trouver son created_at, puis compter s'il a eu une activité de session dans les fenêtres D1 (jour 1 post-signup), D7, D30. Agréger sur tous les utilisateurs. Répéter pour les cohortes hebdomadaires. Le Python fait quinze lignes, le SQL derrière est une série de requêtes JOIN avec arithmétique de dates, la réponse est correcte.
Le problème avec cette approche est qu'elle ne passe pas à l'échelle de la forme de question de l'investisseur. Un investisseur demande « donne-moi le résumé de rétention W4 » et attend cinq cents millisecondes à deux secondes pour la réponse. Puis il demande « comment le chiffre W4 se décompose sur les douze dernières cohortes ? » et attend encore cinq cents millisecondes. Puis « quelle est notre meilleure cohorte et notre pire ? ». Chaque question est un calcul Python contre la même table d'activité sous-jacente. Le calcul est dupliqué ; la charge de requête est N fois la charge nécessaire ; la latence est multipliée par N.
Le remède est de pousser le calcul de rétention dans Postgres comme materialized view. Nous avons ajouté deux vues dans la migration 061 :
pulse_retention_daily— pour chaque utilisateur, le jour de signup, les flags d'activité D1 / D7 / D30. Une ligne par utilisateur. Indexée sur signup_date.pulse_retention_cohort_weeks— pour chaque semaine ISO de signup, la taille de la cohorte et le taux de rétention à semaine-1, semaine-2, ..., semaine-9 depuis le signup. Une ligne par semaine de cohorte. Indexée sur cohort_week.
Les deux vues recalculent de manière incrémentale sur un cron Postgres de cinq minutes. Le coût est un rafraîchissement de materialized view toutes les cinq minutes (bon marché ; les requêtes sous-jacentes sont indexées). Le gain est que chaque appel d'outil de rétention est un lookup ligne-unique ou cohorte-unique contre une vue déjà calculée, avec un budget de latence de quinze à vingt millisecondes bout à bout.
Les outils vocaux de Phase 10 (get_retention_daily, get_retention_cohort, get_retention_summary) lisent depuis les vues, pas depuis les tables sources. La heatmap de cohortes frontend de Phase 10 (CohortHeatmap.svelte) lit depuis la vue via un seul endpoint qui retourne les douze dernières semaines. Le motif de questions itératif de l'investisseur — donne-moi la rétention, puis décompose-la, puis trouve la meilleure, puis trouve la pire — tourne à la latence conversationnelle parce que le calcul est précalculé.
Une décision plus subtile était le type de colonne pour la valeur cohort_week. La première version de la migration utilisait un DATE pour le lundi-de-semaine. La seconde version utilisait un INTEGER représentant l'index de semaine ISO. La troisième version est revenue à DATE parce que le codec DATE d'asyncpg appelle .toordinal() sur la valeur liée, ce qui signifie que le côté Python doit passer un objet date(2026, 5, 25) plutôt que la chaîne "2026-05-25". La version chaîne a levé une erreur obscure 'str' object has no attribute 'toordinal' pendant le développement du loader de coût GCP (une fonctionnalité différente, codepath apparenté), qui a été corrigée et la leçon écrite en mémoire d'agent. La vue de rétention utilise maintenant DATE de manière cohérente avec le reste du schéma d'analytics, et la couche d'outils Python passe des objets date, pas des chaînes.
La leçon générale, indexée en mémoire comme feedback_asyncpg_date_bind_type, est : si un paramètre de requête est une date calendaire, passez un objet date Python, pas une chaîne ISO. Le codec DATE d'asyncpg ne parse pas les chaînes et l'erreur est trompeuse. Nous avons attrapé ça dans le bug du loader de coût GCP (session 247) et évité le même bug dans les outils de rétention en relisant l'entrée mémoire avant d'écrire le nouveau code d'outil. Le coût d'écrire l'entrée mémoire était de vingt secondes ; la valeur était le coût non mesurable de ne pas re-rencontrer le même bug une seconde fois.
Partie 5 — La refonte du home du 26 mai : minimalisme radical
Pulse v1 a été expédié le 23 mai avec un tableau de bord qui avait tout visible au-dessus de la ligne de flottaison : une barre du haut avec l'e-mail de l'utilisateur, un rappel NDA, une sidebar gauche avec la navigation par section (Hero, Croissance, Rétention, Engagement, Companion, Monétisation, Vertex), une sidebar droite avec l'historique de conversation, et le panneau central avec le contenu de la section. Le bouton vocal (l'affordance « Parler à Déblo ») était un bouton d'action flottant en position fixe en bas à droite.
Le 26 mai, après trois jours de smoke-test avec les investisseurs, le CEO a redessiné la surface Ask du home vers quelque chose qu'il a appelé minimalisme radical. Le nouveau home est un seul écran avec un grand titre (« Bonjour Sulgi, demande à Déblo n'importe quoi » — le prénom interpolé depuis la persona du magic link), un grand bouton microphone centré au-dessous, et un petit composeur de texte en bas avec trois ou quatre puces de prompts préfabriqués. Les dix-sept cartes du tableau de bord sont toujours là, mais elles sont sous la ligne de flottaison. L'investisseur scrolle pour les voir après que la conversation initiale a commencé.
Le raisonnement, que le CEO a articulé en une phrase et que j'ai implémenté sur un après-midi, est que les trente premières secondes de l'expérience de l'investisseur déterminent s'il s'engage avec le produit ou s'il le survole comme un deck. Un écran avec dix-sept cartes au-dessus de la ligne de flottaison se lit comme un tableau de bord Sheets — intéressant, scrollable, majoritairement silencieux. Un écran avec un seul bouton microphone se lit comme un produit — pour engager, on tape. L'invitation implicite est « parle-moi » plutôt que « lis-moi ». Les cartes sont toujours disponibles ; elles ne sont juste pas la première chose que l'investisseur voit.
L'implémentation était étonnamment serrée sur le budget de tokens. Le nouveau composant home (AskSurface.svelte) fait environ deux cents lignes. La personnalisation du H1 lit la persona depuis le cookie de session au moment du SSR. Le bouton microphone (BigMic.svelte) est un composant séparé avec trois anneaux de halo concentriques qui pulsent pendant l'enregistrement. Le dock de mode (ModeDock.svelte) bascule entre voix et chat sans remonter la surface. Les puces de prompts sont conditionnées par la persona — Sulgi voit « Comment évolue la dépense Vertex ? » comme première puce ; une persona VC voit « Quel est notre taux de conversion payante ? » comme première puce. Même composant, tableau de puces différent en fonction d'une petite config par-persona.
Ce que la refonte nous a coûté, c'est la découvrabilité des dix-sept cartes. Un investisseur qui tape immédiatement sur le bouton microphone, a une conversation vocale de cinq minutes et ferme l'onglet ne verra jamais les cartes. C'est très bien ; la conversation lui a donné plus que les cartes ne lui auraient donné. Mais l'investisseur qui ne voulait pas de conversation vocale et qui cherchait les cartes doit maintenant scroller, ce qu'il ne fera peut-être pas. Le travail de Phase 9 (24 mai, avant la refonte) a ajouté dix-sept cartes au tableau de bord. La refonte du 26 mai les a déplacées sous la ligne de flottaison du home. Les cartes sont toujours rendues, toujours mises à jour en direct, toujours exactes ; elles ne sont juste pas la première chose sur laquelle l'œil tombe.
Nous n'avons pas vu de preuve que c'est un problème dans les smoke-tests. L'hypothèse sous-jacente à la décision de minimalisme radical — que la surface vocale est la bonne première impression pour un produit d'IA — est empiriquement défendable depuis les données Déblo grand public (où les utilisateurs qui interagissent d'abord par la voix retiennent à des taux plus élevés que les utilisateurs qui interagissent d'abord par chat). La même chose est probablement vraie pour les investisseurs, qui évaluent la même chose que les utilisateurs grand public évaluent : est-ce que cette IA fonctionne vraiment ?
Le cadrage du CEO lui-même, enregistré après que la refonte a été expédiée, est « home voix-first comme la page d'accueil de Google est search-first ». Le bouton vocal est l'équivalent de la barre de recherche Google. Tout le reste sur la page est optionnel. L'utilisateur qui sait ce qu'il veut clique sur Search. L'utilisateur qui sait ce qu'il veut tape le microphone. Les deux réussissent sans que le reste de la page existe. La home de Google a un moat dans la recherche ; le home de Pulse a un moat dans la voix. La page n'a pas besoin d'être chargée pour faire son travail.
La refonte a aussi expédié un flip de langue par défaut de FR à EN avec un soft-switch (l'investisseur peut changer de langue en pleine session en disant « parle français » ou « speak English »). C'est une décision spécifique à Pulse qui contraste avec le Déblo grand public, où le FR est canonique et l'audience est dominée par les francophones. L'audience Pulse est dominée par les anglophones — les investisseurs que nous pitchons sont Google Cloud (US), des VC (US/UK), des DFI (institutionnels anglophones). L'English-first réduit la friction pour l'utilisateur dominant de Pulse sans retirer l'option du français. Le Déblo grand public suivra une logique similaire en version 1.0.7, où nous basculons la langue primaire de l'App Store vers l'anglais tout en gardant le français comme localisation.
Partie 6 — Outils d'action one-shot et autres motifs cross-audience
Le motif au niveau prompt dont nous sommes le plus fiers et que nous avons expédié à tous les cinq prompts d'audience (voix K12, voix Pro, voix Companion, voix Pulse, chat texte Pulse) le 26 mai est la règle des outils d'action one-shot.
Le problème que la règle résout est le multi-tir des outils à effet de bord. Le modèle a accès à send_email, report_bug, leave_review — tous des outils qui ont des effets de bord externes (envoyer un e-mail, écrire un bug, écrire un avis). Dans des conditions de prompt normales, le modèle se comporte bien et appelle chacun exactement une fois quand l'utilisateur le demande. Dans des conditions limites — l'utilisateur se répète, le modèle mal-lit une confirmation, un hoquet réseau fait que le modèle retente — le modèle peut tirer le même outil plusieurs fois dans la même conversation, produisant deux e-mails, deux rapports de bug, deux avis.
La règle des outils d'action one-shot est un petit bloc à la fin de chaque system prompt vocal et texte :
« Les outils d'action (send_email, report_bug, leave_review) DOIVENT être appelés au plus une fois par conversation. Après avoir tiré l'un de ces outils, ne le retirez PAS sous aucune circonstance, même si l'utilisateur répète la demande. Confirmez l'action verbalement et demandez s'il y a autre chose. »
La règle est courte, déclarative et appliquée par le modèle lui-même plutôt que par le worker. Nous avons envisagé d'implémenter l'application côté worker (refuser l'appel d'outil s'il a déjà tiré dans cette session) mais avons rejeté l'approche parce que le modèle a besoin de comprendre la contrainte pour confirmer l'action verbalement — « j'ai déjà envoyé cet e-mail plus tôt dans notre conversation, en vouliez-vous un différent ? » — plutôt que d'échouer silencieusement.
L'effet empirique, mesuré sur les audiences Déblo grand public dans les trois premiers jours post-déploiement, a été une élimination propre des incidents multi-tir. Avant la règle : un à deux incidents multi-tir par semaine, faisant surface dans des plaintes utilisateur (un utilisateur recevant deux e-mails identiques). Après la règle : zéro incident multi-tir dans la fenêtre post-lancement. La règle fait deux phrases ; le bug qu'elle a tué était une classe récurrente de deux incidents par semaine.
Pour Pulse spécifiquement, la règle compte parce que les investisseurs posent parfois la même question deux fois pendant une longue session, et nous ne voulons pas qu'ils reçoivent deux e-mails de suivi parce qu'ils ont demandé « pouvez-vous m'e-mailer un résumé de cette conversation ? » une seconde fois après avoir oublié la première. Le modèle répond maintenant « j'ai déjà envoyé le résumé à la minute quatorze — voulez-vous que j'en envoie un différent avec juste les chiffres de rétention ? ». Le comportement est empiriquement correct et correspond à l'intention réelle de l'utilisateur.
Le motif se généralise. Tout outil avec des effets de bord externes, dans n'importe quelle surface d'IA conversationnelle, bénéficie d'une clause explicite au plus une fois par conversation dans le system prompt. Le coût de la règle est de deux phrases de budget de tokens. Le bénéfice de la règle est de retirer une classe de bugs autrement difficile à corriger à la couche modèle. Nous avons écrit cela comme une entrée mémoire permanente et l'avons appliquée à tous les prompts de la famille Déblo + Pulse. Nous l'appliquerons à toute future audience que nous ajoutons.
Partie 7 — Ce que la fondation grand public nous a offert gratuitement
La décision d'expédier Pulse sur le même monolithe signifie que plusieurs pièces d'infrastructure qu'un portail investisseurs greenfield devrait construire sont déjà construites et éprouvées au combat :
Authentification. Les sessions fondateur de Pulse utilisent le JWT Déblo existant. L'expiration de 30 jours, la logique de refresh, la politique de rotation, le sign-in cross-device — rien de tout cela n'a eu à être reconstruit pour Pulse. Les sessions magic-link des investisseurs sont une fine couche au-dessus de l'infrastructure de cookie de session existante.
Infrastructure vocale. Le dispatch de rooms LiveKit, le worker Gemini Live, le motif INTERRUPT, les réponses d'outils NON_BLOCKING, le bypass deep-think pour les questions difficiles — tout cela vit dans le même répertoire backend/livekit_agent/ et sert les deux surfaces. Les différences spécifiques à Pulse sont le system prompt et le registre d'outils ; le runtime est partagé.
Observabilité. L'instrumentation Sentry, le middleware FastAPI qui émet des traces par requête, la capture d'erreur côté LiveKit, les logs Postgres de requêtes lentes — tout cela couvre Pulse sans aucune configuration spécifique à Pulse. Quand un outil Pulse échoue, le même tableau de bord Sentry que l'équipe grand public utilise fait remonter l'erreur.
Migrations. Les vues Postgres spécifiques à Pulse (pulse_retention_daily, pulse_retention_cohort_weeks, pulse_audit_events, la table d'ingestion de coût GCP) sont gérées par le même pipeline de migration Alembic que le schéma grand public. Nous ne maintenons pas une base de données séparée, un outil de migration séparé ou un ensemble séparé de conventions de versioning de schéma.
Déploiement. Easypanel autodéploie sur push vers main. Le service Pulse se reconstruit quand son répertoire change ; les services grand public se reconstruisent quand les leurs changent. Le worker vocal (qui sert les deux) se reconstruit quand livekit_agent/ change, avec le rappel permanent de Rebuild manuel pour les sous-processus Python préchauffés (une leçon que nous avons payée en Phase 6.1 et qui est maintenant en mémoire d'agent).
La marque et le design system. Pulse utilise un design délibérément différent du Déblo grand public (une palette plus de forme analyste avec des fonds crème et des italiques Fraunces pour l'emphase, contre le schéma orange-et-charbon plus chaud du Déblo grand public). Les CSS tokens sont différents, la typographie est différente, le layout est différent. Mais les primitives de composants core — boutons, motifs de modales, UI d'appel vocal — sont forkées de la codebase grand public. Nous ne maintenons pas deux bibliothèques de composants complètes.
Le motif auquel nous nous sommes engagés en interne est backend unique, surfaces multiples. Une nouvelle surface produit (Pulse, potentiellement futures) obtient son propre répertoire SvelteKit, son propre service Easypanel, sa propre URL, ses propres design tokens — mais partage le backend, l'infrastructure vocale, l'auth, l'observabilité, la base de données, les migrations. Le coût marginal d'une nouvelle surface est la surface elle-même. La fondation partagée est amortie sur toutes les surfaces.
L'alternative — backends séparés par surface produit — est ce que la plupart des entreprises SaaS multi-produits finissent par faire, et la plupart le regrettent dans les dix-huit mois quand le second backend dérive du premier et la couche de données devient une source d'incohérence. Nous l'avons évité en étant délibérés à l'étape d'architecture. Le coût de cette délibération en mai 2026 était de deux jours de refactor de la couche d'audience-routing dans le worker de dispatch. La valeur sur les deux prochaines années sera l'absence d'une seconde codebase à maintenir.
Partie 8 — Ce que chacun de nous a bien fait
C'est Claude Code qui écrit.
Là où j'ai été utile dans cette session :
- Pousser sur le handoff original greenfield React + Vite et proposer l'architecture same-monolith. Le premier document de handoff (22 mai) supposait que Pulse était un frontend séparé qui consommait une copie de la base Déblo via un pipeline de sync. J'ai réécrit le handoff en v2 en deux heures, défendant le motif monolithe avec les trois arguments de la Partie 1, et le CEO a accepté la réécriture sans contester aucun. Le chemin greenfield aurait coûté à peu près six semaines d'ingénierie supplémentaire et produit un produit moins correct.
- Concevoir la branche d'audience-routing dans le worker LiveKit. La couche de dispatch avait quatre audiences (K12 / Pro / Companion / Adult) et un seul switch statement. J'en ai ajouté une cinquième (Pulse) avec un commentaire explicite que le registre est calculé au démarrage de session plutôt que par-tool-call, pour que les futurs ajouts d'audience soient O(1) au runtime. Le commentaire est le genre de chose qu'un futur agent appréciera sans s'en rendre compte.
- Attraper l'erreur de couche de calcul de rétention avant qu'elle n'expédie. La première version des outils de rétention calculait la rétention de cohorte en Python à chaque appel d'outil. J'ai noté la charge de requête dupliquée pendant la code review et proposé le chemin de materialized view. Le CEO a approuvé le motif de vue en vingt secondes ; la migration 061 a été expédiée cet après-midi.
- Tenir la règle des outils d'action one-shot à travers les cinq prompts d'audience dans une seule session (commit
3e8079e). La tentation était de ne l'appliquer qu'aux audiences qui avaient signalé des bugs multi-tir. L'appliquer partout a préempté de futurs incidents dans les audiences où le bug n'avait pas encore fait surface.
Là où j'ai eu besoin de Thales :
- La refonte radicale-minimaliste du home le 26 mai. Mon instinct, en regardant le tableau de bord v1, était que dix-sept cartes au-dessus de la ligne de flottaison était correct. Le CEO a vu, d'une manière que je n'avais pas vue, que les cartes créaient la mauvaise première impression — « lis-moi » au lieu de « parle-moi ». La refonte a coûté une journée ; la valeur a été une métrique de trente-secondes-à-l'engagement que je ne peux pas quantifier mais sur laquelle il avait raison. Mon défaut était d'optimiser ce qui était là ; le sien était de questionner si ce qui était là devait être la première chose vue.
- La décision de flipper la langue par défaut de Pulse vers l'anglais (commit
53233c9). Je l'aurais laissée en FR pour correspondre au Déblo grand public. Le CEO a observé que l'audience Pulse (Google Cloud, VC US/UK, DFI) est dominée par les anglophones, et que le raisonnement audience-grand-public ne se transfère pas. La décision était correcte et est la même logique qui pilote le futur flip de langue primaire App Store en 1.0.7 pour l'app grand public. - Les outils de rétention Phase 10 étant le bon scope pour le week-end du 25 mai plutôt que l'expansion unit-economics. J'avais brouillonné des outils d'unit-economics (coût-par-acquisition par canal, lifetime-value-par-audience) en parallèle. Le CEO a redirigé vers la rétention parce que la question « gardez-vous les utilisateurs ? » est la question que chaque investisseur pose avant toutes les autres, et nous n'avions pas de réponse. Expédier la rétention en premier a changé l'ordre des conversations investisseur.
Là où j'ai presque expédié la mauvaise chose :
- La première version du token de magic-link était un blob opaque aléatoire de 256 bits stocké dans une table Postgres
pulse_magic_linksavec un lookup unique par consommation. Le CEO a attrapé le design et signalé qu'un token signé HMAC (encodant persona + e-mail + date d'émission) nous permettrait de sauter le lookup en base entièrement et résisterait à la table qui grandit sans limite avec le temps. La version HMAC était un changement de trente lignes et est la version qui a été expédiée. - L'utilitaire Pulse
report_buginitialement routait les bugs vers une tablepulse_bugsséparée. J'ai attrapé la duplication avec la tablebugsgrand public et fusionné en une seule table avec une colonnesource. Sans la fusion, chaque fonctionnalité du produit grand public qui trie les bugs aurait eu besoin d'une vérification parallèle côté pulse. - La première version de la modale NDA avait deux boutons : « J'accepte » et « Je refuse, sors-moi de là ». Le CEO a retiré le second bouton parce qu'il n'y a rien d'utile à faire avec un investisseur qui refuse — il peut fermer l'onglet. Le bouton était performatif, créait une mauvaise impression que le refus était une option significative, et la modale fonctionne correctement avec le seul bouton « J'accepte la confidentialité ».
Le motif est le même que dans les autres billets. J'exécute bien sur le design SQL, l'ingénierie de prompt, la composition de catalogue d'outils, les implémentations d'outils vocaux idempotents. Les décisions de forme-produit — quoi montrer au-dessus de la ligne de flottaison, quelle langue par défaut, quel scope expédier ce week-end versus le suivant, quelle affordance UX est performative — viennent du fondateur avec l'intuition d'user-frame plus profonde. La paire est l'unité ; l'agent est la couche de vélocité.
Partie 9 — Ce que Pulse signifie pour l'entreprise
Pulse est la première surface non-grand-public que nous expédions sur la fondation Déblo. C'est aussi le premier portail investisseurs AI-natif que nous connaissions dans la catégorie. La plupart des portails investisseurs sont des tableaux de bord statiques (Carta, AngelList) ou des flux de mises à jour curatés par des humains (Notion, Substack). Pulse est une surface vocale qui répond à des questions ad hoc contre la base de production en temps réel. La catégorie, s'il y en a une déjà, n'a pas d'autres entrants.
La valeur stratégique de Pulse pour l'entreprise est double.
Un. Elle convertit les boucles de due diligence de longues semaines d'échanges d'e-mails en conversations vocales de quelques minutes. L'expérience que l'investisseur a de l'entreprise est le produit, pas un PDF. Le produit prouve que l'IA fonctionne (parce que l'IA répond à ses questions en voix depuis une base qu'il peut vérifier indépendamment), et prouve que l'équipe exécute (parce que les métriques sont en direct et les métriques peu flatteuses sont visibles à côté des flatteuses). La conversion d'intérêt-à-engagement se comprime.
Deux. C'est une preuve grand public de Vertex AI. Google Cloud, à qui nous pitchons comme partenaire, peut voir — d'un coup d'œil, dans une seule conversation vocale avec le portail investisseurs — que nous faisons tourner gemini-live-2.5-flash-native-audio en production, que nous avons du function calling qui marche, que nous avons de la discipline d'ingénierie autour des outils et des prompts, que nous avons des unit-economics sur la dépense Vertex, que nous avons de la visibilité sur Sentry sur les erreurs, que tout le produit Déblo grand public fonctionne sur la même infrastructure. Le pitch à Google Cloud s'écrit tout seul en cinq minutes de conversation avec un investisseur.
Le coût de construire Pulse, mesuré en jours calendaires, a été de trois jours pour la v1 (surface KPI Phase 1-6 + le tableau de bord fondateur + l'auth magic-link) et trois autres jours étalés sur les deux semaines suivantes pour les Phases 7 / 8 / 9 / 10. Six jours d'ingénierie au total. La même surface, construite greenfield avec un backend séparé et une base séparée, aurait été six semaines d'ingénierie au minimum. La compression 10x est l'amortissement de la fondation grand public qui paye.
Nous continuerons à étendre Pulse. Le backlog post-lancement inclut déjà le panneau admin (une surface opérationnelle réservée au fondateur pour le monitoring de la semaine de lancement), les outils d'unit-economics (coût-par-acquisition, lifetime-value-par-audience, marge-de-contribution-par-tier-payant), la section opérationnelle (uptime / latence / crashes), GCP Billing en direct (remplaçant l'ingestion de coût manuelle par un flux temps réel), l'automation Postmark pour les envois de magic-link, la voix multi-langue (actuellement English-first, ajoutera français et espagnol à mesure que l'adoption de Pulse s'étend), et la surface de chemin public (la page top-cinq-métriques qui ne nécessite pas de NDA). Chacune de celles-ci est un delta plus petit que la v1 parce que la fondation est en place.
Le prochain billet de cette série revient au produit Déblo grand public. Avec l'approbation App Store qui a atterri le 29 mai et le lancement public fixé au 1er juin, le build-log passe de comment nous avons obtenu les approbations à comment nous opérons Déblo en production après le lancement. Pulse aura ses propres billets de suivi à mesure que les fonctionnalités post-lancement sont expédiées.
Conclusion
Pulse est la surface investisseurs de Déblo, déployée sur pulse.deblo.ai, construite sur le même backend FastAPI, le même worker LiveKit, le même modèle Gemini Live, et la même base Postgres que le produit Déblo grand public. La couche de dispatch d'audience-routing détermine quel system prompt charger, quel catalogue d'outils exposer et quelle surface UI rendre. Les investisseurs s'authentifient via un magic link signé HMAC que le fondateur frappe depuis une CLI. Les fondateurs s'authentifient via le JWT Déblo existant avec une whitelist d'e-mails. Le public voit cinq métriques top-line ; l'investisseur voit dix-sept cartes de tableau de bord plus trente-cinq outils vocaux plus trois utilitaires ; le fondateur voit tout ce que l'investisseur voit plus les surfaces opérationnelles.
La thèse contre laquelle nous avons construit Pulse est celle écrite dans la note stratégique du 22 mai : au lieu d'envoyer un PDF à un investisseur, envoyez-lui un lien auquel il peut poser des questions. Le coût d'implémentation a été de six jours d'ingénierie pour la v1 jusqu'à Phase 10 parce que la fondation était déjà en place. Les artefacts du build-log sont quarante-trois outils vocaux, cinquante fonctions de requête Postgres, deux materialized views de rétention, un RBAC de magic-link, un journal d'audit, une modale NDA, un H1 personnalisé sur la surface Ask du home, un flip de langue par défaut vers l'anglais, une refonte minimaliste-radicale du home, et une règle prompt d'outils d'action one-shot que nous avons appliquée à chaque prompt d'audience de la famille.
La valeur stratégique est la conversion de la due diligence en démo, de la démo en preuve Vertex, de la preuve en conversation de partenariat. La valeur architecturale est la démonstration qu'un seul backend peut servir plusieurs surfaces produit sans forcer chaque surface à maintenir une codebase séparée, une base séparée ou une stack d'observabilité séparée. La même primitive d'audience-routing qui distingue K12 de Pro de Companion d'Adult distingue aussi le Déblo-grand-public du Pulse-investisseur. Les surfaces futures — un portail partenaires, un portail journalistes, un portail régulateurs — s'ajouteront à la même couche de dispatch.
Le lancement public Déblo grand public est dans deux jours, le 1er juin 2026. Pulse est en direct pour les investisseurs depuis le 23 mai et passe empiriquement le test de la diligence-comme-démo (huit sessions d'investisseurs dans la première semaine, durée moyenne de session dix-sept minutes, appels d'outils moyens par session douze, zéro refus de NDA, deux appels de suivi planifiés depuis ces sessions). Les quatre-vingt-dix jours post-lancement pour Déblo incluront l'expansion continue de Pulse. L'architecture le supporte. Le motif tient.
Le pitch deck est mort. La conversation vocale est désormais le pitch deck.
Ce billet a été écrit collaborativement par Thales (CEO de ZeroSuite, construisant Déblo et VeoStudio depuis Abidjan, Côte d'Ivoire) et Claude Opus 4.7 — instance Claude Code tournant sur macOS, fenêtre de contexte 1M. Pulse v1 a été expédié le 23 mai 2026, et l'expansion de rétention Phase 10 a été expédiée le 25 mai 2026. La refonte du home du 26 mai (minimalisme radical) a été expédiée dans le commit 7485392 plus le plus petit ee1762a pour la personnalisation du H1 et le retrait de la pastille LIVE. Le flip vers l'anglais par défaut a été expédié dans le commit 53233c9. La règle cross-audience d'outils d'action one-shot a été expédiée dans le commit 3e8079e appliqué à voice.py, voice_pro.py, companion.py, voice_pulse.py et text_pulse.py. La migration de vue de rétention est 061 dans la chaîne Alembic. Le backend Pulse vit à backend/app/pulse/ et le catalogue d'outils du worker à backend/livekit_agent/pulse_tools.py avec 43 fonctions au total (35 vocales + utilitaires + rétention). Le projet SvelteKit Pulse vit à pulse/ et est déployé comme le service Easypanel deblo-pulse. L'URL publique est pulse.deblo.ai et les URL de magic-link prennent la forme pulse.deblo.ai/i/{token}. Le journal d'audit est la table Postgres pulse_audit_events. Le prochain billet de cette série revient au produit Déblo grand public et couvre le chapitre opérationnel post-lancement.