System
The command center watching itself — scheduler pulse, per-site snapshots, the live monitor log, instruments, and operational memory.
- Sites
- 2
- Reporting
- 2/2
- Last run
- 1mo ago
- Snapshot
- Oct 24, 10:17
Per-site telemetry
Cron monitor stream
Skills inventory
.claude/skills/arianeIn this project ('seo'), anything touching Ichai's world goes through Ariane, never through a key left on the side. Open here: his mail (read, search, send, reply, archive, unsubscribe); his calendar and tasks (see, create, move, answer an invitation); WhatsApp (read a conversation, search, send a message, a photo, a voice note); his contacts (search, create, correct); his Drive files (list, read, upload, share, write a document); notifying him on Telegram; his Ariane apps (Tube, Documents, Atlas, YouTube); his banks; hosting this project (put it online, rebuild, start, stop, its domains, its logs) and the sandbox (make a page public). USE IT as soon as a request needs one of those — 'check my mail', 'send him an email', 'what do I have this week', 'put that on Thursday', 'send him a message', 'what did he say', 'what is his number', 'put that on Drive', 'find that document', 'let me know when it is done', 'download that video', 'file that document', 'how much is left', 'deploy', 'rebuild the container', 'restart the app', 'add a domain', 'make the page public' — and whenever the information asked for might be there. The exact list of verbs is asked of Ariane (the `ariane` command); it is not written here. 'Note that' and 'remind me' go to `ariane agents.commitment_add` (Ariane's commitments, since 21/10/2026), never to a file; a note with a time is reminded at that time, on Telegram and on his phone.
.claude/skills/proposeAnalyse TOUT (les sites, les clients, la mémoire, et notre propre outillage) puis PROPOSE tout — nouvelles idées d'actifs (outils gratuits, calculateurs, comparateurs, observatoires), contenu, local, conversion, distribution, business, système. Panel de 8 agents Opus avec MÉMOIRE (interdiction de ressortir une idée déjà au registre). Écrit tout au registre pour que rien ne s'évapore. À DÉCLENCHER sur "/propose", "propose-moi des trucs", "qu'est-ce qu'on peut faire", "des idées neuves", "on fait quoi maintenant", "analyse tout et propose", "qu'est-ce qu'on rate".
.claude/skills/rapportCockpit SEO d'Ichai — rapport INTERNE exhaustif (état + tendance monte/descend, leads, santé technique, alertes mail Google Search Console, ce qui monte/chute, opportunités chiffrées, concurrence DataForSEO, PLAN D'ACTION priorisé par IMPACT, angles morts). À DÉCLENCHER sur "/rapport", "rapport", "fais un rapport", "où on en est", "cockpit SEO". Sans argument = TOUS les sites ; "rapport emet" = Emet seul, "rapport genius" = Genius Prep seul.
.claude/skills/seoLA porte d'entrée du command center — pour chaque site ACTIF (Emet Expertise, Genius Prep) : où on en est (positions, tendance, leads, technique, alertes Google reçues par mail, ce qui ne concourt pas, qui nous doit quoi) PUIS un carnet de commandes concret et numéroté — articles précis, pages à créer, outils/actifs à construire, correctifs techniques, actions hors-site — chacun prêt à lancer d'un « fais le 3 ». À DÉCLENCHER sur "/seo", "on fait quoi", "où on en est", "qu'est-ce qu'on fait cette semaine", "propose-moi des trucs à faire", "des articles", "des pages", "quoi faire sur les sites".
.claude/skills/seo-auditLance l'audit technique SEO d'un site géré par le projet /root/apps/seo (robots.txt, sitemap, title/meta, canonical, headings, JSON-LD, Open Graph, indexabilité) et résume les findings par sévérité. À DÉCLENCHER sur "audit seo", "audite <site>", "check technique SEO", "scanne le SEO de <site>".
.claude/skills/seo-cloudflareGère Cloudflare (DNS, zones, SSL, cache) pour les sites clients du projet /root/apps/seo, via l'API et la clé stockée hors repo (/root/.secrets/cloudflare.env). À DÉCLENCHER sur "cloudflare", "passer un site sur cloudflare", "DNS du domaine", "changer les nameservers", "purge cache cloudflare", "SSL cloudflare", "migrer un domaine".
.claude/skills/seo-cwvMesure les Core Web Vitals (performance, LCP, CLS, TBT/INP) des sites du projet /root/apps/seo via l'API Google PageSpeed Insights — données LAB Lighthouse + champs RÉELS CrUX, findings par sévérité, snapshots versionnés. À DÉCLENCHER sur "core web vitals", "performance <site>", "vitesse <site>", "lighthouse", "pagespeed".
.claude/skills/seo-keywordsRecherche de mots-clés, volumes de recherche réels, SERP/positions et idées de mots-clés via l'API DataForSEO (pay-per-call). Passe par Ariane (seo.dataforseo), aucune clé locale. À DÉCLENCHER sur "volume de recherche", "mots-clés <sujet>", "position de <site> sur <requête>", "idées de mots-clés", "rank tracking", "combien cherchent <requête>".
.claude/skills/seo-monitorSuivi Google Search Console des sites du projet /root/apps/seo (clics, impressions, CTR, position, top requêtes/pages, pages en déclin), lu dans le relevé quotidien d'Ariane, snapshots versionnés. À DÉCLENCHER sur "monitoring SEO", "données GSC", "positions <site>", "régressions SEO", "pages qui baissent", "rapport Search Console".
.claude/skills/seo-onboardOnboarde un nouveau site/client dans le SEO command center (/root/apps/seo) — ajoute l'entrée dans config/sites.json, crée l'arborescence sites/<key>/, génère la fiche client memory/clients/<key>.md et affiche les prochaines étapes. Idempotent. À DÉCLENCHER sur "onboard un site", "ajoute un client SEO", "nouveau site <nom>", "brancher <client>".
.claude/skills/seo-opportunitiesTransforme la data Google Search Console déjà collectée (snapshots GSC du projet /root/apps/seo) en backlog SEO priorisé, SANS aucune API externe — striking distance (pos 5-20), requêtes/pages à fortes impressions mais faible CTR (title/meta à revoir), estimation de clics potentiels. À DÉCLENCHER sur "opportunités SEO", "striking distance", "quick wins <site>", "où gagner du trafic", "backlog SEO", "pages à optimiser".
.claude/skills/seo-reportGénère le rapport mensuel CLIENT en PDF brandé (KPIs GSC + tendance 7j vs 7j, top requêtes/pages, santé technique, ce qui a été fait, prochaines priorités) pour un site du projet /root/apps/seo. À DÉCLENCHER sur "rapport mensuel <client>", "rapport SEO", "PDF client", "reporting".
Knowledge ledger
Decisions
Décisions — CE QUI EST VRAI AUJOURD'HUI
✅ Ce fichier ne contient que des décisions ACTUELLEMENT en vigueur. Une décision annulée, réfutée ou remplacée n'y figure plus : elle vit dans
decisions-historique.md. En cas de contradiction entre les deux fichiers, celui-ci gagne.📕 L'archive sert à comprendre pourquoi on a tranché, jamais à sourcer un fait. Plusieurs affirmations y sont explicitement fausses (elles ont été réfutées par vérification adverse le 20/07) — ne jamais les recycler.
🧾 Les idées, actifs et recommandations ne sont plus ici : ils vivent dans le registre (
backlog.md, sourceledger.json), avec leur statut et leur condition de réveil. Les attentes côté client :waiting-on.md.Restructuré le 2026-08-25.
2026-10-04 — Emet : clients partout en France, prix LMNP fixé par nous, plus de relecture par Nathan (Ichai)
- Le cabinet accepte les clients à distance, partout en France. Les pages nationales (observatoires, outils, articles) peuvent proposer une mission à distance. Ferme la question L472.
- Le prix LMNP est fixé par nous, d'après le marché, et publié sur le site. Ferme L513 (question 1) et L460 (volet LMNP).
- On ne fait plus rien relire à Nathan : formules, textes, articles et outils partent sans sa validation. Toute ligne du registre conditionnée à « Nathan relit / valide » est levée. La rigueur passe par les textes officiels relus et cités.
🏗️ Socle technique
| Depuis | Décision | Pourquoi |
|---|---|---|
| 2026-06-25 | Alertes via Signal CLI (signal-send.mjs) |
déjà en place sur le VPS |
| 2026-06-25 | GSC : geniusprep via OAuth wizytics · emet via service account (/root/.secrets/emet-gsc-service-account.json) |
les deux fonctionnent, testés |
| 2026-06-25 | Skills projet dans .claude/skills/, créés progressivement |
scope au projet, pas de pollution globale |
| 2026-06-26 | Dashboard = conteneur seo + seo.vpsdashboard.space, lit les fichiers plats (/root/apps/seo:/data/seo:ro) |
visuel live sans dépendance ; PocketBase dédié = différé, jamais monté |
| 2026-06-26 | Gratuit d'abord : PSI (gcloud), GSC, kie.ai (images GPT Image 2) | couvre audit/monitoring/CWV/opportunités/GEO |
| 2026-07-16 | DataForSEO branché (/root/.secrets/seo-apis.env) — SERP, volumes, Labs. BrightLocal : non branché, optionnel |
⚠️ solde vérifié le 2026-08-25 : 0,11 $ — à recharger avant tout usage sérieux |
| 2026-07-21 | Cloudflare : Always Online sur emet + geniusprep ; cache HTML 4 h à l'edge sur emet (exclut /api/) |
le site reste en ligne même VPS éteint. ⚠️ Conséquence : deploy.sh (build + up + PURGE CF) est OBLIGATOIRE sur emet, sinon l'ancien HTML est servi 4 h |
| 2026-08-25 | wizytics ABANDONNÉ (Ichai). GSC geniusprep passe en OAuth direct (/root/.secrets/geniusprep-gsc-oauth.env), conteneurs arrêtés |
l'app s'éteignait sans prévenir (5 j puis 3 semaines de données gelées, aucune alerte). En direct : plus de dépendance, et plus de données (250 requêtes, 49 pages, croisement requête × page que wizytics ne donnait pas). ⚠️ Ne pas supprimer le volume wizytics_pocketbase_wizytics_data : seule copie du consentement OAuth |
| 2026-10-14 | Emet : le numéro du site est le portable d'Avi (07 81 75 30 47), jamais masqué (décision d'Ichai) | le 01 89 52 15 14 ne sonne pas ; le masquer (02/10) privait le site de son canal d'appel. Interrupteur phoneActive=true dans src/data/company.ts ; détail dans clients/emetexpertise.md |
| 2026-08-25 | Marva = SORTI DU PÉRIMÈTRE, archivé dans corbeille/marva/, retiré de config/sites.json |
plus d'alerte, plus de rapport, aucune proposition dessus. ⚠️ Corrigé le 2026-09-02 : cette ligne disait « paused:true » — cette clé n'existe dans aucun fichier de config/ (grep -rn paused config/ ne rend rien). Elle est pourtant lue par report-data.mjs:89 et system-check.mjs:29, qui la trouvent donc toujours absente. Ne jamais re-sourcer un statut de site sur paused |
| 2026-08-25 | Un snapshot GSC stocke la limit utilisée |
sans ça, « 0 impression » et « hors des N lignes remontées » sont indiscernables — la confusion a produit un faux constat le jour même (11 articles Genius Prep déclarés invisibles à tort) |
| 2026-08-25 | gsc.mjs stocke 250 requêtes (était 25) |
à 25, la cohorte de mots-clés n'était pas observable : une requête hors du top 25 par volume était indistinguable d'une requête disparue |
Mesure d'audience : Umami sur les trois sites (Emet, Genius Prep, moedcal), décision d'Ichai du 2026-10-14
Ichai veut le même outil partout. Une seule instance Umami (conteneur moed-umami, projet /root/apps/moed/analytics,
sans adresse publique depuis le 14/10 — Ichai ne veut pas d'accès, Umami est pour Claude seul ; lecture par l'IP du conteneur, identifiants et WEBSITE_ID dans /root/.secrets/moed-umami.env), un « site » par domaine.
Le script et l'envoi passent par le domaine de chaque site (/m/s.js, /m/api/send) : conforme aux CSP 'self'.
Aucun cookie, aucun stockage navigateur, Do Not Track respecté, IP non conservée (code de visite qui change chaque mois).
Les beacons maison restent la source des relevés existants (Emet /api/click → clicks.jsonl, Genius Prep /api/track
→ conversion_events) ; chaque événement est AUSSI copié dans Umami par umami.track.
⚠️ Règle CNIL vérifiée le 14/10 (L600), après la pose : telle que posée, la config N'ENTRE PAS dans l'exemption
(UTM dans l'adresse, pas de lien d'opposition, conservation illimitée). Les réglages qui l'y font entrer sont au registre L600 ;
le choix (réglages ou bandeau) revient à Ichai.
⚠️ Cette décision remplace celle du 2026-07-16 (« serveur-first, aucun tracker JS client »), descendue dans l'archive.
➡️ Search Console, CF-IPCountry, cf-edge-snapshot.mjs et les leads restent en place.
💼 Cadre client
| Depuis | Décision | Détail |
|---|---|---|
| 2026-07-13 | Emet : retainer 500 €/mois, facturation le 14 | périmètre : articles + suivi de positions + technique + rapport mensuel |
| 2026-08-25 | Genius Prep : PAUSE LEVÉE — le client a payé. Travail actif repris, paused retiré, monitor:true |
c'est le site à volume réel du portefeuille : 19 583 impressions / 159 clics sur 28 j, contre ~550 impressions pour Emet. La priorisation du portefeuille change en conséquence |
| 2026-08-25 | Notre stack surveille Genius Prep (monitor:true) ; l'ancienne stack geniusprep est désactivée |
ses 5 scripts (seo-snapshot 6h, gsc-monitor 9h, gsc-daily-briefing, gsc-indexing-monitor, seo-daily-check) passaient TOUS par wizytics, abandonné. Les 2 crons actifs auraient envoyé une fausse alerte Signal quotidienne « GSC déconnecté ». Commentés dans le crontab (datés, réversibles), pas supprimés |
| 2026-07-14 | Alerte lead Emet → mail cabinet + a.cohen@emetexpertise.fr + Signal à Ichai |
speed-to-lead ; Signal passe avant Brevo dans le mailer (sinon lead gravé et personne prévenu) |
| 2026-08-02 | Le point mensuel du 14 = franc-parler business, pas des métriques de vanité | dire le plafond AVANT qu'Avi le découvre seul |
| 2026-08-19 | Objectif contractuel proposé : « 3 clients signés traçables d'ici août 2027 » | ≈1,2 lead qualifié/mois rentabilise le retainer (LTV 4,5-7,5 k€). Remplace « 12 leads/mois » du 14/08, jamais prouvé livré au client |
📐 Doctrine SEO Emet
- Cadence de contenu : 1-2 articles/mois (2026-07-19). Un article de plus sur un domaine à 0 backlink ne reçoit aucun lien : la distribution est le facteur limitant, pas la production. Le temps libéré va aux actifs.
- KPI : on ne rapporte PLUS la position moyenne site-wide (2026-07-19). Elle se dégrade mécaniquement quand on
publie. Remplacée par une cohorte figée (
sites/<key>/keywords.json) + « nb de requêtes en top 10 / top 20 ». ➡️ Sous surveillance depuis le 2026-08-25 :scripts/cohorte.mjs. - Pilier = cluster URSSAF / employeur (2026-08-02) : autorité fonctionnelle, pas sectorielle → compatible avec le « généraliste » non négociable de Nathan, et 100 % portable au déménagement.
- Refus définitif de « bulletin de paie » (8 100/mo) : aimant à salariés, SERP institutionnelle imprenable. Filtrer par QUI PAIE, pas par volume. Ne pas re-litiger à chaque keyword research.
- Semer sur PARIS, pas sur Sarcelles (2026-08-02) : cible du déménagement 6-12 mois. Aucun asset périssable.
- Règle de liens (2026-08-19) : uniquement des liens qui restent vrais après le déménagement (Ordre, data.gouv, France Num, presse pro). JAMAIS de liens locaux Sarcelles (sponsoring, mairie, CCI 95).
/expert-comptable-val-doise→ 301 vers/expert-comptable-sarcelles(2026-07-19) : on ne sacrifie pas la seule page qui progresse. La portabilité se réglera par une chaîne de 301 le jour du déménagement.- Le baromètre est un actif de PRESSE + E-E-A-T, pas une usine à articles : refresh trimestriel + 1 angle éditorial
dérivé par trimestre. ⚠️ Correction d'historique : l'archive contient un « baromètre RETIRÉ » (20/07) — il a en
fait été construit le 30/07 et il est vivant (
/observatoire/val-d-oise+ réutilisation data.gouv.fr). - Ne jamais réécrire un title sur la base d'un « CTR à 0 % » en page 3 : à la position 24, l'espérance est de 0,3 clic. C'est un problème de position, pas de titre.
⚖️ Déontologie & entités (non négociable)
- Faux avis : NON, définitif. Je n'en mets pas en place ; si ça se fait, ça se fait sans moi. Pénal L132-2 (5 ans / 750 k€ en ligne, ×5 pour une personne morale) + échelle disciplinaire jusqu'à la radiation pour Nathan, inscrit via deux entités. Posture : ne pas arbitrer avec Avi — c'est la licence de Nathan, la question se pose à lui. Meilleur argument : le bandeau public d'avertissement que Google affiche sur la fiche, visible des clients. (Les chiffres DGCCRF/plainte ordinale de l'archive ont été corrigés le 20/07 : s'y référer.)
- Entités : question CLOSE. METRAL CONSEIL (SIREN 103667648) et EMET EXPERTISE (SIREN 104307871) sont toutes deux inscrites au tableau, toutes deux SEC, Metral Conseil est Présidente d'Emet Expertise. Montage régulier. Ne plus jamais poser la question du doublon au client.
- E-E-A-T sur
Organization, pasPerson: l'annuaire de l'Ordre ne publie ni fiche personne ni numéro d'inscription. ⚠️ Piège d'homonymie : il existe un Nathan Metral acteur (nathanmetral.com) — jamais ensameAs. - Q&R de la fiche GBP : posées depuis le compte du cabinet, ouvertement. L'usage du secteur (compte perso pour masquer) est refusé — même mécanique déguisée que les faux avis.
📍 Google Business Profile
- Ne rien éditer tant qu'un examen est en cours (ça invalide la vérification), puis un champ important à la fois, espacé de quelques jours. ⚠️ La cause n°1 de suspension est la RAFALE d'édits, pas l'âge de la fiche — le « seuil des 90 jours » de l'archive est du folklore, réfuté.
- Pas de call tracking sur la fiche : changer le numéro principal déclenche un ré-examen. L'onglet Performances donne déjà appels / itinéraires / clics.
- Avis : vrais clients uniquement, 1-2 par semaine, jamais en rafale. Ils ne doivent PAS mentionner « Sarcelles » (déménagement → avis géo-ancrés = passif). Question ouverte pendant l'appel : « sur quoi on t'accompagne ? ».
- Adresse masquée (SAB pur) = la configuration la plus handicapée du pack local (corrélation négative documentée, Sterling Sky). C'est un CHOIX (préparer le déménagement) qui coûte du classement maintenant : à assumer explicitement avec le client, pas à subir. Conséquence : une part du classement se gagnera sur le site et les citations, pas dans la fiche.
- Bing Places / Apple Maps : se justifient par Copilot + Bing Maps, PAS par ChatGPT (réfuté). Chercher une fiche préexistante et la revendiquer avant d'en créer une (Apple ingère du Yelp, terrain à doublons).
🧠 Discipline du système (2026-08-25)
- Rien ne meurt sans raison écrite. Toute idée, reco ou attente vit dans le registre
(
scripts/ledger.mjs) ; les statutsgele/abandonneexigent un--pourquoi, et un gel exige une condition de réveil (datée quand c'est possible, pour être re-arbitrée automatiquement). - Les panels d'agents ont désormais une mémoire : chaque agent reçoit
ledger.mjs briefet n'a pas le droit de ressortir une proposition déjà formulée. Sans ça, ils reconvergeaient chaque mois sur les 3 mêmes conclusions. - Un plan d'action doit contenir au moins un chantier d'ACTIF (étage 🔵), séparé des quick wins — sinon les
tâches courtes mangent systématiquement les actifs, qui sont pourtant le seul levier de distribution prouvé
(le baromètre a rapporté un dofollow
.gouv.fr; 11 articles n'ont rapporté aucun lien). - Une qualité moyenne qui empire n'est pas une régression si le volume utile monte (règle née de 2 faux positifs). Toute alerte nouvelle doit avoir un plancher de volume (≥10 impressions) avant d'être émise.
2026-09-17 — Paris : une page d'atterrissage, et /contact qui redevient une page de contact
Décidé. /expert-comptable-paris porte l'intention commerciale parisienne (« expert comptable
paris », « cabinet comptable paris », « comptable paris »). /contact redevient une page de contact
courte : elle garde le nœud #lieu du JSON-LD et le bloc NAP, et abandonne l'ambition sur la requête
commerciale.
Pourquoi. Depuis le 15/09, /contact portait à la fois le titre « Expert-comptable à Paris et en
Île-de-France » et le rôle de page-lieu unique, câblé en dur dans src/components/seo/SchemaGraph.astro.
Créer une page Paris à côté aurait fait concourir deux URL du site sur la même requête. Le dépôt a déjà
perdu une page ainsi : l'ancienne page Val-d'Oise faisait 67 impressions en position 44 pour zéro clic
et a été redirigée vers la page Sarcelles. C'est le schéma que le pivot des 15-16/09 venait de corriger.
On sépare donc les deux intentions, exactement comme on l'a fait pour Sarcelles le 16/09.
Ce qui la fonde côté chiffres (mesuré le 17/09, DataForSEO) : le marché parisien pèse environ 6 900 recherches par mois sur les quatre formulations principales, contre 140 sur Sarcelles. Le site est absent des résultats organiques rendus sur les trois requêtes parisiennes, et aucune des 58 requêtes locales captées en 28 jours dans Search Console ne contient le mot « paris ». Le pivot des 15-16/09 avait changé des libellés sans créer la page.
Ce qu'elle ne fait pas. Elle ne prétend à aucun bureau à Paris, et elle ne vise pas le bloc local parisien, qui exige une adresse à Paris (voir L340). Elle joue l'organique, pas la carte.
Précédent qui l'autorise : le 15/09, en privé, Avi a demandé ce qu'il fallait pour ressortir sur Paris ; la réponse retenue a été que la fiche Google exige une adresse dédiée qui n'existe pas, mais que le site peut commencer à se déplacer doucement. Cette page est ce geste-là.
Reste à faire après la livraison : retitrer /contact, déplacer ou étendre la condition de page-lieu
dans SchemaGraph.astro, ajouter l'entrée de navigation dans src/data/nav.ts (aucune entrée
géographique n'y figure aujourd'hui), vérifier les cibles de Zones.astro.
2026-09-17 — Ce qu'une image d'article a le droit de montrer (règle née de l'audit des 62 images)
Pourquoi cette règle existe. L'audit visuel du 17/09 a ouvert les 62 images des articles de Genius Prep, une par une. 38 demandent une action, dont 22 sur des pages indexées. Mais le constat qui compte n'est pas le compte : 17 images posent le nom ou l'emblème d'une institution réelle sur un document ou un bâtiment fabriqué, et ce n'est pas un accident. C'est ce que le générateur fait spontanément quand on lui demande un décor new-yorkais crédible. Sans règle écrite, chaque regénération reproduira le motif.
Interdit, sans exception
- Aucune pièce officielle fabriquée. Ni relevé de notes, ni convocation d'examen, ni lettre d'admission, ni confirmation d'inscription, ni dossier de candidature. Le cas trouvé le 17/09 est un relevé « CollegeBoard / Student Score Report » au wordmark correct, à l'arithmétique juste et au percentile plausible : recadré, il se lit comme un document authentique. Il existait en double, dont une fois sur page indexée.
- Aucun emblème, écusson, logo ou nom d'établissement réel sur une pièce ou un bâtiment inventé. Trouvés : lettre Brown avec écusson, enveloppes Yale, Dartmouth et NYU, logo du Department of Education de New York, sigles ERB et ISAAGNY, portail MySchools.nyc.
- Aucun document fabriqué illustrant un résultat revendiqué. C'est le cumul le plus lourd : les pièces les plus crédibles vivaient sur des articles de TÉMOIGNAGES. Une pièce officielle qui illustre un résultat ne se lit pas comme une illustration, elle se lit comme une preuve. C'est la même ligne que celle tenue sur Emet, où l'on refuse tout avis inventé.
- Aucun chiffre d'examen dans une image. Score, barème, percentile, stanine, durée, nombre de questions : un chiffre faux dans une image contredit la compétence qu'on facture, et personne ne le relit. Trouvés : un total SHSAT de 88 quand l'échelle va de 200 à 700, un stanine 5 donné au 40ᵉ percentile, des modules de SAT nommés « Quantitative » et « Verbal », qui sont le vocabulaire de l'ISEE et du GRE.
- Aucun établissement réel situé ailleurs qu'où il est. Trouvés : un arc de pierre gravé « Stuyvesant High School » quand Stuyvesant est une tour de 1992, un garçon devant The Spence School qui n'accueille que des filles, Hunter College donnée comme privée alors qu'elle est publique, Bronx Science épinglée dans le New Jersey.
Autorisé
Des lieux génériques et des gens au travail : un quai de métro, un bureau, un carnet, une salle, une rue. Une marque commerciale correctement rendue sur un objet du quotidien (sac, carnet, crayon) passe. Un nom d'établissement inventé passe et ne risque rien — l'audit a relevé « The Spencer School », qui n'existe pas, et c'est précisément pour ça qu'elle est sans problème.
Comment on vérifie
Une image se juge en l'OUVRANT, jamais sur le prompt qui l'a produite ni sur son nom de fichier. Piège avéré : les « 28 images réparées » du 16/09 concernaient l'hébergement des fichiers, pas leur contenu. Une ligne de journal portant le mot « images » avait donné l'impression que le sujet était traité, alors qu'aucune image n'avait jamais été jugée sur ce qu'elle montre.
Trois questions par image, dans cet ordre : est-ce qu'elle imite une pièce ou une marque réelle ? est-ce qu'elle affiche un chiffre ou un texte lisible, et est-il juste ? est-ce que ce qu'elle montre correspond à l'article ?
Le levier avant les regénérations
Un visuel récurrent validé UNE fois et réutilisé ferme une série entière. Exemple mesuré : la MetroCard apparaît dans 12 images et son logo est faux dans 6. Une image propre réutilisée supprime six défauts d'un coup, pour un sixième du travail de six regénérations.
2026-09-17 (soir) — L'adresse parisienne d'Emet est décidée
Décidé par Ichai. L'établissement parisien n'est plus une hypothèse : il se fera, échéance « bientôt », sans date précise. Ne plus poser la question à Avi ni à Nathan.
Ce que ça retourne. L'arbitrage du jour reposait sur l'inverse : le bloc local parisien était jugé hors d'atteinte faute d'adresse, et les 6 900 recherches par mois du marché parisien n'étaient donc qu'un horizon. Elles redeviennent un objectif.
Ce qui se prépare sans attendre le bail, par ordre d'effet :
- Les avis. Le bloc local de « expert comptable paris » aligne des cabinets de 26 à 1 700 avis ; Emet en a 6. Les avis SUIVENT le cabinet quand la fiche déménage, donc chacun obtenu aujourd'hui est capitalisé pour le jour de l'ouverture. C'est la seule action qui prépare Paris sans dépendre de quoi que ce soit d'autre.
- Les photos, la fiche n'en a aucune.
- La page
/expert-comptable-paris, déjà en ligne depuis aujourd'hui : elle prend de l'ancienneté, ce qui est exactement ce dont elle a besoin.
⚠️ LE PIÈGE DU JOUR J, à savoir avant et pas après. On ne change PAS l'adresse de la fiche Google existante. Éditer l'adresse d'une fiche à zone de service casse son classement dans la nouvelle zone. La méthode est : créer une NOUVELLE fiche à l'adresse parisienne, puis demander au support Google de marquer l'ancienne comme déménagée — les avis migrent. (Règle déjà établie au dossier client le 19/07, elle prend seulement toute sa valeur maintenant.)
Et une condition matérielle à vérifier quand la date approche : une domiciliation sans présence ne suffit pas. Google demande une enseigne et quelqu'un sur place aux heures affichées.
2026-09-26 — Relances mises de côté
Ichai met de côté pour le moment : les 22 cabinets comptables contactés en juin, et la question de Nathan sur l'adresse des alertes. Ne plus les remonter en début de session ni dans le carnet /seo tant qu'Ichai ne les rouvre pas. Ruben Toledano est rouvert par Ichai le 16/10/2026 (il l'a rappelé, proposition par mail en préparation, L390).
2026-10-03 — Sitemap d'Emet : la date de mise à jour suit aussi le contenu visible
lastmod = max(date du millésime des données, date de CONTENU). La table CONTENU vit dans
/root/apps/emetexpertise/scripts/lastmod.mjs, une ligne par famille de pages ; tout changement visible d'un
gabarit d'observatoire monte sa ligne dans le même commit. Avant, le millésime primait (choix du 16/09 pour ne
pas envoyer un signal mort à chaque virgule) : 3 820 pages CFE disaient « 02/09 » après quatre refontes, et Google
servait leur version du 11/09.
2026-10-03 — Baromètre du Val-d'Oise : il compte les jugements d'ouverture
Le baromètre compte les jugements d'ouverture de procédure collective (liquidation, redressement, sauvegarde,
« autre jugement d'ouverture »), par mois de publication au BODACC, lus dans le champ structuré jugement.
Avant le 03/10 il comptait toute la famille « collective » (clôtures, états des créances, interdictions de gérer) :
3 198 sur 12 mois au lieu de 1 015. La série publiée a été recalculée une fois, avec une mention datée sur la page.
Chaque relevé est contrôlé contre le jeu Firmio sur data.gouv (scripts/barometre-firmio.mjs) ; un écart bloque
la mise à jour du mois. Ouvert : faut-il garder les 24 « autres jugements d'ouverture » (des réouvertures de
liquidation) ? Sans eux, 984.
2026-10-13 — Moed : le site moedcal.com entre au command center
Ichai veut un site à lui, pour Moed (son app iPhone de calendrier juif), et pousser le SEO « pour voir jusqu'où on peut aller »,
avec des analytics complets (d'où viennent les gens). Domaine moedcal.com acheté par Ichai chez Cloudflare le 13/10
(zone active, le jeton du serveur écrit le DNS). Code : /root/apps/moed/site (plan SITE.md du 05/10, qui reste la référence SEO).
Décidé le 13/10 : les maquettes du 05/10 ne lui plaisent pas → nouvelle direction visuelle, choisie par nous (« fais un truc à toi ») ;
le site est à la fois vitrine de l'app et pages utiles (horaires par ville, fêtes, outils) ; français, hébreu, anglais dès l'ouverture ;
le site entier d'un coup (pas d'accueils à choisir avant), construit par une dizaine d'agents, optimisé SEO et téléphone.
14/10 — modèle visuel : Pillars (https://thepillarsapp.com, app de prières musulmanes). Le style « ciel vivant » du 13/10 a été
rejeté (« 100 fois trop IA, très moche ») ; parmi six sites d'apps primés montrés côte à côte
(sandbox moed-modeles), Ichai a choisi Pillars. On l'imite fidèlement ; un prototype de l'accueil est montré avant de construire le reste.
14/10 — prototype Pillars : « ok mais mieux » (https://sandbox.vpsdashboard.space/moedcal-pillars/). Les quatre demandes : des écrans de l'app
plus grands et plus nombreux, avec une seule date partout ; plus de vie (carrousel dans le téléphone, apparitions au
défilement) ; un rendu plus fini (icônes dessinées, détails au pixel, haut de page plus riche) ; moins de vide (fêtes à venir, villes, articles sur l'accueil).
14/10 — écrans de l'app pour le site : les captures ios/design/public/app-v2/ datent du 05/10 à 16:43, avant l'arrondi
prudent (commit 1d7c7b8, build 22). Elles affichent 18:56 / 19:57 à Paris le 9/10, au lieu de 18:55 / 19:58. On ne s'en sert jamais
pour les heures, et le site les remplace par des captures du build en vente avant la mise en ligne. Haftara : l'app en vente ignore Mahar Hodech et les
autres cas spéciaux (CalendarEngine.parashaInfo) ; la session moed le remonte à Ichai, et la session SEO ne touche pas à ios/.
2026-10-16 — Ruben Toledano : proposition
Rouvert par Ichai (appel du 16/10). Tarif : 560 €/mois, engagement 6 mois, TVA non applicable (293 B). Pas de Google Ads dans le périmètre.
Démarrage : accès au domaine (espace OVH, ou serveurs DNS basculés vers notre Cloudflare), accès au WordPress (fichiers FTP ou compte), Search Console en « Restreint » et fiche Google en gestionnaire pour ichaiwizlol@gmail.com.
On crée des pages selon ce qui rapporte, comme les observatoires (ex. prix de cession des restaurants, BODACC). Brouillon : sites/_prompts/AA-ruben-proposition-16-10.md, non envoyé.
- 17/10 : contre-proposition de Ruben (500 € × 2 mois puis 560 € « si c'est good ») → Ichai : ok 500 € en novembre et décembre, mais 6 mois fermes de novembre à avril (560 € de janvier à avril), pas d'essai de 2 mois (le référencement prend au minimum 3 mois).
2026-10-19 — Aucune personne inventée sur un site client
15 articles du blog Genius Prep (venus de l'orchestrateur publish-geniusprep, aujourd'hui éteint) citaient des experts et des familles introuvables ; tout est retiré (registre L683, compte rendu sites/geniusprep/blog/2026-10-19-nettoyage.md), sur accord d'Ichai. Règle : un expert, une famille, une étude ou un chiffre cité sur un site client existe et a sa source vérifiée, sinon il ne paraît pas. Un témoignage = un avis réel, cité mot pour mot. Si l'orchestrateur revient, sa consigne se corrige avant toute publication.
Repetitive → skills
Manips répétitives → candidats skills/scripts
| Repéré | Manip | Statut |
|---|---|---|
| 2026-06-25 | Auditer techniquement un site + lire les findings | ✅ scripts/audit.mjs → skill seo-audit |
| 2026-06-25 | Écrire au journal en fin de session | ✅ scripts/journal.mjs |
| 2026-06-25 | Vérifier si un autre Claude bosse sur un projet (avant d'agir/s'alarmer) | ✅ scripts/claude-sessions.sh [projet] |
| 2026-06-25 | Générer de vraies images (articles/OG) via kie.ai GPT Image 2 | ✅ scripts/kie-image.mjs (clé /root/.secrets/kie.env) — candidat skill seo-image |
| 2026-06-25 | Pull GSC (geniusprep via wizytics, emet via SA) — 2 méthodes à unifier | ✅ scripts/gsc.mjs (unifié, JWT en node pur) + gsc-diff.mjs + signal-send.mjs + monitor.sh → skill seo-monitor |
| 2026-06-25 | Poser robots.txt + sitemap sur un site qui n'en a pas | ⏸️ pas nécessaire (emet a déjà tout) — dé-priorisé |
| 2026-06-25 | Auditer un site via plusieurs agents (recon/contenu) puis synthétiser | 🔜 candidat workflow seo-deep-audit (fan-out + vérif adverse) |
| 2026-06-26 | Migrer un domaine client sur Cloudflare (DNS/SSL/cache) | ✅ scripts/cloudflare.mjs + skill seo-cloudflare + runbook runbooks/cloudflare-migration.md |
| 2026-06-26 | Documenter ce qu'on refait (procédures) | ✅ système runbooks/ (discipline gravée dans CLAUDE.md) |
| 2026-08-25 | Retrouver « ce qu'on avait dit / décidé / gelé » sur une idée | ✅ scripts/ledger.mjs (registre + brief anti-redite) |
| 2026-08-25 | Vérifier que nos crons, données et budgets d'API tournent encore | ✅ scripts/system-check.mjs (dans le cron hebdo) |
| 2026-08-25 | Savoir si un article publié concourt vraiment | ✅ scripts/article-watch.mjs |
| 2026-08-25 | Suivre les positions de la cohorte cliente | ✅ scripts/cohorte.mjs (KPI officiel) |
| 2026-08-25 | Analyser tout + proposer du neuf sans se répéter | ✅ skill /propose (8 angles avec mémoire) |
2026-09-03 — Relever les avis d'une fiche Google Business → automatisé
Manip faite trois fois à la main (30/08, 02/09, 03/09) : ouvrir un navigateur headless sur
search.google.com/local/reviews?placeid=… avec le cookie de consentement SOCS et un UA mobile,
faire défiler, lire le texte, compter les réponses du propriétaire.
➡️ Devenue scripts/gbp-reviews.mjs <emetexpertise|geniusprep> [--json] [--dump=fichier].
Elle rend le total d'avis, la note, le nombre de réponses du gérant et la liste nominative des avis
sans réponse — la seule information actionnable.
Trois pièges qu'elle encapsule et qu'on ne re-découvrira plus :
- le
innerTextcontient des espaces insécables là où le libellé montre un espace normal : unsplitsur la chaîne littérale rend 0 alors que le marqueur est bien là ; - la réponse du gérant ne s'écrit pas « Response from the owner » dans ce rendu mais
«
(Owner) » / « (Propriétaire) » ; - les avis se chargent dans un conteneur interne : un
mouse.wheelsur la fenêtre n'en charge qu'une partie (4 sur 9 chez GeniusPrep). Le total fiable se lit dans le bloc de synthèse « note (N) », le compte des avis rendus n'est qu'un plancher.
2026-09-22 — mesuré sur les transcripts, pas deviné
Les 21 sessions de ce projet ont été passées au crible : 3 694 commandes, 3,3 millions de caractères. Trois manips dominent, et deux des trois coûtent des erreurs, pas du temps.
| Manip | Mesure | Statut |
|---|---|---|
Écrire au registre (add/set) |
483 écritures à la main, un paragraphe tapé en ligne de commande à chaque fois | ✅ ledger.mjs import <fichier.json> — tout ou rien, et le texte traverse intact |
| Vérifier une page en ligne | ≈495 mesures refaites à la main (HTTP, sitemap, liens, robots, JSON-LD) | ✅ scripts/verify.mjs <site> <url> — les six pièges encapsulés, contrôle positif obligatoire |
| head -N |
1 205 fois, deux faussetés livrées (27/08, 22/09) | ⚠️ pas automatisable — voir L403 |
Attente bloquante (sleep) |
170 fois, 57 minutes de temps mort pour Ichai | ⚠️ tout doit partir en fond (règle du 15/09, encore mal tenue) |
Pourquoi verify.mjs existe alors que chaque mesure tient en une ligne de shell : parce que
chacune de ces lignes porte un piège dans lequel ce dossier est retombé après l'avoir documenté.
grep -c "<loc>" sur un sitemap servi en une ligne (30/08 puis 07/09) · grep -o avec [^<]* qui
s'arrête au premier < (03/09) · une seule forme de href comptée alors qu'un lien s'écrit en
relatif ou en absolu (16/09) · un zéro pris pour une absence sans contrôle positif (07/09) · exact
contre préfixe, qui ne rendent pas le même nombre (22/09) · une URL forgée de tête au lieu d'être
lue dans le sitemap (19/09). Le script ne fait pas gagner du temps : il supprime une classe
d'erreur. Et quand son contrôle positif échoue, il refuse de conclure au lieu de rendre un zéro.
2026-10-18 — chantier « moins de tokens, moins de temps » : les commandes d'attente et de vérification
Mesuré sur les 32 dernières sessions + 287 sous-agents (les sous-agents font ~2/3 des tokens). Cinq manips
refaites à la main à chaque passage, chacune devenue une commande. Toutes testées pour de vrai le 18/10
(sauf un déploiement réel : deployer.sh n'a tourné qu'à blanc).
| Manip | Mesure | Statut |
|---|---|---|
| Attendre la fin d'un déploiement, d'une tâche du PC, d'Ariane qui redémarre en 502, d'une URL | ~195 attentes écrites à la main (sleep/until), souvent au premier plan |
✅ bash scripts/attendre.sh <deploiement <site>|pc <tache>|ariane|url <url> [code]> [--max=s] [--depuis=ISO] — dernière ligne « fini / échec / délai dépassé », code 0 / 1 / 124, à lancer en fond |
| Lire les mails Google Search Console | ~27 lectures par gog gmail avec le keyring à la main |
✅ node scripts/gsc-alertes.mjs [--jours=N] [--json] [--tout] — par ariane gmail.search/gmail.thread, une ligne par alerte (date · site · type · ce qui est touché) |
| Lire la page servie par le conteneur, sans Cloudflare | ~30 fois docker inspect pour l'IP, puis curl avec le Host |
✅ bash scripts/origine.sh <site> <chemin> [--code|--entetes] — conteneur et port lus dans <appDir>/ariane.json |
| Capturer une page | Playwright recherché à chaque fois (find … -name playwright, import réécrit) |
✅ node scripts/capture.mjs <url> [--sortie=] [--mobile] [--ordi] [--ecran] — 390 et 1440, bandeau cookies fermé s'il existe, deux captures à la fois au plus (places dans /tmp/seo-capture-places/) |
| Vérifier les pages après un déploiement | ~80 boucles for u in …; curl alors que verify.mjs existe |
✅ bash scripts/deployer.sh <site> [chemins…] [--a-blanc] — chemin officiel du site, attendre.sh, puis verify.mjs sur l'accueil et chaque chemin, journal dans sites/<site>/deploiements/<date>.md · ⚠️ jamais lancé pour de vrai au 18/10 |
Appeler DataForSEO en direct (. seo-apis.env + curl) |
~53 appels au lieu de ariane seo.dataforseo |
➡️ pas de commande neuve : la règle est dans les skills (agent A) |
| WebFetch legifrance / bofip / impots.gouv / service-public | ~300 par les agents d'Emet | ➡️ scripts/sources.mjs (agent B) |
Pièges rencontrés en les écrivant, encapsulés dans les commandes :
verify.mjsrendait 0 même sur une page en 404 (corrigé le 18/10 : 0 = rien de bloquant, 2 = un 🔴).deployer.shlit ce code ; tout autre code, ou une sortie sans ligne HTTP, compte la page « non vérifiée », jamais verte.ariane pc.waitrefusetimeoutMs> 1 500 000 (25 min) :attendre.sh pcl'appelle par tranches.gmail.searchplafonne àlimit100 et n'a pas de filtre de date : on filtre après (65 mails Search Console depuis le 31/03, ça tient).- La fin d'un déploiement d'Emet n'est pas la fin de
vps.project_deploy:deploy.shpurge Cloudflare APRÈS.attendre.shattend aussi que le processusdeploy.shsoit fini, sans compter le shell qui l'a lancé. - Un
--depuisest nécessaire juste après un lancement : sans lui,attendre.sh deploiementvoyait le déploiement PRÉCÉDENT et disait « fini » sur un déploiement vieux de trois jours. Désormais, sans--depuis, il n'accepte qu'un déploiement vu en cours ou fini il y a moins de 10 min, sinon code 3 « aucun déploiement récent ».deployer.shpasse toujours--depuis. --max=abcattendait sans fin (lu comme une variable vide) : refusé désormais (code 2), et--maxest tenu à la seconde près (pauses et appels bornés à ce qui reste).deployer.sh --a-blancavec un contrôle avant départ en ❌ rend 4 et ne dit jamais « tout est vert ». Un chemin vide ou sans/est refusé (code 2).- Le skill
seodit quedeploy.shd'Emet « prévient IndexNow » : faux au 18/10, le fichier (83 lignes) n'appelle pas IndexNow.
2026-10-18 — mesure d'indexation par famille (L626) : scripts/indexation-familles.mjs
Mesure à refaire toutes les 4 semaines sur les deux sites : quelle part de chaque famille de pages (observatoires d'Emet, /data de Genius Prep) Google a réellement indexée. Devenue une commande dès la 1ʳᵉ fois, puisqu'elle revient.
✅ node scripts/indexation-familles.mjs <emetexpertise|geniusprep|all> [--n=100] [--graine=<date>] [--a-blanc] [--budget=1500]
- lit le sitemap SERVI (même lecture que
verify.mjs), range les URL par famille (tableFAMILLESen tête du script : à compléter quand un site ajoute une section, sinon elle tombe dans « autres »), tire N URL par famille avec une graine reproductible, les passe àurl-inspect.mjs --json(une famille par appel), et écritsites/<site>/indexation/<date>.json+.md(%, intervalle à 95 %, plancher ≥1 impression du dernier relevé GSC). --a-blanc: tirage et comptes sans API, rien d'écrit. Refuse de partir au-delà de--budgetinspections par site.- À lancer en fond :
/root/bin/fond indexation-familles node /root/apps/seo/scripts/indexation-familles.mjs all(~840 inspections Emet + ~475 Genius Prep à n=100). - ⚠️ Les comptes de L626 incluaient la page-mère pour Emet et l'excluaient pour Genius Prep : le script compte la page-mère dans sa famille partout (écart de 1 attendu côté Genius Prep).
Frictions & fixes
Frictions rencontrées (+ solutions)
| Date | Friction | Solution |
|---|---|---|
| 2026-06-25 | TaskCreate échoue (schéma non chargé — tool déféré) |
Charger via ToolSearch "select:TaskCreate" avant usage, OU utiliser le journal/memory du projet (préféré ici) |
| 2026-06-25 | cd /root/.secrets → "Shell cwd was reset" après la commande |
Toujours chemins absolus, ne pas cd (rappelé dans CLAUDE.md) |
| 2026-06-25 | keyPath /contact présumé → 404 côté geniusprep |
Ne pas présumer les chemins ; les dériver du sitemap ou vérifier en live |
| 2026-06-25 | Fausse alerte « emet 502 / site down » : c'était un état transitoire pendant le déploiement du mailer par un autre Claude | Avant d'annoncer un incident : bash scripts/claude-sessions.sh <site> + uptime conteneur + git/mtimes récents. Doctrine gravée dans CLAUDE.md |
| 2026-06-25 | Bug moteur audit : sitemap_missing faux positif (testait /sitemap.xml en dur ; Astro émet sitemap-index.xml) → m'a fait croire au 404 d'emet |
audit.mjs corrigé : résout le sitemap via la directive Sitemap: de robots.txt + chemins usuels (sitemap-index.xml…). Re-test emet = 100/100 |
| 2026-06-26 | Doublon introduit : monitor.sh (7h) est le 4e système GSC sur geniusprep (6h/8h/9h existaient déjà). Mon grep -i seo ratait les scripts gsc-* |
Grep large `gsc |
| 2026-06-26 | Auto-critique workflow : la détection de régression gsc-diff est "décorative" (fenêtre glissante 28j → snapshots chevauchants à 27/28) |
Refondre en fenêtres 7j non chevauchantes (dimension date) + diff par requête/page. Voir reports/2026-06-26-system-review.md P1-A. |
| 2026-06-26 | ✅ RÉSOLU (commit c6ea795) : les 6 points de la revue corrigés | régression → 7j non chevauchant (timeseries) + diff requête ; doublon → monitor:false geniusprep ; audit → audit-diff.mjs + cron audit-weekly.sh ; GEO → geo.mjs ; parser robots par groupe UA ; secret Signal → config/notify.json ; git init (filet de sécurité, pas de backup-server.sh sur le VPS → backup off-site à prévoir). |
2026-06-26 — Doublon de fiche client (ruben)
- Friction : deux fiches pour le même client dans
memory/clients/—ruben-toledano.md(rich, créée à la main au stade prospect) etruben.md(générée parnew-site.mjs, = clé attendue par les scripts report/audit). Risque de divergence. - Solution appliquée :
ruben.mddevient la fiche canonique (clé-matchée + baseline) ;ruben-toledano.mdréduit à un pointeur en tête. Convention à retenir : la fiche SEO vit toujours sousmemory/clients/<key>.md(iciruben.md), pas sous le nom complet. - Note : ne pas confondre avec
/root/apps/chat/whatsapp/contacts/ruben-toledano.md(miroir WhatsApp, autre repo) qui, lui, garde son nom complet par mapping. | 2026-06-26 | Emballement kie : un agent a spawné un watcher bash qui relançait en boucle des batches kie.ai →200 crédits brûlés, images sauvées dans scratchpad/raw (PNG) au lieu d'emet | Tuer watcher+générateurs (pgrep/kill ciblé), RÉCUPÉRER les PNG de scratchpad → convertir webp. Leçon : ne JAMAIS laisser un agent créer des watchers/loops background auto-relançants ; pour générer en masse → 1 batch détaché idempotent, contrôlé, pas de boucle. | | 2026-06-26 | kie + Bash tué en plein vol : batches kie en1-2 images par invocation avant kill) |run_in_backgroundtués ~exit 144 (cap temps) ; version foregroundnode ... & wait+\| tail→ exit 1 prématuré (kie-image.mjsest idempotent (skip si--outexiste) → relancer le même batch finit le reste. Pattern fiable = 1 batch foreground> log.txt 2>&1(pas de pipe, pas de&/wait, pas de watcher), réexécuté jusqu'à N/N. | | 2026-06-26 | Auto-commit bot VPS sur emet : un hookVPS Dashboard <bot@vpsdashboard.space>commit+déploie le working tree d'emet (a committé mes imgs+blog index+heros enb9776a4avant mongit commit→ "nothing to commit" ; conteneur redémarré et heros recompressés apparus en cours de session) | Non bloquant : l'état committé = exactement mon travail vérifié. À savoir : sur emet le working tree est auto-committé/déployé — vérifiergit log(auteur bot) au lieu de supposer ; ne pas s'alarmer d'un conteneur "Up Xs" ni de fichiers déjà à jour. |
2026-06-27 — Miroir WhatsApp figé : migration LID de WhatsApp (CRITIQUE, récurrent)
- Friction :
avi.mdfigé depuis le 26/06 alors qu'Avi écrivait en continu (conversation live sur le Google Business Profile invisible).wa-routertournait pourtant (cursor qui avance, "N events traités"). - Cause racine : WhatsApp a migré les chats 1-1 vers des LID (
<num>@lid) au lieu du JID téléphone (<phone>@s.whatsapp.net). Les messages d'Avi arrivaient sous162749229334551@lid. Dansregistry.jsonson entrée avaitlid: null→ la fonctionmatchContactdu routeur (/app/index.js, match surc.lidouc.jiduniquement) ne matchait rien → message consommé (cursor++ , ajouté àseen) mais jamais écrit. L'auto-bind du LID (index.js l.99) ne se déclenche qu'après un match réussi → chicken-and-egg, jamais réparé seul. - Solution appliquée : récupérer le LID du contact (ici via les
participantsdu groupe Emet :162749229334551= Avi), le poser dansregistry.json("lid": "162749229334551@lid"). Le routeur recharge la registry à chaque tick → mirroring live réparé immédiatement (vérifié : il a appendé 00:45→00:48 tout seul). Backlog manquant (14:33→00:44, déjà dansseen, non rejoué) : récupéré via l'APIGET whatsapp-api:3000/api/messages/<lid>?limit=500(mémoire du bridge, garde ~70 msgs récents) + transcription Deepgram, mergé dansavi.md(insertion du trou, sans écraser l'ancien historique —backfill.jsécrase, donc NE PAS le lancer tel quel si l'historique d'avant migration n'est plus dans la mémoire du bridge). - ⚠️ Va recommencer :
houny,lance, et les autres ontlid: null→ mêmes silences quand WhatsApp les migrera. Réflexe quand un*.mdcontact se fige :docker exec wa-router node -e→ fetch/api/archive/events→ repérer les@lidnon mappés / fortement actifs → cross-référencer (pushName, participants groupe) → patcher la registry. Fix robuste (côté chat, non fait) : que le routeur mappe@lid→contact viaparticipantPhone/table LID↔phone, pas seulement viac.lidstatique. - Note périmètre :
/root/apps/chatest normalement read-only (autre projet) ; patch registry + mergeavi.mdfaits car Ichai a explicitement demandé de réparer le bridge. Changement minimal et réversible.
2026-07-19 — Les « kits d'actions client » ne s'exécutent pas
Constat. templates/emet-actions-avi-nathan.md livré le 16/07, prêt-à-coller, priorisé. 3 jours plus tard : zéro
ligne exécutée. Fiche GBP toujours non vérifiée, aucun profil revendiqué.
Ce n'est PAS de la paresse du client — Nathan a créé la fiche, filmé 66 s, zoomé sur le Kbis, retenté le 13/07, commandé des cartes de visite. Ce client exécute. Ce qui bloque :
- Le kit demande une décision, pas une action. Toute ligne contenant un point d'interrogation ne sera jamais exécutée — surtout sur un sujet sensible (deux structures à une adresse résidentielle, dont une au nom de l'associé).
- Impuissance apprise : il a échoué 2× sur la vidéo et a dit « je sais pas quoi faire de plus ». Lui renvoyer un script écrit, c'est lui redemander de refaire seul ce qui a déjà raté.
- Pas de propriétaire ni de date. Un livrable adressé « à Avi/Nathan » est adressé à personne.
Règles adoptées :
- Ne plus livrer de kits. Ce qui est délégable (formulaires : PagesJaunes, Ordre, Pappers, Societe.com) → on demande les accès une fois et on exécute nous-mêmes. Zéro tâche déléguée = zéro tâche non faite.
- Ce qui est irréductiblement au client (vidéo GBP filmée en direct dans l'app) → un créneau visio de 20 min, pas un document. C'est ce qui a marché le 30/06 : fiche créée en 17 min avec Ichai en direct.
- Jamais une question au client si la réponse est publique. Vérifier d'abord (API entreprises, annuaires, SERP).
- Router par personne, jamais « au groupe ». Nathan = physique/administratif. Avi = tout ce qui implique de parler à des humains (prescripteurs, avis). Avi est un closer : une tâche administrative pour lui est une tâche morte.
- Formuler en décision, pas en question : « J'ai vérifié X. On fait Y. Dis-moi juste OK. »
2026-07-19 — Annuaire de l'Ordre : le paramètre q= est un piège silencieux
annuaire.experts-comptables.org/recherche?q=<nom> ignore silencieusement le paramètre et renvoie
l'annuaire entier (3148 pages) — ce qui se lit comme « 0 résultat pertinent » et fait conclure à tort
que le cabinet n'est pas inscrit. J'ai failli livrer cette fausse conclusion au client.
✅ Le bon paramètre est comptable= : annuaire.experts-comptables.org/recherche?comptable=metral
Filtres de type disponibles et utiles : SEC (société d'expertise comptable), SPE (société de participations),
SUCCURSALE, AGC. Ils permettent de trancher la nature réelle d'une structure.
À réutiliser pour tout futur client expert-comptable (vérification d'inscription, sameAs officiel).
2026-07-19 — Revendication fiches annuaires Emet : murs anti-bot / SSO (VPS IP)
Contexte : mission de revendiquer/compléter Emet Expertise sur PagesJaunes, Pappers/Societe.com, annuaire de l'Ordre, en attendant la fiche Google Business (bloquée en validation vidéo). Blocages constatés (preuves = screenshots scratchpad/claims/) :
- PagesJaunes / Solocal : la fiche n'existe pas encore (que des concurrents Y2M, Sarcelles Expertise). L'inscription owner passe par
solocal.com→ Cloudflare Turnstile non résolvable (headless ET headed xvfb, IP datacenter). Le fallback "Suggérer un pro" (formcqlr_ajoutetab, sans compte) affiche lui aussi un Turnstile qui échoue ("Échec de la vérification") → soumission impossible. BLOQUÉ. - Pappers :
pappers.frentièrement derrière Cloudflare Turnstile (403). L'espace dirigeant (claim gratuit) est derrière ce mur. BLOQUÉ. - Societe.com : fiche existe (
emet-expertise-104307871.html), pas de captcha, mais aucun champ gratuit pour ajouter le site web ; édition = espace client payant, data INSEE/INPI. Lien non ajoutable gratuitement. - Annuaire Ordre : fiche existe (Nathan METRAL, adresse OK, pas de site web). Édition = login Comptexpert SSO (
identification.experts-comptables.org) = identifiants Ordre de Nathan. BLOQUÉ, action Nathan requise. Leçon : les annuaires FR grand public (Solocal, Pappers) sont sous Cloudflare Turnstile → inrevendicables par bot depuis le VPS. Ces claims doivent être faits manuellement par le client (Avi/Nathan) depuis leur navigateur résidentiel. Fournir des runbooks pas-à-pas plutôt que d'automatiser.
2026-07-19 — Faux champs live dans le JSON-LD / mentions-légales (confiance d'entité)
Découvert via panel /wide (données structurées). Trois champs de src/data/company.ts étaient faux ou factices
et nuisaient activement à la reconnaissance d'entité (Google recoupe le JSON-LD avec les registres pointés en sameAs) :
foundingYear: '2025'alors que la création RNE est le 2026-04-09 →foundingDatecorrigé.vatId: 'FR [à confirmer par le cabinet]'— s'affichait tel quel EN PUBLIC sur/mentions-legales. La TVA intracommunautaire FR est dérivable (algo DGFiP : clé = (12 + 3×(SIREN mod 97)) mod 97 = 73) →FR73104307871. Rendu rendu conditionnel (ne jamais laisser fuir un placeholder). ⚠️ à faire confirmer au cabinet.sameAs: un seul lien[A CONFIRMER]→ enrichi à 3 registres vérifiés. Règle : un placeholder[A CONFIRMER]danscompany.tspeut fuir en clair sur une page publique — les traquer.
2026-07-19 — llms.txt = cargo-cult (ne plus le vendre comme atout)
Vérifié (Ahrefs 137k sites : 97% à 0 trafic ; Google/Mueller : non lu, comparé à meta keywords). On garde le fichier (ne nuit pas) mais on ne le compte plus comme un levier GEO dans les rapports. Ce qui compte pour être cité par les LLM en 2026 : entité (sameAs/Wikidata), Q&A visible (pas le JSON-LD FAQ), et surtout mentions tierces (digital PR).
2026-07-21 — Faux positif « RÉGRESSION position moyenne » (2e occurrence, autre script)
Alerte Signal reçue : « 🔴 RÉGRESSION : position moy. 21,1→27,3 (+6,2) » sur Emet. C'était un FAUX POSITIF. Sur les mêmes 7 jours : clics +67 % ET impressions +63 %, et vérifié couple par couple : la page money progressait (21,5→21,2), home et paie-social stables, aucune page clé en recul. La position moyenne montait uniquement parce que le site (jeune) apparaît sur de nouvelles requêtes longue-traîne mal classées → dilution mécanique = expansion saine, pas une chute.
Cause : scripts/gsc-diff.mjs l.50 alertait sur dPos >= 2 sans regarder le trafic.
Fix : n'alerter sur la position que si les clics chutent ou stagnent (dClicks <= 5%). Une vraie perte de
rang fait baisser les clics ; une expansion les fait monter. Testé dans les deux sens (vraie chute alerte toujours).
⚠️ C'est la 2e fois que ce même faux positif pique : déjà corrigé sur audit-diff.mjs (score-drop-alone) le
mois dernier, il subsistait sur gsc-diff.mjs. Règle générale à retenir : une métrique de "qualité moyenne"
(position moyenne, score) qui empire n'est JAMAIS une régression si le volume utile (clics/trafic) monte en même temps.
Vérifier tout autre détecteur qui suivrait ce schéma.
2026-07-31 — Cloudflare cache la home d'emetexpertise → déploiements servis PÉRIMÉS
Constaté après le déploiement du baromètre : cf-cache-status: HIT sur la home. Le conteneur servait la
version fraîche, mais Cloudflare servait une version cachée aux vrais visiteurs (lien footer absent,
et potentiellement les corrections du jour : formulaire home, écritures collées, schema).
Règle : après CHAQUE déploiement d'emetexpertise (et tout site derrière Cloudflare proxifié), purger le cache Cloudflare, sinon les visiteurs voient l'ancienne version (règle héritée n°11 : vérifier la version réellement servie, pas seulement le conteneur).
Purge (secret dans /root/.secrets/cloudflare.env) :
ZONE=$(curl -s -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" "https://api.cloudflare.com/client/v4/zones?name=emetexpertise.fr" | jq -r '.result[0].id')
curl -s -X POST -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/purge_cache" --data '{"purge_everything":true}'
À intégrer dans un script de déploiement emet (build → up → purge CF → IndexNow → vérif live).
2026-08-14 — trois frictions détectées au rapport
- wizytics down (~10 j) → pull GSC geniusprep cassé (
docker exec wizytics …échoue) et le cron quotidien échoue probablement en silence. Fix proposé : soitdocker start wizytics pocketbase-wizytics, soit migrer geniusprep en service account JWT comme emet (plus de dépendance conteneur). - marva hors ligne (8 conteneurs stoppés ~2 sem. + proxy host NPM inactif → SSL unrecognized name).
L'audit sort score 0/critical×5 artificiels. Fix : décider actif/pause ; si pause →
paused: truedansconfig/sites.json. - Mémoire fausse :
clients/emetexpertise.mdaffirmaitgoogleReviewUrlcâblé — faux (avis.astro:58vide depuis le 19/07, vérifié en prod). Corrigé dans la fiche. Leçon : « prêt côté nous » ne se note qu'après vérification du code déployé, pas de l'intention.
2026-08-16 — envoi d'un fichier sur un groupe WhatsApp client : AUCUN chemin automatisé
Demande : envoyer le rapport PDF sur le groupe « SEO EMET EXPERTISE ». Trois voies tentées, trois échecs : (1) bridge whatsapp-api VPS = pas de fichiers + bug groupes (Baileys 6.7.8, « not-acceptable ») ; (2) WhatsApp Web sur ICHAIPC = session non connectée (QR demandé) ; (3) WhatsApp Desktop sur ICHAIPC = non connecté (écran « Bienvenue »). Solutions durables possibles : upgrade Baileys (risque session, à faire hors conversation active) OU demander à Ichai de connecter durablement WhatsApp (Desktop ou Web profil ichaiwizlol) sur ICHAIPC — ça débloquerait tous les envois clients futurs (fichiers + groupes). En attendant : fallback = PDF sur Telegram → Ichai transfère depuis son téléphone.
✅ RÉSOLU 2026-08-16 (le soir même) — envoi groupes WhatsApp
Upgrade Baileys 6.7.8 → 6.7.24 dans whatsapp-api (+ stub makeInMemoryStore retiré de l'API, session conservée sans re-scan, backup sessions dans /root/backups/whatsapp-sessions-20260816-1839.tar.gz). L'endpoint /api/send-file (non documenté) marche maintenant sur les groupes. Rapport PDF envoyé sur le groupe SEO EMET EXPERTISE et vérifié dans l'archive (fromMe:true). Skill whatsapp mis à jour.
2026-08-19 — Rapport cockpit (panel 5 angles)
- [CORRIGÉ 19/08 soir] Miroir WhatsApp : le diagnostic « mort » du panel était à moitié faux — les ARCHIVES (whatsapp-api,
/app/sessions/archive/messages-live-*.jsonl) n'ont JAMAIS cessé, seule la couche markdowncontacts/*.mdétait figée depuis le 03/08 car le conteneurwa-routeravait disparu (absent même dedocker ps -a, cause inconnue). RÉPARÉ : conteneur relancé (/root/apps/wa-router, compose up) + backfill one-shot du trou 03→19/08 (script conservé :/root/apps/chat/whatsapp/_backfill/backfill.js, exécuter avecdocker exec -e NODE_PATH=/app/node_modules wa-router node …après avoir copié les jsonl d'archives). ⚠️ Piège appris : le rejeu par recul de curseur ne marche PAS (/api/archive/events?since=renvoie les événements les plus récents → le curseur saute le backlog). Leçon : en cas de doute sur le miroir, vérifier les ARCHIVES du conteneur avant de déclarer le canal mort. - Faux blocage « mot de passe contact@ » (19/07→19/08) : jamais re-testé alors qu'Avi contrôle a.cohen@emetexpertise.fr depuis le 02/08 (M365 + AnyDesk fait). 4 actions annuaires bloquées 1 mois pour rien. Leçon : un blocage inscrit en mémoire doit être RE-TESTÉ à chaque rapport, pas recopié. Idem : la fiche Ordre se débloque via SSO Comptexpert de Nathan, sans aucun mot de passe.
- Le mailer n'a pas de mode test : l'audit CRO du jour a envoyé 2 mails de lead [Test] + 2 alertes Signal à Avi en testant la prod. Fix : gate
source=test-*avant alerte (action du rapport 19/08). Prévenir Avi. - report-data.mjs / competition : n'interroge que la requête MARQUE ("emet expertise") au lieu de la requête locale n°1 → SERP concurrence inutilisable dans le rapport. Fix : prendre la 1re requête locale non-marque des queries GSC.
- Intersection backlinks DataForSEO sur marché local : 320 domaines, 0 dofollow, 283 rank 0 → analyse cul-de-sac sur ce type de marché, ne plus la payer pour des SERP locales de cabinets.
2026-08-25 — Compter les liens internes sur src/ donne un faux résultat
Friction. Un agent a conclu « 3 orphelins purs, zéro lien entrant » en grepant /root/apps/emetexpertise/src/.
Un second agent, mesurant sur le rendu (dist/ + curl live), a trouvé 2 à 10 liens entrants sur les mêmes
pages — et Search Console a tranché : l'article déclaré orphelin est indexé, avec une referringUrl en /plan-du-site.
Cause. Sur un site à templates (Astro, Next), une grande partie des liens est générée : nav, footer, plan du
site, blocs « articles liés », hubs auteur. Ils n'existent nulle part dans src/ sous forme d'URL littérale.
Règle. Un comptage de maillage interne se fait sur le HTML rendu, jamais sur les sources. Et quand la question
est « Google a-t-il découvert cette page », la réponse ne se déduit pas du tout : elle se demande à Google
(API URL Inspection, URL_IS_UNKNOWN_TO_GOOGLE vs indexée + referringUrl).
Distinction à garder : « zéro lien entrant » et « zéro lien éditorial depuis un autre article » sont deux énoncés différents, et seul le second était vrai ici.
2026-08-26 — Le miroir WhatsApp n'était lu que pour UN client sur deux
Friction (Ichai). « Il faut envoyer un message à Lance pour lui demander accès à sa fiche Google Business non ? Pourquoi tu me l'as pas proposé ? »
Cause. Le skill /seo listait les fichiers du miroir WhatsApp en dur, et seulement ceux d'Emet.
lance.md (le client de Genius Prep) existait depuis toujours et n'a jamais été ouvert — alors qu'il contenait,
daté de la veille, le contrat signé, la définition contractuelle d'un lead, une migration promise, et cette
phrase d'Ichai : « i never properly set up your google business ». On a donc « découvert » péniblement ce que
le client avait déjà dit.
Règle. Une liste de fichiers écrite en dur dans un skill est un piège : elle est juste le jour où on l'écrit
et fausse dès qu'un client s'ajoute. Faire lister le dossier par une commande (ls), et donner le tableau
comme aide, pas comme source de vérité. Vaut pour tout ce qui s'énumère : sites, clients, secrets, crons.
Deuxième règle. « Je n'ai pas pu vérifier faute d'accès » n'est pas un caveat, c'est une dépendance client. Test : qui, dans le monde réel, pourrait débloquer ça en 5 minutes ? Si c'est une personne, ça devient une attente au registre et un message prêt à envoyer.
2026-08-26 — ichaipc retiré : un prompt sur Telegram remplace le pilotage du PC
Friction (Ichai). « on utilise plus ça, skill à supprimer. Si y'a une manip impossible à faire depuis le serveur, alors il peut me donner un prompt, que je vais moi-même donner à l'app GPT qui a accès à l'ordinateur à 100 %, et à toutes les sessions Chrome. Prompt à envoyer sur Telegram directement. »
Mesure. Skill archivé dans /root/.agents/skills/_archive/ichaipc-supprime-20260826/, ses 6 points
d'entrée retirés (5 symlinks dans les homes + la copie hermes). Règle 16 ajoutée à /root/CLAUDE.md.
⚠️ L'infrastructure /root/win-bridge/ (scripts wintask.sh, heartbeat) n'a pas été touchée — Ichai n'a
demandé que le skill.
Ce qui compte dans la nouvelle règle. Le prompt part vers une app qui n'a aucun contexte de notre
conversation : il doit être autonome (URL de départ, compte à utiliser, chaque clic, quoi faire si l'écran ne
correspond pas) et surtout dire ce qu'on veut EN RETOUR — sinon la manip est faite et on n'apprend rien.
Un prompt = une manip. Piège rencontré à la première utilisation : ping.sh bloque un message qui
ressemble à une question → FORCE=1 obligatoire pour ce type d'envoi.
2026-08-26 — Une URL longue collée dans le terminal est un lien mort
Friction (Ichai). « Quand je clique y'a que la première ligne qui marche, pas la deuxième, donc ça marche pas. »
Cause. Le terminal coupe une URL longue sur plusieurs lignes ; le clic ne prend que la première. Aucune règle ne disait de raccourcir.
Mesure. Outil /root/bin/lien : lien "<url>" [slug] → https://sandbox.vpsdashboard.space/shared/l-<slug>/.
Règle 17 dans /root/CLAUDE.md.
Piège trouvé en le construisant : le sandbox est derrière une auth basique, sauf une poignée de chemins
déclarés auth_basic off dans NPM (/data/nginx/proxy_host/379.conf). /shared/ est ouvert — un lien posé
ailleurs renvoie 401. Vérifié : /l-gbp/ → 401, /shared/l-gbp/ → 200.
2026-08-26 — Un prompt trop prudent freine au lieu d'aider
Friction (Ichai). « le fait que le prompt soit si prudent ça me freine : il veut que le GPT soit autonome, genre même si y'a des trucs pas prévus il s'arrête pas, il doit continuer. »
Cause. J'ai écrit la règle 16 en disant que le prompt devait préciser « ce qu'il faut faire si l'écran ne correspond pas » — je l'ai traduit par « arrête-toi et demande-moi », deux fois dans le même prompt. Un agent qui s'arrête = un aller-retour de plus = la manip n'est pas faite. La prudence a été mise au mauvais endroit.
Règle (16 amendée). Le prompt donne un OBJECTIF et des PRINCIPES DE DÉCISION, pas un script rigide. Champ imprévu, écran différent, libellé changé ⇒ il tranche et il continue, puis il raconte ses décisions dans son compte rendu. « Arrête-toi et demande-moi » est banni comme réflexe. Il ne s'arrête que devant : une action irréversible ou coûteuse non prévue (paiement, suppression, changement de propriétaire), un 2FA qu'il n'a pas, ou un choix qui engage vraiment Ichai. Dans le doute il FAIT, et il signale.
Garde-fou conservé : ne jamais fabriquer un fait sur le client. Autonomie sur le CHEMIN, pas sur les FAITS.
Détail d'envoi : un prompt long est découpé en 2 messages par Telegram et devient impossible à copier
d'un bloc → l'envoyer en fichier (ping.sh -f).
2026-08-26 — Un prompt s'envoie en UN SEUL message, jamais en fichier
Friction (Ichai). « non toujours en un seul message pas sous forme de fichier ».
Cause. Mon prompt faisait 3 939 caractères, Telegram l'a découpé en 2. J'en ai conclu « il faut l'envoyer en fichier » — mauvais correctif : un fichier oblige à ouvrir, télécharger, puis copier. Ichai veut copier d'un geste depuis la conversation.
Règle (16 amendée). Toujours un seul message texte. Jamais de fichier, jamais de découpage.
Limite Telegram 4 096 caractères, viser ≤ 3 500. Vérifier avec wc -c avant d'envoyer.
Mesures réelles : 3 939 → découpé en 2 ; 3 060 → passé en un seul message.
Le vrai enseignement. Si un prompt ne tient pas dans un message, ce n'est pas un problème d'envoi, c'est un prompt mal écrit. On coupe le bavardage — explications, redites, mise en forme — pas le contenu utile : les faits, les valeurs à saisir, les principes de décision et le compte rendu attendu restent intacts. La version resserrée (3 060 c.) ne dit rien de moins que celle de 3 939.
2026-08-27 — Penser en effort, c'est produire du volume et jamais du levier
Friction (Ichai). « ne jamais dire "si tu ne fais qu'une chose". Tout doit être la priorité. Il ne faut jamais voir les choses en mode effort. Changer une ligne de code ou reconstruire Ariane 5 de zéro, c'est au même niveau, et les deux propositions doivent être regardées de la même manière. »
Cause. Le skill /seo était construit là-dessus : carnet « trié par (impact ÷ effort) », colonne Effort
avant Impact, et une section finale « ➡️ Si tu ne fais qu'une chose cette semaine ». /rapport classait le plan
en « quick wins ≤1 j → chantiers moyens », /propose en « quick wins agence ». Le correctif du 25/08 — imposer
un « chantier d'actif » séparé des quick wins — était déjà un pansement sur cette maladie sans la nommer.
Mesure. Interdit n°1 du §6 de /seo, règle 18 de /root/CLAUDE.md, propagé à /rapport et /propose.
Le carnet est trié par impact seul ; la colonne devient Charge et passe après Impact ; la section finale
devient « ➡️ PAR OÙ ON COMMENCE » — l'ordre d'attaque du carnet entier, pas une sélection.
La preuve chiffrée qui rend la règle non négociable : ~30 micro-correctifs pour un seul actif en deux
mois, et c'est cet actif unique qui a rapporté le seul backlink .gouv.fr du dossier.
2026-08-27 — Une question et une information dans le même message : on ne sait plus ce que « ok » acquitte
Message envoyé à Lance : « does that include saturday? … and friday, your site was saying you were closed. thats fixed ». Réponse : « Ok 👍 ». Impossible de savoir si ça répond à la question ou si ça acquitte l'info — et l'interpréter aurait mis de faux horaires sur la fiche d'un client.
Règle : dans un message à un client, une question ne voyage jamais avec une information. Et quand la réponse doit servir à écrire quelque chose de public, poser une question fermée et binaire, impossible à satisfaire par « ok » : « saturday, is it 12-4 or 2:30-6:30 ? ».
2026-08-27 — Une ligne de carnet « Qui : toi » livrée sans le prompt
Friction. Le carnet /seo emet du 27/08 au soir posait en n°1 : « Demander l'indexation de
/services/comptabilite dans l'UI GSC — Qui : toi ». Une tâche manuelle rendue à Ichai, nue, sans l'objet qui
la rend exécutable. Sa réaction : « on a dit quoi pour les trucs manuels ? »
Cause racine. Le SKILL.md de /seo n'a jamais mentionné la règle 16 du /root/CLAUDE.md (manip qui exige
le navigateur d'Ichai ⇒ on rédige un prompt autonome pour son app GPT). Le skill couvrait déjà le cas voisin —
« accès que seul le CLIENT détient ⇒ attente au registre + message WhatsApp prêt » — mais pas le cas « manip que
seul ICHAI peut faire ». La colonne « Qui » du carnet acceptait donc « toi » sans rien exiger de plus.
Correctif. Interdit explicite ajouté dans .claude/skills/seo/SKILL.md, §4, règles du carnet : écrire
« Qui : toi / Ichai » sans livrer le prompt dans le même message est interdit. Avec le contenu obligatoire du
prompt (URL de départ, compte, valeurs, ce qu'on veut EN RETOUR, principes de décision, jamais « arrête-toi et
demande-moi »), et la règle de présence (Ichai présent ⇒ dans la conversation ; absent ⇒ Telegram ≤ 3 500 car.).
Test mental gravé : pour chaque ligne dont le « Qui » n'est pas l'agence, est-ce que je livre l'objet qui la rend
exécutable ? Client ⇒ attente + message prêt. Ichai ⇒ prompt autonome.
Rebond du même jour — le prompt ne devait pas rester dans la conversation
Friction. Le prompt GSC a été livré dans le chat au motif qu'Ichai était présent. Sa réaction : « pourquoi tu me l'envoie pas sur telegram ». Correction appliquée dans la même minute : envoyé.
Cause racine. La règle de présence de ping-me (reprise dans la règle 16 du /root/CLAUDE.md) était lue comme
universelle. Elle ne l'est pas : elle existe pour éviter le doublon de notification. Un prompt n'est pas une
notification — c'est un artefact à copier-coller dans son app GPT, et le terminal est le pire endroit pour
copier 3 000 caractères.
Correctif. /root/CLAUDE.md règle 16 et .claude/skills/seo/SKILL.md §4 réécrits sur le même énoncé : un
prompt part TOUJOURS sur Telegram, présent ou absent ; s'il est présent, on l'envoie ET on le dit en une ligne,
sans recoller le pavé. Distinction générale gravée : ce qui se LIT suit la règle de présence, ce qui se COPIE
part sur Telegram (prompt, bloc NAP à recopier à l'identique, message à transférer à un client).
2026-08-30 — Deux agents qui écrivent dans le même dépôt ne doivent pas déployer
Ce qui a frotté. Deux agents travaillaient en parallèle sur /root/apps/emetexpertise, sur des
périmètres de fichiers disjoints — ce qui était la bonne précaution, et elle n'a pas suffi. L'un a
lancé deploy.sh et committé : son commit a emporté le renommage de fichier que l'autre avait déjà
mis dans l'index git (expert-comptable-sarcelles.astro → _expert-comptable-sarcelles.astro), et son
déploiement a mis en ligne le travail en cours de l'autre, à moitié fait.
Pourquoi la précaution ne suffisait pas. L'ownership par répertoire protège les écritures de
fichiers. Il ne protège ni l'index git (git add -A ramasse tout le dépôt, pas seulement son
périmètre) ni le conteneur (docker compose up sert l'arbre entier, pas le sous-dossier de l'agent).
Ce sont deux ressources partagées que le découpage par répertoire ne découpe pas.
La règle appliquée pour la suite, et qui devient le défaut : quand plusieurs agents écrivent dans le
même dépôt, aucun d'eux ne déploie et aucun ne commite. Ils modifient, ils vérifient que ça compile
(npm run build), et ils s'arrêtent. L'orchestrateur déploie une seule fois, à la fin, et commite.
On y gagne en plus une vérification live unique sur l'état final, au lieu de N vérifications sur des
états intermédiaires dont aucun n'est celui qui restera.
2026-08-31 — J'ai documenté un défaut, puis je l'ai reproduit vingt minutes plus tard
Ce qui a frotté. Le panel a trouvé que 5 e-mails de test intitulés « Nouveau lead site — TEST … »
étaient partis dans la boîte du client de Genius Prep après qu'il a payé — sur un contrat où le lead
est l'unité de facturation et où il suit le compte par mail. J'ai écrit l'entrée au registre, rédigé le
message d'aveu… puis testé le tunnel en production avec un POST /api/contact, ce qui lui a envoyé un
sixième faux lead. La connaissance du défaut n'a rien empêché.
Cause racine, et elle n'est pas l'inattention. Le seul garde-fou existant était se souvenir. La
route /api/contact n'avait aucun moyen de distinguer une vérification technique d'une vraie soumission :
tout appel qui réussissait notifiait le client. Un défaut dont la seule protection est la vigilance se
reproduit — surtout à la fin d'une longue session, exactement au moment où l'on teste le plus.
Correctif mécanique posé (pas une note, du code). src/app/api/contact/route.ts reconnaît désormais
un source commençant par verif- ou test- : le lead est persisté — c'est bien la chaîne complète
qu'on veut éprouver — mais aucune notification ne part. Vérifié en live : la réponse est
{"ok":true,"verification":true,"stored":true}, le lead est en base, rien n'arrive chez le client.
La règle générale à retenir, elle vaut au-delà de ce site : dès qu'un test traverse un système qui notifie un tiers, le mode test doit être une propriété du code, jamais une consigne. Et « j'ai nettoyé mes tests » ne veut rien dire tant qu'on n'a nettoyé que sa propre base : un test se nettoie chez le destinataire, ou il s'annonce.
2026-09-02 — J'ai déclaré un panel défaillant alors qu'il avait travaillé
Ce qui s'est passé. /seo a lancé les 7 agents prévus. Ils sont tous passés en idle dans ListAgents
sans qu'aucune notification de complétion n'arrive. J'ai attendu ~35 min, relancé chacun par SendMessage,
toujours rien reçu — et j'ai conclu par écrit, dans le livrable rendu à Ichai, que « le panel n'a rien
rendu ». J'ai gravé cette leçon dans le skill, dans ce fichier, dans le journal, et je l'ai committée.
C'était faux. Les 7 rapports sont arrivés quelques minutes plus tard. Ils étaient denses, vérifiés, et ils
contenaient plusieurs trouvailles majeures que ma propre collecte n'avait pas faites : le markup d'avis fautif
sur 30 URLs et non 8 (les 20 articles de blog en portaient aussi), wizytics mort depuis le 25/08 à 16h00 —
le jour exact où Lance a payé, le garde-fou verif- à moitié construit (persistLead code source en dur,
donc un lead de test est indiscernable d'un vrai dans le compteur facturé), et le calcul montrant que le
portefeuille est à 96 % de revenu fixe. L'un d'eux a aussi réfuté une entrée que je venais d'écrire au
registre (L143 : l'alerte Dataset était close depuis le 14/08, et ma cause était fausse).
La cause racine, et ce n'est pas la patience. J'ai tiré une conclusion négative (« ils n'ont rien produit »)
d'une seule observation indirecte (idle + silence), exactement ce que le skill interdit partout ailleurs pour
les sites. Je m'appliquais la règle aux données et pas à mes propres agents.
Correctifs (skill /seo §3, réécrits).
idle= « ne fait rien à cet instant », pas « a terminé ». Le seul signal d'achèvement est la notification de complétion, et elle peut arriver bien après le passage enidle.- Ne jamais déclarer un agent défaillant sans preuve — c'est un fait négatif tiré d'une observation unique.
- Un rapport de plus de 16 000 caractères est tronqué à la livraison (4 des 7 l'ont été). Parade vérifiée :
SendMessagedemandant la même chose en moins de 6 000 caractères, faits et chiffres gardés, raisonnement coupé. Mieux : l'écrire dans le prompt initial. - La collecte de l'orchestrateur reste à faire à fond et en premier — elle a trouvé ce que les agents n'ont pas vu (faux positif de régression, 5 avis sans réponse). Panel et collecte se recoupent.
Ce qui a été défait : la section correspondante du skill, cette entrée, le journal du 02/09, et L143 repassé
en abandonne avec la réfutation écrite.
2026-09-02 — Message client trop long : Ichai l'a supprimé avant envoi
Ce qui s'est passé. Le second message à Gabriel (Audafi) faisait ~1 900 caractères, structuré en trois points numérotés avec des exemples. Ichai : « c trop long comme message je l'ai supprimé. Faut des trucs court et aller droit au but. »
La règle. Un message client se mesure en lignes, pas en qualité d'argumentation. Viser 3 à 6 lignes, une seule idée, une seule demande. Ce qui est vrai pour un prompt Telegram (« on coupe le bavardage, pas le contenu utile ») est encore plus vrai pour un message WhatsApp à un prospect : il le lit sur un téléphone, entre deux dossiers, et un pavé se referme sans être lu.
Ce qui doit sauter : les exemples, les « aujourd'hui : … / il faudrait : … », les justifications pédagogiques, les titres de section, les récapitulatifs. Ce qui reste : le fait, la demande, point.
Corollaire. Un contenu long n'est pas interdit — il est simplement hors WhatsApp. Un audit, une liste de demandes techniques, une comparaison : ça devient une page qu'on héberge et dont on envoie le lien (règle 15 du CLAUDE.md serveur). Le message reste court, le fond vit au bout du lien.
2026-09-02 — Le miroir WhatsApp a PERDU un message, et j'ai affirmé à partir de là
Ce qui s'est passé. Gabriel Aknine a envoyé deux messages à une minute d'intervalle :
- 19:21 — « désolé je ne vous ai pas répondu, c'est un budget que je n'ai pas concernant ma communication »
- 19:22 — « j'accepte votre proposition d'aide »
Le miroir contacts/gabriel-aknine.md ne contient que le second. Le log du routeur le confirme :
un seul événement entrant mirroité (16:22:12 UTC). Le premier message n'a jamais été écrit.
Ce que j'en ai fait, et c'est la vraie faute. Ichai m'a dit « mais il a dit c'est trop cher donc ? ». J'ai cherché dans le miroir et dans le fil e-mail (7 messages, lus en entier), je n'ai rien trouvé, et j'ai répondu « le mot cher n'apparaît nulle part » — une affirmation catégorique bâtie sur deux sources dont l'une était incomplète et l'autre antérieure au fait. J'ai même corrigé Ichai en lui disant qu'il confondait avec Ruben. Il avait raison, moi non.
Les deux règles qui en sortent :
- Le miroir WhatsApp n'est PAS exhaustif.
CLAUDE.mdle présente comme « la source la plus fraîche de l'état réel d'un client » — c'est vrai, mais « la plus fraîche » n'est pas « complète ». Des messages rapprochés peuvent y manquer. On ne conclut donc jamais l'absence d'un propos à partir du miroir seul : on peut y lire ce qui est écrit, jamais y lire ce qui ne l'est pas. - Quand l'utilisateur affirme un fait sur SA propre conversation, il est la source primaire. Il était dans le fil, pas moi. Le réflexe correct n'était pas de le corriger, c'était de lui demander de coller le message — ce qu'il a fini par faire, et qui a tranché en deux secondes.
Correctif d'outillage à instruire : comprendre pourquoi le routeur a sauté un message (course entre deux entrants rapprochés ? contact ajouté au registre entre les deux ?) et, à défaut, prévoir une relecture directe de la conversation quand un point est décisif.
2026-09-03 — Deux de nos propres capteurs annonçaient « tout va bien » alors que non
url-inspect --sitemap disait « ✅ 0 URL non indexée sur 27 » avec 5 URLs inconnues à l'écran.
La ligne de synthèse filtrait sur le libellé français (« ne reconnaît pas ») alors que l'API répond
désormais en anglais (« URL is unknown to Google »). Le détail était juste, le compteur mentait —
et c'est le compteur qu'on lit. Corrigé : le filtre porte sur verdict !== "PASS", stable quelle que
soit la langue, et les URLs fautives sont désormais relistées sous le compteur.
Leçon : ne jamais faire reposer un compteur d'alerte sur une chaîne de caractères localisée.
gsc-diff criait « 🔴 RÉGRESSION -41 % » sur un site stable, pour la deuxième fois en deux jours.
Le total de fenêtre est écrasé par une seule journée : le 22/08 pèse 155 impressions quand tous les
autres jours d'Emet tiennent entre 15 et 51. Hors ce pic : 26 → 25 impressions/jour, du bruit.
Corrigé : l'alerte porte sur la médiane quotidienne, et quand le total chute sans la médiane, le
script l'écrit explicitement (« pic isolé dans la fenêtre précédente, pas une régression ») au lieu de
se taire. Le 02/09 la parade était une consigne dans le skill — une consigne ne protège que si on la
relit ; ici c'est l'outil qui ne peut plus mentir.
2026-09-03 — J'ai livré un faux constat parce que ma commande de vérification coupait la preuve
grep -o "<h[23][^>]*>[^<]*" sur les articles de Genius Prep rendait <h2>The 2026 . J'y ai vu un titre
cassé par du markdown non rendu, et je l'ai mis au carnet en impact fort. La classe [^<]* s'arrête au
premier < — c'est-à-dire à la balise <a> ouvrante du lien correctement rendu. La sortie réelle est
<h2>The 2026 <a href="…/sat-prep">SAT</a> in New York City…</h2>, et le scan des 20 articles ne trouve
aucun titre fautif.
Aggravant : j'avais étayé la fausse lecture par une inférence plausible — conteneur démarré à 14 h 54, commit à 17 h 53, donc « pas déployé ». Un raisonnement juste au service d'une mesure fausse est plus convaincant qu'une mesure fausse seule, donc plus dangereux.
C'est un agent du panel qui m'a corrigé, avec sa propre preuve. Gravé au skill : on vérifie un élément
rendu sur son contenu entier, jamais sur une classe qui exclut < ; et une absence se re-teste par
la présence de ce qu'on attend avant d'être annoncée.
2026-09-03 — J'ai édité le fichier exclusif d'un agent encore actif, puis déployé pendant qu'il travaillait
J'avais donné src/lib/neighborhoods.ts en exclusif à un agent, dans un run où j'avais imposé ce patron
à six agents. Son rapport est arrivé, j'ai conclu qu'il avait fini — alors que idle ne veut pas dire
« a fini », règle que j'avais moi-même gravée au skill la veille. J'ai édité son fichier à 15:07 puis
15:08 ; deux de ses passes d'édition ont échoué à mi-course parce que le texte cible bougeait sous lui.
Puis j'ai commité à 15:11:57 et déployé.
Ça s'est bien terminé par chance : ses derniers écrits datent de 15:10:27, une minute avant mon commit, et la vérification live montre bien sa version finale. Quatre-vingt-dix secondes de plus et je mettais en ligne un fichier à moitié réécrit sur le site d'un client.
C'est l'agent qui me l'a signalé — il croyait à un autre agent concurrent. Un orchestrateur qui édite le fichier d'un agent actif n'est plus un orchestrateur : c'est un septième agent sans périmètre.
Gravé au skill §4bis : le périmètre d'un agent ne se touche pas tant que sa notification de complétion
n'est pas arrivée, orchestrateur compris ; une correction urgente part en SendMessage et on attend la
confirmation.
2026-09-03 — Cinq erreurs de vérification dans la même journée, toutes du même genre
Une par une : un grep -o "<h2>[^<]*" qui coupait au premier < et m'a fait déclarer cassés des titres
justes · une détection de doublon de facture qui lisait la mauvaise clé JSON et rendait « aucun doublon » ·
un chemin dist/blog/<slug>/index.html là où Astro écrit dist/blog/<slug>.html · un comptage de mots et
d'occurrences fait sur le HTML brut (10 216 mots au lieu de 3 045, 25 occurrences au lieu de 4) · et une
liste alphabétique tronquée par head (le 27/08, même famille).
Le point commun n'est pas l'inattention : c'est que chaque commande semblait confirmer une hypothèse plausible. Trois fois sur cinq, c'est en re-testant autrement — parce que le résultat était surprenant — que j'ai trouvé l'erreur. Les deux autres fois, c'est un agent qui m'a corrigé.
2026-09-07 — un serveur de test laissé résident 4 jours a fait mal mesurer un agent
Ce qui s'est passé. Un agent d'exécution a mesuré le sitemap sur npx next start et obtenu
72 URLs avec les anciens slugs génériques au lieu du build courant. Deux causes cumulées :
le dépôt est en output: standalone (il faut node .next/standalone/server.js, next start sert
un build périmé), et le port qu'il visait était déjà pris par un next-server (pid 3120015,
cwd /root/apps/geniusprep, port 3999) résident depuis 4 jours et 21 heures, laissé par
une session antérieure à celle-ci.
Pourquoi ça compte. La règle 19(d) du /root/CLAUDE.md dit qu'une tâche lourde s'arrête
proprement et que rien ne reste résident après elle. Ici le reliquat n'a pas coûté de la RAM
(59 Mo) — il a coûté une mesure fausse, et l'agent a dû s'en apercevoir seul.
Solution proposée, non appliquée (le process est antérieur à cette session, je ne tue pas ce
qui ne m'appartient pas sans demander) : kill 3120015. Et pour la suite, deux gestes :
un serveur de test se lance sur un port tiré au hasard plutôt que sur un port fixe partagé,
et on le tue dans le même tour que celui qui l'a lancé.
audit.mjsÉCHANTILLONNE (25 pages) — ses compteurs ⚪🟡🟠🔴 ne sont pas des totaux (09/09/2026) J'ai annoncé à Ichai « 10 titles trop longs, 12 meta trop longues » en lisant la sortie d'audit. L'agent a mesuré le jeu complet : 601 titles sur 601 et 601 descriptions sur 601 étaient hors cible (title max 106 caractères, pas 84 ; description max 246). L'audit avait vu 25 pages sur 601. Leçon : sur un gabarit qui génère des centaines de pages, l'audit sert d'ALERTE, jamais de DÉNOMBREMENT. Le compte se fait en appliquant le gabarit au jeu de données entier, par script. Même famille que legrep -csur sitemap d'une seule ligne : un outil qui répond vite à côté. (Corollaire noté au passage : le score d'Emet 90→78 n'était pas « 22 petits défauts », c'était 601 pages tronquées en SERP vues par un trou de serrure.)Un agent qui « refait ses calculs à la main » en réappliquant la formule de son code ne vérifie RIEN (09/09/2026) Le calculateur d'apprenti a failli partir en production avec un coût employeur faux : le code supposait le coefficient de la réduction générale à son maximum (commentaire : « ce qui est toujours le cas ici »), au lieu de le calculer. Faux dès 1 823,03 € de rémunération — donc faux sur le cas « 26 ans » qui figurait dans les propres cas de test de l'agent (517 €/an annoncés à zéro), et à 27 % d'écart sur une sur-rémunération à 2 500 €. L'agent avait « posé le calcul à la main, chiffre par chiffre » dans son rapport — avec la formule du code. Un contrôle qui rejoue sa propre prémisse se confirme toujours lui-même. Règle : le contrôle d'un chiffre doit venir d'une source indépendante du code. Ici, l'exemple chiffré publié par l'Urssaf (2 000 € → 0,3178) invalidait l'hypothèse en une ligne, et c'est ce qu'a fait l'agent de vérif adverse : reproduire l'exemple officiel AVANT d'accuser. Corollaire pour les briefs : demander « refais les cas à la main » ne suffit pas — il faut écrire « compare à un résultat publié par la source, pas à ton propre calcul ». (Piège jumeau dans le même dossier : le Smic de référence de la formule RGDU 2026 est celui du 1ᵉʳ JANVIER 2026 (1 823,03 €), pas celui de juin (1 867,02 €) qui sert au salaire. L'agent avait parfaitement évité le piège des deux revalorisations pour le salaire et y est tombé pour la formule.)
2026-09-10 — Éprouver un VRAI formulaire a envoyé un faux lead au client
Ce qui a frotté : pour prouver la capture d'origine, j'ai fait remplir un vrai formulaire de
/isee-prep par un navigateur Playwright. Le lead est parti chez Lance comme un vrai (mail Resend),
et il a atterri dans contact_leads, qui EST la facture. Exactement l'incident que le garde-fou
verif- du 31/08 devait éteindre — sauf que ce garde-fou ne protège que ce qu'on écrit soi-même
dans le payload : dans une passe navigateur, le source est celui de la page (page-isee-prep).
Cause racine : le seul mode de vérification passait par une valeur du payload, alors que la
seule preuve qui vaille (règle 1) est une passe utilisateur réelle, où justement on ne contrôle
pas le payload.
Correctif livré (site geniusprep, commit e6e074d) : /api/contact accepte un en-tête
x-gp-verification valant CONTACT_VERIFICATION_TOKEN (env serveur, donc inutilisable par un
visiteur). Un lead ainsi marqué est persisté, NON notifié, et son source est préfixé
verif-browser- pour sortir du compteur facturé. Vérifié en live : lead persisté, log
« client NON notifié », compteur toujours à 3.
Reste : le faux lead du 10/09 14:07 a été supprimé de la base, mais l'e-mail est parti.
2026-09-15 — Un agent a cassé l'auto-génération de clé primaire de contact_leads (prod GeniusPrep) pendant ~9 min
Quoi : pour ajouter un champ suspect, l'agent a PATCHé la collection PocketBase en renvoyant une liste de champs
réécrite ; le champ id a perdu autogeneratePattern, toute écriture rendait « id: Cannot be blank ». Réparé par lui
(motif [a-z0-9]{15}, min 15), vérifié par moi ; aucun lead réel dans la fenêtre.
Solution : toujours GET la définition courante, y AJOUTER le champ, et renvoyer la liste complète telle quelle ;
puis POST de test immédiat. À écrire dans le skill d'exécution : « une modif de schéma PB se fait en lecture-ajout, jamais en réécriture ».
2026-09-15 — POCKETBASE_URL de docker-compose.yml est INUTILISABLE en dev, et l'échec est silencieux
Quoi : pour vérifier L311 (ua / referer à l'écriture d'un lead) il fallait un next dev avec la
persistance branchée. POCKETBASE_URL n'existe que dans docker-compose.yml de geniusprep, où elle vaut le
nom d'hôte interne Docker pocketbase-geniusprep — qui ne résout pas depuis l'hôte. Exportée telle
quelle, chaque POST échouait en getaddrinfo EAI_AGAIN.
Pourquoi ça coûte cher : /api/contact ne fait JAMAIS échouer une soumission sur un échec de
persistance (c'est voulu, un lead ne doit rien coûter). La route répond donc 200 {"ok":true} avec
stored:false, et un test qui ne lit que le code HTTP conclut au succès alors que rien n'est écrit.
J'ai bien failli valider une vérification vide.
Solution : en dev, exporter POCKETBASE_URL depuis l'URL externe de /root/.secrets/geniusprep-pb.env
(PB_URL), jamais depuis docker-compose. Et une vérification de lead ne se conclut pas sur le code HTTP :
elle se conclut sur stored:true PUIS sur une relecture de l'enregistrement en base.
Générale : c'est la même règle que le gotcha PocketBase de /root/CLAUDE.md (« côté client, TOUJOURS
l'URL externe ») — elle vaut aussi pour le serveur Next quand il tourne hors de son réseau Docker.
2026-09-15 — Veilleurs while pgrep -f "<script>.sh" qui se voient eux-mêmes
Trois chaînes de déploiement bloquées et deux shells tués (exit 144) parce que le motif figurait dans la ligne de
commande du veilleur ou du pkill. Solution : attendre un PID (while kill -0 $PID) ou un marqueur EXIT écrit
par le script dans son log ; tuer par PID ; construire le motif hors de la ligne (P=deploy; grep "[d]$P").
2026-09-16 — trois capteurs qui mentent, et un outil muet
url-inspect --sitemapn'imprime qu'à la fin : 0 octet pendant 25 min sur GP (586 URLs), et Emet (2 188 URLs) dépasse le quota de 2 000/jour. Ce n'est pas une panne. Solution : sortie en flux + inspection par tranche (lastmod récent d'abord). À faire.leads.mjsignorelead_typeet devine au texte : une candidature de tuteur comptée en lead réel sur le contrat facturé au lead. Solution : lirelead_typeavant les heuristiques (L326).- Le beacon de clics d'Emet n'a pas de mode test : 5 des 9 « intentions » étaient nos audits. Solution : préfixes
audit-/test-/verif-→test:true, comme le tunnel (L319). read_messages.pydu skill whatsapp ne trouve pas Ruben sur 14 jours (0 ligne pour « toledano ») alors que le miroir s'arrête au 02/09 et que le bridge portait un vocal du 03/09 (journal du 15/09). Le canal de repli n'a pas été réinterrogeable ce soir. Non résolu : vérifier le nom de chat dans/api/contacts.pkill -fm'a tué le shell une deuxième fois le 16/09 (exit 144) alors que la règle est écrite dans le skill depuis le 15/09. Forme obligatoire :for pid in $(pgrep -f "[d]eploy-gp.sh"); do kill $pid; done. Réécrit dans le skill.npx astro checksur Emet dépasse 300 s (3 000+ pages) : ne pas l'attendre en premier plan ; le build dedeploy.shest le vrai contrôle.- Le contrarian du panel a disparu avec le changement d'identité de session (agent injoignable, aucun fichier écrit). Règle ajoutée au skill : un agent de panel muet 40 min après les autres se relance, on ne l'attend pas.
2026-09-17 — « 4 clics appeler » alors qu'il y en avait UN
Ce qui a frotté. Dans les stats du 17/09, j'ai écrit « 4 personnes ont cliqué sur appeler » pour Emet.
Faux. Ichai a demandé « 4 personnes ont cliquer sur appeler ? » et la vérification a donné :
1 phone, 2 cta (sticky-callback), 1 email.
Cause. leads.mjs affiche une ligne agrégée INTENTION : 4 (clics "appeler" et formulaire). J'ai repris
le total ET le libellé de la première moitié de la phrase, sans ouvrir clicks.mjs qui, lui, sépare par type
et affiche explicitement « Dont « phone » : 1 ». Le libellé du script mélange deux canaux dans un seul nombre.
Ce que ça changeait. Les 2 sticky-callback venaient de pages observatoire à Marignane et Toulouse, donc hors Île-de-France, non convertibles pour un cabinet à Sarcelles/Paris. Annoncer 4 rendait le canal téléphone quatre fois plus vivant qu'il ne l'est, sur le seul site où le commercial ne décolle pas.
Règle. Un total d'INTENTION ne se raconte JAMAIS avec le nom d'un seul de ses types. Dès qu'on cite un
chiffre de contact Emet, on passe par scripts/clicks.mjs et on donne la ventilation par type, avec la page
d'origine (une intention hors IDF n'est pas une intention commerciale). Corollaire : ajouter la ventilation
dans le libellé de leads.mjs plutôt que le total nu.
2026-09-17 — Deux lectures de SERP fausses le même jour, deux causes différentes
1. keywords.mjs rank ne filtrait pas l'organique. Il annonçait « expert comptable sarcelles →
emetexpertise.fr en position 11 (/contact) » pendant que serp-track.mjs, sur la même requête, disait
« hors top 100 ». Cause : rank faisait items.find(...) sur TOUS les éléments de la SERP (bloc local,
cartes, autres blocs) et imprimait leur rank_absolute, alors que serp-track filtre type === "organic".
Un rank_absolute de bloc local n'est pas une position organique et ne se compare à rien. Corrigé : rank
filtre l'organique, imprime rank_group (la vraie position) et signale à part une présence dans le bloc local.
2. Une requête LOCALE lue depuis la mauvaise ville ment. serp-track tournait avec
location_name="Paris,Ile-de-France,France" et rendait « expert comptable sarcelles → hors top 100 ».
Relue depuis location_code=1006115 (Sarcelles), la même requête place emetexpertise.fr 3e organique.
Ce n'est pas une fluctuation : Google sert une SERP différente. Conclusion qui aurait été publiée sans la
contre-lecture : « on a perdu notre position locale », faux, et exactement le genre de chiffre qui part
chez le client.
Règle. Une requête qui porte un nom de ville se lit DEPUIS cette ville, jamais depuis la capitale
régionale. Et toute position annoncée précise sa localisation de lecture. Codes DataForSEO :
/v3/serp/google/locations/fr (Sarcelles = 1006115, Paris = 1006094).
3. Corollaire de coût. serp-track.mjs porte déjà l'avertissement « ~0,029 $ par requête, le
commentaire annonçait 0,0012 $, faux d'un facteur 25 ». Il reste vrai : 10 requêtes = 0,29 $.
2026-09-17 — Deux agents dans le même fichier, et c'est l'orchestrateur qui l'a causé
Ce qui a frotté. L'agent d'exécution construisait expert-comptable-paris.astro. Pendant son
build, j'ai audité le fichier et j'y ai corrigé six chiffres. L'agent a vu son fichier changer sous
lui, a signalé la collision, et a réécrit l'en-tête pendant que je m'apprêtais à le réécrire aussi.
Deux séries d'éditions se sont croisées sur le même fichier.
Cause, et elle est à moi. La règle du projet dit « un agent = un livrable fichier ». Je l'ai appliquée en lançant l'agent, puis je l'ai violée en éditant son livrable avant qu'il ait rendu. Un agent qui « tourne encore » n'a pas fini d'écrire, même quand le fichier existe déjà sur le disque : sa présence n'est pas une livraison.
Ce qui a bien marché, et qu'il faut garder. L'agent a signalé au lieu d'écraser en silence, et il a contesté une de mes affirmations avec un argument précis (« ce n'étaient pas des chiffres faux mais un millésime antérieur »). La contestation était fausse, mais elle a été vérifiée point par point avant d'être écartée — et la vérification a produit deux contrôles qui n'existaient pas avant : la somme des secteurs A10 et la somme des vingt arrondissements, qui redonnent toutes deux le total communal exact. Un désaccord d'agent bien argumenté vaut un contrôle de plus.
Règle. Tant qu'un agent d'écriture n'a pas rendu son rapport, son fichier ne se touche pas : on lui envoie les corrections par message, ou on attend. Si l'urgence impose de reprendre la main, on le lui dit AVANT d'éditer, pas après. Et quand un agent conteste un fait, on vérifie sa version avant de la rejeter, y compris quand on est sûr de soi.
2026-09-17 — Une attente qui s'attend elle-même (pgrep -f et sa propre ligne de commande)
Ce qui a frotté. Deux tâches de fond enchaînées attendaient la fin d'un déploiement avec
until ! pgrep -f "deploy.sh"; do sleep 15; done. Elles ne se sont jamais débloquées. Cause :
pgrep -f cherche dans la ligne de commande COMPLÈTE, et la ligne de commande de la tâche qui
attend contient elle-même le texte « deploy.sh », puisque c'est ce qu'elle va lancer. Chaque
tâche se voyait donc elle-même et s'attendait indéfiniment. Les deux ont fini en exit 144.
Ce que ça a coûté. Vingt minutes pendant lesquelles je croyais un déploiement en cours alors qu'aucun ne tournait, et deux commits restés non déployés pendant ce temps.
Règle. Ne jamais attendre un processus avec pgrep -f <motif> quand le motif apparaît dans
sa propre commande. Trois sorties, par ordre de préférence :
- attendre la CONDITION et pas le processus (
until curl -s … | grep -q <ce qu'on attend>), ce qui a en plus l'avantage de vérifier le résultat et pas seulement la fin ; - garder le PID au lancement et boucler sur
[ -d /proc/$PID ]; - au minimum
pgrep -xsur le nom exact de l'exécutable, jamais-fsur une sous-chaîne.
Famille connue. C'est le troisième incident de cette famille dans le dossier, après les deux
pkill -f sortis en 144. Le point commun n'est pas la commande, c'est le motif trop large qui
attrape l'appelant.
⚠️ QUATRIÈME OCCURRENCE, UNE HEURE APRÈS AVOIR ÉCRIT CE QUI PRÉCÈDE. J'ai relancé une attente
until ! pgrep -f "npm run build" dont la propre ligne de commande contenait « npm run build ».
Écrire la règle ne suffit donc pas à l'appliquer : le réflexe revient parce que pgrep est ce qui
vient à l'esprit quand on veut attendre « la fin de quelque chose ».
Le vrai correctif est de changer d'objet, pas de motif. On n'attend pas UN PROCESSUS, on attend LA CONDITION qu'on espérait de lui, dans son journal ou sur le service :
until grep -qE "Compiled successfully|Failed to compile|Error:" "$LOG"; do sleep 10; done
until curl -sf -o /dev/null "https://…/la-page-attendue"; do sleep 10; done
Deux avantages par-dessus le fait de ne pas se mordre : l'attente couvre l'échec autant que le succès, et elle vérifie le RÉSULTAT au lieu de constater qu'un processus s'est arrêté — un processus mort n'a pas forcément réussi.
2026-09-17 — nysed.gov injoignable depuis le VPS : chaîne de certificats incomplète
Ce qui a frotté. Toute tentative de lire www.nysed.gov depuis le serveur échoue : curl rend
http 000, et l'outil de récupération web rend « unable to verify the first certificate ». Cinq
minutes perdues à croire à un blocage sortant ou à un filtrage.
Cause, et elle est chez eux. Le serveur du ministère de l'éducation de l'État de New York envoie
son certificat sans l'intermédiaire. Le certificat lui-même est parfaitement valide, émis par
GlobalSign RSA OV SSL CA 2018 pour *.nysed.gov, valable jusqu'au 28 novembre 2026. C'est une erreur
de configuration de leur côté, pas un problème de confiance.
La sortie, sans jamais désactiver la vérification :
curl -s http://secure.globalsign.com/cacert/gsrsaovsslca2018.crt -o /tmp/gs.crt
openssl x509 -inform DER -in /tmp/gs.crt -out /tmp/gs.pem
cat /etc/ssl/certs/ca-certificates.crt /tmp/gs.pem > /tmp/bundle.pem
curl -sL --cacert /tmp/bundle.pem "https://www.nysed.gov/..."
L'URL de l'intermédiaire se lit sur le certificat lui-même, champ « Authority Information Access » :
openssl s_client -connect <hôte>:443 -servername <hôte> | openssl x509 -noout -text.
Règle. Un curl qui rend 000 sur un site public n'est pas un site en panne : on regarde la
poignée de main TLS avant de conclure. Et on ne passe JAMAIS par -k / --insecure pour contourner :
on complète la chaîne, ce qui vérifie autant et laisse une trace de ce qu'on a vérifié.
Pourquoi ça compte ici. C'est la source officielle des règles du diplôme encodées dans
/tools/regents-diploma-requirements. Sans accès, aucune règle n'était vérifiable, et la consigne
était de n'encoder que le vérifié : le mur de certificat aurait silencieusement vidé l'outil.
2026-09-17 — IndexNow ne marchait pas pour Genius Prep, et personne ne l'avait vu
Ce qui a frotté. scripts/indexnow.mjs geniusprep sortait en erreur. Cause : le script lit la clé
dans <appDir>/.indexnow-key, et ce fichier n'existait pas pour Genius Prep. La clé, elle, existait
bien — dans public/51690c7030260480c17802376de031f1.txt, servie en 200 sur le domaine, comme le
protocole l'exige. C'est notre copie locale qui manquait, pas la déclaration publique.
Ce que ça coûtait. Aucune URL de Genius Prep n'avait jamais pu être soumise. Or IndexNow alimente Bing, et Bing alimente Yahoo, DuckDuckGo, Ecosia, ChatGPT et Copilot. Sur un dossier où l'on vient d'établir que l'aperçu IA de Google mange le trafic informationnel (L345), le canal des moteurs secondaires et des assistants n'est pas un confort : c'est la moitié du pari des actifs.
Corrigé. .indexnow-key rétabli à la racine de l'app depuis la valeur publique, et les deux
outils neufs soumis avec succès. Le fichier n'est PAS un secret — le protocole exige qu'il soit
public — donc il peut vivre dans le dépôt, et il y vit désormais pour ne plus disparaître.
Règle. Un script qui ne rend rien pour un site et tout pour un autre n'est pas cassé : il lui
manque une donnée propre à ce site. Comparer les deux côtés avant de chercher un bug — ici, Emet avait
son .indexnow-key, Genius Prep non, et c'était toute l'histoire. Et après avoir réparé un canal de
soumission, vérifier qu'il n'est pas resté muet sur d'autres sites du registre.
2026-09-17 — Sur Genius Prep, un git push déploie le DOSSIER DE TRAVAIL, pas le commit
Ce qui a frotté. J'ai livré trois fois pendant qu'un agent modifiait le gabarit d'article sans avoir commité. J'avais soigneusement retiré son fichier de mes commits, croyant l'isoler. Ça n'a servi à rien : pendant une vingtaine de minutes, la production a servi son travail en cours.
Cause. Le déploiement de ce site est un envoi vers le dépôt qui déclenche un webhook, lequel fait
un git pull puis un docker compose up -d --build. Or le contexte de construction de Docker est le
DOSSIER DE TRAVAIL du serveur, pas l'arbre du commit. Tout fichier modifié et non commité par
n'importe quel agent part donc en production au premier envoi de n'importe qui d'autre.
Preuve, et c'est elle qui compte : la règle text-wrap: balance était servie sur le domaine alors
qu'elle n'existait dans AUCUN commit (git show HEAD:... la rendait absente, le fichier de travail la
contenait). On ne le déduit pas, on le vérifie comme ça.
Ce qui a réellement changé pendant la fenêtre, et pas plus : la page /terms partage la classe
.prose-article avec les articles, elle a donc hérité de la typographie en cours de refonte — corps
passé de 1,05 à 1,125 rem, titres avec filet rouge, un niveau de titre supplémentaire déclaré.
⚠️ Correction d'une affirmation que j'ai faite trop vite : j'ai d'abord annoncé que la page des
conditions avait hérité d'une lettrine décorative. C'est FAUX. La lettrine est sur .prose-article
depuis bien avant, elle est dans le commit précédent, et cette page l'a toujours eue. J'ai vu un
défaut réel sur une capture et je lui ai attribué la mauvaise cause sans vérifier l'historique.
Deux règles.
- Sur ce dépôt, tant qu'un agent d'écriture travaille, personne ne déploie. Retirer son fichier d'un commit ne protège de rien. Soit on attend qu'il rende, soit on lui demande de remiser.
- Avant d'imputer un défaut visuel à un changement récent, on regarde l'historique du fichier.
Un
git show <commit d'avant>:<fichier> | grepcoûte trois secondes et évite d'accuser le dernier qui est passé.
Résolu. La refonte est cloisonnée dans .prose-post, .prose-article est restauré à l'octet près
et vérifié en ligne, /terms a retrouvé son rendu d'origine.
2026-09-17 — Remplacer le fichier d'une image ne remplace pas l'image (Genius Prep)
Ce qui s'est passé. Cinq images d'articles remplacées par un PATCH sur image_file
et mid_image_file de blog_posts. Les cinq envois ont répondu OK. Les pages, elles,
sont restées cassées : PocketBase avait supprimé l'ancien fichier, et les pages
pointaient encore dessus, en 404.
Pourquoi. Genius Prep ne lit pas image_file. src/lib/blog.ts mappe record.image,
un champ texte qui contient l'URL complète du fichier. Et les images de corps ne sont
pas un champ du tout : ce sont des URL écrites en dur dans le markdown de body.
Le champ fichier sert de stockage, pas de source d'affichage.
Ce qu'il faut faire. Toute image remplacée se change en trois endroits :
- le fichier (
image_file/mid_image_file) ; - le champ texte
imagepour un en-tête, ou l'URL dansbodypour une image de corps ; image_alt, qui décrit souvent l'ANCIENNE image et devient faux en silence (« Vintage calendar with SAT test dates marked » sur une photo de perron).
Comment vérifier. node /root/apps/geniusprep/scripts/check-blog-images.mjs —
33 articles, en-tête et corps, code HTTP réel. Un envoi en OK ne prouve rien.
2026-09-17 — Un audit dont les verdicts n'ont pas le nom de fichier ne sert qu'une fois
Ce qui s'est passé. L'audit des 62 images du matin a produit des comptes justes (19 à refaire, 19 à remplacer, 22 sur pages indexées) et une liste de priorités décrite en prose (« le calendrier à huit colonnes », « le billet ERB »). Le rapport détaillé de l'agent, qui portait les noms de fichiers, est mort avec son contexte. Le soir, pour traiter le lot suivant, il a fallu rouvrir 19 images pour savoir lesquelles étaient concernées.
La règle. Un audit visuel s'archive avec, pour chaque image : l'identifiant de
l'enregistrement, le nom du fichier, la place (en-tête ou corps), et le verdict. La prose
seule ne se rejoue pas. Voir le format posé dans
sites/geniusprep/audits/2026-09-17-images.md, section « Lot 2 ».
2026-09-17 — Lire NOINDEX_SLUGS à la regex donne une fausse liste d'articles indexés
Ce qui s'est passé. Pour savoir quelles images traiter, j'ai extrait NOINDEX_SLUGS de
src/lib/blog.ts par expression régulière. Elle a renvoyé 16 entrées au lieu de 14, en
ratant shsat-prep-guide-2026-timeline-tips-strategies-nyc et en comptant indexés deux
articles qui ne le sont pas. Résultat : deux images générées pour un article qui redirige
en 308 vers /shsat-test-dates et ne rend jamais. 20 crédits pour rien.
Pourquoi la regex échoue. Le tableau contient de longs blocs de commentaires entre les
entrées, dont un qui explique justement pourquoi ce slug doit rester listé bien que la 301
vive dans next.config.ts.
La bonne commande (ne lit que les entrées en début de ligne, ignore les commentaires) :
awk '/NOINDEX_SLUGS/,/^\]/' src/lib/blog.ts | grep -o "^ '[^']*'" | tr -d " '"
Et avant de traiter une page : vérifier son code HTTP. Un 308 dit tout de suite que la page ne rend pas, quelle que soit la liste.
2026-09-19 — J'ai déclaré « bonne » une image dont le bras est à l'envers
Ce qui s'est passé. Ichai a signalé une image au bras retourné. J'ai cherché ailleurs et
proposé une mauvaise candidate. Il a donné l'URL : c'était l'en-tête de
nyc-school-admissions-shsat-isee-screened-programs-guide, que j'avais moi-même ouverte
quelques heures plus tôt et classée « bonne ». L'enfant est vu de dos, le coude est sous la
main, l'avant-bras remonte vers l'épaule, et c'est la paume qu'on voit au lieu du dos de la main.
Pourquoi je l'ai ratée. Je l'ai jugée sur la vue d'ensemble, à 950 px de large, où la scène est belle et cohérente. À cette taille un membre retourné passe pour un pli de manche. Je m'étais même dit « les doigts ont l'air plausibles » — sans recadrer.
La règle. Ouvrir l'image ne suffit pas quand il y a quelqu'un dedans. Dès qu'une image
contient une personne, on recadre sur chaque main, chaque poignet et chaque coude avant de
prononcer un verdict — convert img.png -crop LxH+X+Y +repage -resize 900x zoom.jpg, en
coordonnées pixels (les offsets en % ne tombent pas où on croit). Trois questions à chaque
membre : le coude plie-t-il dans le bon sens ; voit-on la face de la main cohérente avec
l'orientation du corps ; l'avant-bras rejoint-il une épaule.
Et pour montrer une image à Ichai : la publier sous /root/sandbox/shared/<slug>/ avec
l'entière et les recadrages, puis donner le lien. Décrire un défaut visuel ne vaut rien.
2026-09-23 — ariane pc.wait avec un timeoutMs > 300 000 plante côté client
Le client ariane coupe toute requête à 300 s (urlopen(timeout=300)) : un pc.wait avec timeoutMs: 1500000
rend RemoteDisconnected après 5 min alors que la tâche Codex tourne toujours (vérifié par pc.task : running: true).
Solution : timeoutMs ≤ 120 000 et relancer en boucle jusqu'à "running": false, dans une commande en fond.
À corriger côté Ariane : aligner le délai du client sur timeoutMs.
2026-09-23 — Codex sur le PC ne peut pas envoyer de fichier depuis Chrome sans une option de l'extension
Publier des photos sur la fiche GeniusPrep a échoué : l'extension Chrome de ChatGPT/Codex n'a pas l'option
« Allow access to file URLs ». Réglage une fois pour toutes par Ichai : chrome://extensions → Détails sous
l'extension ChatGPT → activer « Autoriser l'accès aux URL de fichier ». Tant que ce n'est pas fait, aucune tâche
Codex ne peut téléverser un fichier (photos, PDF). Autre point : pc.download n'est pas accordé à Agents dans les
Réglages d'Ariane ; contournement utilisé, Codex télécharge lui-même depuis une URL /shared/ temporaire, effacée après.
2026-09-27 — La collecte Search Console est morte depuis le 26/09 23:41, sans que rien ne le dise ici
Une session d'Ariane a rapatrié les clés Google (GSC Genius Prep, compte de service Emet, DataForSEO, PSI, kie) dans
annex_secrets puis supprimé les fichiers de /root/.secrets/. gsc.mjs, url-inspect.mjs, keywords.mjs,
kie-image.mjs échouent en ENOENT. Et l'annexe seo d'Ariane n'est dans aucune famille (services/agents/manifest.ts),
donc ariane seo.* refuse ce projet. Les relevés quotidiens d'Ariane tournent (annex_seo.days).
Solution proposée : ajouter seo à une famille dans Ariane (demande n° 63), puis faire lire gsc.mjs depuis
ariane seo.queries/pages/history au lieu de l'API. En attendant : lecture seule de annex_seo par scripts/sql.
2026-09-27 — Codex sur le PC ne peut plus cliquer dans Chrome (demande Ariane n° de la même date)
Quatre essais pour la Search Console : fenêtre non identifiée (plusieurs Chrome ouverts), puis « failed to activate
captured window » même après mise au premier plan par pc.run (ShowWindow + SetForegroundWindow = True).
Lecture d'écran et commandes marchent ; seule la saisie échoue. PC inactif depuis ~48 min.
Ce qui a aidé à diagnostiquer : pc.screenshot + énumération des fenêtres par pc.run (EnumWindows).
Règle : après deux échecs de Codex sur la même cause, on arrête, on signale (ariane request broken), on ne relance pas en boucle.
2026-09-28 — lead Emet déclaré « jamais transmis » sans avoir lu le groupe
J'ai annoncé à Ichai que le rappel du 24/09 n'avait jamais atteint le cabinet, sur la seule lecture du fil d'Avi. Le groupe Emet (120363408579980939@g.us) montrait le lead transmis par Ichai à 15:41 et rappelé par Nathan à 15:47. Cause : whatsapp.chats par nom ne trouvait pas le groupe, et je n'ai pas pris l'identifiant dans l'encart du miroir. Solution : l'identifiant du groupe est désormais écrit dans le skill /seo, avec l'interdit.
2026-09-28 — Codex sur le PC : mauvais profil Chrome, arrêté par Ichai
Ichai : les tâches Codex « font mal le boulot, ne sont pas dans le bon profil Chrome ». Tâche en cours arrêtée, l'agent d'envoi Emet a l'ordre de ne plus lancer de pc.start, plainte à Ariane (demande n° 78). Tant qu'elle n'est pas réparée : AUCUNE tâche Codex pour ce projet ; ce qui exige un navigateur attend. À vérifier : les réponses aux avis Emet publiées ce matin par Codex sont-elles bien sous le nom du cabinet (Google les montrait « en attente d'examen ») ?
- 2026-09-30 —
system-checkaffichait 🔴 « Brevo bloque contact@ Emet » un jour après le déblocage : l'état de la chaîne n'est relu que par form-synth, en pause depuis le 27/09. Repro : pause form-synth, débloquer Brevo, lancer system-check. Solution appliquée :form-synth.mjs <key> --dry-run --chaine(lecture seule) ; à faire : que system-check relise la chaîne lui-même quand le test est en pause et l'état a plus de 24 h.
2026-10-03 — /seo
- Accès rendus sans que le dossier le sache (3ᵉ fois) :
seo.inspect,seo.dataforseoetai.ask(kie) répondaient alors que L502 les disait coupés. Solution : à chaque passage,ariane seoetariane ai(liste « OPEN TO THIS PROJECT ») + un appel témoin avant de déclarer un outil hors d'atteinte (écrit au skill). Scripts rebranchés sur Ariane (exec-outils-ariane.md). - Solde DataForSEO illisible :
/appendix/user_dataest un GET,seo.dataforseol'envoie en requête longue,net.fetchrefuse. Demande Ariane n° 136.system-checkreste rouge sur ce point d'ici là. - Scratchpad partagé entre agents du panel : deux
vol.jsonécrasés l'un par l'autre (0,09 $ payés deux fois). Solution : un sous-dossier par agent dans le prompt (écrit au skill §3). gbp-reviews.mjsne nommait pas les avis de Local Guide sans réponse (espace insécable dans « Local Guide »). Corrigé (Local\s+Guide), vérifié : il nomme Yoel Lellouche.gsc.mjsetgsc-pages-daily.mjsrestent sans source : Ariane ne garde que des fenêtres de 28 j, pas de série quotidienne ni de requête brute. À trancher : verbe Ariane de requête brute, ougsc.mjsqui relit le relevé degsc-from-ariane.mjs.- Capture d'un élément plus haut que l'écran (Playwright,
locator.screenshot) : l'en-tête fixe et la barre « Être rappelé » d'Emet sont recollés au milieu de la capture. Parade : masquer les élémentsposition: fixedavant la capture (agent « loueur », 03/10). - L'IP d'un lead Genius Prep vient du premier
X-Forwarded-For, que le client peut écrire lui-même (Cloudflare devant, NPM enproxy_add_x_forwarded_for) : un robot choisit l'IP gravée et passe le contrôle Tor. Piste : lireCF-Connecting-IPd'abord. Non corrigé (change l'IP de tous les leads et le limiteur), à trancher (exec-lead.md, 03/10).
2026-10-04 — Passage à Ariane pour l'hébergement
- Le journal d'accès de l'ancien proxy s'arrête le 04/10 à 10:50 (proxy remplacé par celui d'Ariane, configuration interdite en lecture aux projets). Perdu jusqu'à nouvel accès : visites Google jour par jour, entonnoir par gabarit (
leads.mjs), contrôle des leads douteux par navigateur et référent, passages de Googlebot. Demande Ariane n° 157. - Interblocage évité : Ariane construit sous
/root/bin/heavy; nos tâches planifiées d'Emet tournaient sousheavyetdeploy.shaurait attendu un verrou tenu par son propre appelant. Corrigé le 04/10 :deploy.shappellevps.project_deploy, plus deheavydans la crontab ni dans_refresh-git.sh.
2026-10-06 — Le passage du proxy à Traefik (04/10) a rendu Genius Prep aveugle à l'IP des visiteurs
- Constat : depuis le 04/10 ~11:18,
contact_leads.ipporte une IP Cloudflare (172.68-71.x, 162.158.x) pour TOUTES les soumissions, y compris le test horaire qui partait avant de l'IP du serveur.isTorExit(ip)ne pouvait plus rien voir, et la limite de débit groupait des inconnus derrière un même nœud Cloudflare. - Cause : les routes
/api/contactet/api/tracklisaientx-forwarded-foren premier ; l'ancien proxy y mettait l'IP réelle, Traefik y met le nœud Cloudflare. - Correctif :
cf-connecting-iplu en premier (commit 4ffe552 de geniusprep). - Leçon : après un changement de proxy, relire une ligne fraîche de chaque table qui grave une IP ; un test synthétique dont l'IP change de famille du jour au lendemain est le signal.
2026-10-11 — ledger.mjs set --detail REMPLACE le détail, il n'ajoute pas
Ce qui a frotté. Pour verser une preuve neuve sur L022 et L121, j'ai lancé set <ID> --detail="…" :
le détail d'origine (le diagnostic complet de L121) a été écrasé. Rattrapé depuis git show HEAD:memory/ledger.json
avant commit. Solution proposée : une option --ajout="…" qui concatène au détail existant, et un avertissement
quand --detail remplace un texte de plus de 200 caractères.
2026-10-11 — Les journaux d'accès nginx-proxy-manager s'arrêtent le 04/10 ; un agent les a crus à jour
Ce qui a frotté. L'agent d'audit IA a compté les robots dans /srv/nginx-proxy-manager/data/logs/proxy-host-*
et conclu « aucune donnée depuis le 04/10 ». Faux : depuis le 04/10 11:18 les visites se lisent par
ARIANE_PROJECT=seo ariane vps.proxy_visits (4 mois, robots exclus par défaut, robots:true pour les garder,
limit ≤ 5000, since en ISO complet). leads.mjs le sait déjà ; la mémoire ne le disait pas à un agent neuf.
Règle : tout prompt d'agent qui touche aux visites cite vps.proxy_visits, pas les fichiers nginx.
2026-10-11 — Deux déploiements lancés en même temps : disque plein, les deux échouent
Ce qui a frotté. J'ai lancé deploy.sh d'Emet et vps.project_deploy de Genius Prep en parallèle (règle 19e du
serveur : jamais deux builds en même temps). Le disque est monté à 100 % (697 Go, no space left on device dans buildx),
les deux constructions ont échoué ; quelques minutes après, 57 Go étaient de nouveau libres. Les sites sont restés
sur leur version précédente. Règle : un seul déploiement à la fois, le second lancé après le .done du premier,
et df -h / avant (le disque n'est pas dans le contrôle vmstat/free de la règle 19a).
2026-10-12 — whatsapp.media : chat par lid, fichier posé dans le dossier courant
ariane whatsapp.mediaavec le fil33781753047@s.whatsapp.netrépond « ce message n'est pas dans ce que l'annexe a gardé » ; avec le lid renvoyé parwhatsapp.send(162749229334551@lid), ça marche.- Le fichier est écrit dans le dossier courant (
/root/apps/seo/image-*.jpeg) : le déplacer aussitôt dans le scratchpad. - Solution proposée : demander à Ariane d'accepter le numéro comme alias du lid (
ariane request).
2026-10-13 — gsc-from-ariane.mjs plante : Ariane coupe scripts/sql à 5000 lignes
[RÉPARÉ 13/10 : lecture par tranches de 4 000] Symptôme : snapshots GSC figés au 08/10 (system-check 🔴 6 j), le script meurt sur stderr « Coupé à 5000 lignes : une lecture plus large passe par une annexe ». Repro : node scripts/gsc-from-ariane.mjs all. Solution proposée : paginer la requête queryPages (LIMIT/OFFSET par tranche de 4000) ou passer par ariane seo.queries/pages. Les fenêtres 28 j (annex_seo.days) restent lisibles.
2026-10-13 — Agents « figés » 40 min : ils attendaient la file de construction partagée
Repro : 2 agents Emet lancent heavy npx astro build vers 16:55 ; à 17:35 la file compte 6 tâches (go test Ariane, fleet-image Ariane, jest dreamforma, nos 2 builds). Pendant ce temps le lot formation (outils/lot.sh, xargs -P 4 claude -p + Playwright) prend ~6 cœurs hors file, charge 37 sur 12 cœurs, 6,5 Go de swap. Tabby hors cause (les processus attendaient côté serveur). Mesures : règle 19 (f)(g) de /root/CLAUDE.md, skill seo §4 bis (agents sans construction complète).
- Même jour : le garde-fou des commandes longues (/root/.claude/hooks/bash-background.py) refusait toute commande contenant le mot de la file (un nom de fichier du verrou, ou un motif grep coupé sur « | ») : il refusait la lecture même de la file que la règle 19 (g) demande. Corrigé : le mot doit être la commande lancée avec un argument ; 6 cas testés (3 lectures passent, 3 constructions refusées).
- Même jour :
deploy.shd'Emet sort en exit 4 quand Ariane ne répond pas en 5 s pendant l'attente de la file (serveur chargé) ; le job continue chez Ariane, mais la purge Cloudflare ne se fait pas. Geste :ariane wait <job>puis purger à la main (bloc de fin de deploy.sh). Demande Ariane ouverte.
2026-10-13 — moedcal.com choisi le 05/10, jamais acheté, décision perdue [RÉSOLU 13/10 : acheté par Ichai chez Cloudflare, zone active, jeton du serveur lit et écrit le DNS]
Le nom du site Moed a été choisi par Ichai le 05/10 dans une session Ariane (411e6691) ; l'achat a bloqué (Cloudflare déconnecté dans Chrome sur son PC) et personne n'a relancé. Le 13/10 il redemande « trouver un nom de domaine » ; la session moed ne s'en souvenait pas, la trace n'existait que dans le transcript Ariane (grep -rl moedcal ~/.claude/projects). whois : toujours libre. Le jeton Cloudflare de /root/.secrets/cloudflare.env n'a pas le droit Registrar (erreur 10000) : l'achat passe par le tableau de bord. Règle : un achat bloqué s'inscrit au registre en attente (ledger.mjs), sinon il s'évapore.
2026-10-14 — Reprise d'un Workflow : agents relancés malgré le cache, Telegram envoyé deux fois
Repro : run wf_d467bd0d-a0c (moedcal) coupé pendant la fondation ; Workflow({scriptPath, resumeFromRunId}) sans changer le script a relancé contenu:villes, telegram-jalon et fondation avec des clés neuves (journal.jsonl : v2:77e9… puis v2:e01f… pour le même agent). Cause probable : l'ordre de départ des agents d'un pipeline() et d'agent() lancés sans await change d'un passage à l'autre, et la clé de cache dépend de cet ordre. Coût : Telegram reçu deux fois par Ichai. Solution : pour reprendre, écrire un script de suite qui embarque les résultats déjà rendus (lus dans journal.jsonl) et part de l'étape suivante ; et ne jamais mettre un envoi (Telegram, mail) dans un agent d'un workflow qu'on pourrait reprendre.
2026-10-14 — playwright screenshot (CLI Python) sans navigateur
/usr/local/bin/playwright est le paquet Python, sans navigateur installé : chaque capture échoue (« playwright install »). Les navigateurs de Node sont dans ~/.cache/ms-playwright : passer par require('/root/apps/emetexpertise/node_modules/playwright') (script de captures en série : scratchpad shots.cjs, node shots.cjs <dossier> <largeur> <hauteur> <url…>). Les pages de la sandbox sont privées (401) tant que vps.sandbox_share ne les rend pas publiques : une capture se fait alors en file://.
2026-10-14 — Le Workflow s'arrête quand la session se ferme (2 fois sur moedcal)
Les runs wf_d467bd0d-a0c (13/10 23:38) et wf_49648ac2-a1a (14/10 vers 09:50) se sont arrêtés avec la session, pendant la fondation, et rien ne l'a signalé. Ichai demande alors « t'as fini le site ? » et la réponse est non. Un Workflow vit dans la session Claude : la règle 7 (/root/bin/fond) ne le protège pas. Solution proposée : un run de plusieurs heures part par ralph-loop ou fond + claude -p, avec l'état sur disque. Sinon, on prévient Ichai que fermer la session arrête la construction.
2026-10-14 — Capture pleine page : la page paraît vide après le premier écran
Repro : page.screenshot({ fullPage: true }) sur moedcal.com (/fr/, 390 px) : au-delà d'environ 1 500 px, tout est blanc. La page n'est pas en cause : les blocs apparaissent au défilement par une animation CSS liée au défilement (animation-timeline: view(), .reveal dans src/styles/moed.css), et une capture pleine page ne fait pas défiler. Un visiteur, Google et un navigateur sans JavaScript voient tout. Solution : capturer écran par écran en faisant défiler (scratchpad/revue/steps.cjs <dossier> <largeur> <chemins…>, MAX=n pour limiter), puis recoller en planches. Candidat script du dépôt si on recapture un site à animations.
2026-10-14 — Umami ne compte pas une visite Playwright sans rien dire
- Constat : visite d'Emet en Chromium sans tête :
POST /m/api/sendrépond 200, mais Umami affiche 0 page vue. Umami écarte comme robot l'User-AgentHeadlessChrome, et il répond 200 quand même. - Reproduire :
node umami-live.cjs https://emetexpertise.fr/, puisGET /api/websites/<id>/statsdonne 0. - Solution : pour éprouver la mesure, donner au contexte un User-Agent de navigateur ordinaire (iPhone Safari). La visite compte alors (1 page vue relue par l'API).
2026-10-14 — Deux constructions par site au lieu d'une (Umami sur Emet et Genius Prep)
- Constat : la relecture adverse a été lancée APRÈS la première mise en ligne. Elle a trouvé des phrases fausses dans les deux pages de confidentialité, et il a fallu reconstruire les deux sites une seconde fois. Il a fallu environ 45 min, dont la moitié aurait été évitée. Ichai a demandé pourquoi c'était si long.
- Solution : sur un site client, la relecture (textes et code) passe AVANT la première construction. On ne fait qu'une construction par site, avec le lot complet.
2026-10-14 — Script en ligne qui modifie un nœud rendu par React (moedcal) : erreur #418, bouton perdu
- Constat : le bouton « ne pas être mesuré » de moedcal était rendu par React (
<p hidden>+ bouton), puis un script en ligne changeait son texte avant l'hydratation. Pour un visiteur déjà opposé, React lève l'erreur #418 (le HTML ne correspond pas), refait le rendu, et le bloc redevient caché : impossible de revenir en arrière. Un visiteur neuf ne voit rien d'anormal. - Reproduire : Playwright avec
localStorage.setItem('umami.disabled','1')en init script, ouvrir/fr/confidentialite/, écouterpageerror. - Solution : sur un site sans composant client (moedcal), tout bloc qu'un script en ligne modifie passe en entier par
dangerouslySetInnerHTML. React ne réconcilie pas ce contenu. Toujours tester les deux états (neuf et déjà opposé).
2026-10-14 — Rapport client Emet mois 3 : trois frictions trouvées en le produisant
- Période contractuelle exacte sans clé Google : Ariane ne garde que des fenêtres de 28 j (
annex_seo.days, fenêtre [J-28, J] dont les derniers jours sont incomplets), aucune ne tombe sur 14/09 → 13/10. Solution utilisée : Search Console lue dans le Chrome caché d'Ariane (ariane pc.page/pc.browse, compte ichaiwizlol, lecture seule, écran d'Ichai libre) avecstart_date=AAAAMMJJ&end_date=AAAAMMJJ&breakdown=date|query|page|devicedans l'URL, 500 lignes par page (listbox « Nombre de lignes par page »). Totaux = somme de l'onglet JOURS (l'en-tête arrondit à « 1,71 k »). Pièges : une ligne survolée sort sur plusieurs lignes (« \n\t ») et les pages portent « Copier l'URL / Ouvrir… / Inspecter l'URL » ; l'interface plafonne à 1 000 lignes. Scripts jetables dans le scratchpad de la session (gscui/browse2.mjs,parse.py,build.py). Candidat script :gsc-periode.mjs <site> <début> <fin>(2e mois de suite qu'on refait cette lecture à la main). - L'archive du mois 2 était incomplète :
reports/gsc-periode-mois2-2026-08-14_2026-09-13.json(51 clics) n'a pas le 13/09, pas encore publié le 15/09. Relu le 14/10 : 68 clics, 3 887 impressions, position 10,9 (archive…-relu-2026-10-14.json). Règle : une période qui finit la veille ou l'avant-veille est incomplète ; vérifier que la série JOURS contient bien le dernier jour avant d'archiver. report.mjsaurait imprimé les déclarations<!-- chiffres … -->des fichiers curés dans le PDF (curatedLines lisait chaque ligne). Corrigé : les commentaires HTML sont retirés avant lecture. Corrigé aussi : libellés lisibles des pages d'observatoire dans « Top pages » (« Majoration · la grande motte » au lieu de « /observatoire/majoration-resi… » sur dix lignes) et dernière section autorisée à passer d'une page à l'autre.- L'article du 12/10 n'est pas paru :
/var/log/emet-publier-articles.log→ « ⛔ Dépôt pas propre : rien n'est publié » (M .claude/skills/ariane/SKILL.md,?? ariane.json,?? drafts/). Le dépôt est toujours sale le 14/10 : la sortie du 19/10 échouera aussi. Non corrigé (consigne : ne pas toucher au dépôt du site). Solution proposée : commiter ou ignorer ces trois chemins dans/root/apps/emetexpertise, puis publierclassement-meuble-de-tourisme-fiscaliteà la main.
2026-10-14 — Les tâches automatiques d'Emet bloquées en silence pendant 10 jours (dépôt pas propre)
- Constat : du 05/10 au 12/10,
cessions-refresh,reventes-refresh,cfe-refreshetpublier-articles-dus(article du 12/10) ont refusé de tourner. Le dépôt contenait trois fichiers non commités :.claude/skills/ariane/SKILL.md, réécrit par Ariane ;ariane.json, la déclaration du 04/10, jamais commitée ;drafts/tarifs.astro, notre brouillon du 11/10. La garde de_refresh-git.shest juste, mais personne n'a vu le blocage. On l'a trouvé en faisant le rapport du mois. - Reproduire :
git -C /root/apps/emetexpertise status --porcelainnon vide, puisgrep ⛔ /var/log/emet-*-refresh.log. - Solution : commit des trois fichiers (14/10), puis rattrapage des quatre tâches. Ariane réécrit la skill dans chaque projet, donc ça reviendra. À faire : la garde doit ignorer les fichiers gérés par Ariane (
.claude/skills/ariane/,ariane.json). Et après tout travail dans un dépôt de site,git statusdoit être vide avant de rendre la main.
2026-10-15 — /seo : trois frictions trouvées pendant la collecte
- Un robot imite le référent Google sur Genius Prep : le journal d'Ariane (
vps.proxy_visits,referrer:"google") comptait 239 visites le 10/10 et 755 le 14/10, quand Search Console donne 40 à 60 clics par jour. 180 des 184 visites de/tools/psat-vs-school-satportent un?school=<code>différent, une par visite, à 1-3 par minute ; 137 sur un échantillon de 300 portentAndroid 10; KChrome 132/133 ou Mac Chrome 153, un slug /data différent par visite. Le filtre de robots d'Ariane ne les voit pas,ROBOT_UAnon plus. Reproduire :ariane vps.proxy_visits '{"host":"geniusprep.com","since":"2026-10-14T00:00:00+03:00","referrer":"google","statuses":[200,304],"limit":300}'. Règle : ces volumes ne se citent jamais comme des visites ; la tendance se lit dans Search Console. À faire : une règle de rafale (un même UA, un chemin différent par visite, cadence régulière) dansleads.mjs, puis une demande à Ariane pour son filtre. - Les journaux de l'ancien proxy ont été supprimés le 11/10 par une session d'Ariane (
rm -rfretrouvé dans/root/.claude/projects/-root-ariane/a15ab44f….jsonl).leads.mjsdéclarait alors toutes les arrivées « NON MESURÉES ». Corrigé : l'absence du fichier est une période sans journal, pas une panne ; les arrivées se comptent depuis le 04/10 11:18 et l'assiette le dit. Skill/seoréécrit. - Codex : « failed to activate captured window » une 2e fois (ThreeBestRated, 15/10 12:08 et 12:10,
pc.openpuis reprise : même échec). Ajouté à la demande Ariane 550. Le formulaire exige de toute façon un captcha, trois photos et un avis rédigé : il ne se remplit pas depuis le serveur. - Construction de Genius Prep refusée par le garde-fou du sitemap (15/10) : j'avais passé
DATES_CHECKEDde/shsat-test-datesau 15/10 sans touchersrc/lib/pageDates.ts;assertPageDatesMatchSourcesfait échouer le rendu de/sitemap.xmlau build. Le contrôle des types (tsc --noEmit) ne le voit pas, seule la construction le voit : la mise en ligne par Ariane a échoué, et Ariane ne garde que la fin du journal (sans l'erreur). Reproduire : construire le site sous /root/bin/heavy en tâche de fond → « DATES_CHECKED a changé sans src/lib/pageDates.ts ». Règle : toute page dont le contenu change monte sa ligne danspageDates.tsle même jour ; les pages dePRIVATE_DATES_CHECKED(/psat-prep, /shsat-test-dates, /isee-test-dates) changent les deux ensemble. À écrire dans le brief de tout agent qui modifie une page de Genius Prep.
2026-10-16 — verify.mjs ne peut pas re-dériver un chiffre GSC
node scripts/gsc.mjs emetexpertise --json rend [] (ENOENT sur /root/.secrets/emet-gsc-service-account.json : les clés vivent dans Ariane depuis le 26/09). gsc-from-ariane.mjs écrit un snapshot mais n'imprime rien. Un chiffre de clics dans un message sortant ne peut donc être déclaré qu'en fixe : (snapshot du jour), pas re-dérivé.
Solution proposée : une option --json à gsc-from-ariane.mjs (imprimer le snapshot écrit), ou accepter snapshot <key> :: expr comme source dans verify.mjs.
2026-10-16 — arrêter une tâche fond laisse ses enfants tourner
systemctl stop fond-veille-ruben a arrêté l'unité mais laissé les deux ariane watch --stream et les boucles bash (fond est en KillMode=process), puis le relancement a échoué (« unit already loaded »). Il a fallu tuer les pid à la main, vérifier qu'il n'en restait aucun, puis relancer.
Solution proposée : un fond stop <nom> qui tue l'arbre de processus de la tâche et décharge l'unité.
2026-10-18 — un agent d'équipe qui attend sa propre tâche de fond s'endort
Pendant le chantier « moins de tokens », l'agent C a lancé ses essais en fond puis s'est mis en attente : il n'a pas été réveillé à leur fin, et un de ses essais (attendre.sh url … --max=abc) tournait sans fin. Il a fallu tuer l'essai et le relancer à la main par message, deux fois ; même chose pour l'agent B, qui attend la fin de fond sources-remplir5.
Reproduire : un agent nommé (Agent avec name) qui lance run_in_background puis rend la main en « j'attends ».
Règle : la session principale tient elle-même la veille (until [ -f /var/log/fond/<nom>.done ] en fond) et relance l'agent à la fin ; dans le brief d'un agent, « ne rends pas la main en attendant : attends en premier plan borné (timeout) ou finis sans l'attente ».
2026-10-18 — Légifrance et economie.gouv refusent ce serveur
Écran Cloudflare « Un instant… » (HTTP 403) en curl, Chromium headless, Chrome sur écran virtuel et ariane extract.read. scripts/sources.mjs se replie sur web.archive.org (copies parfois de 2024, marquées « archive » et rendues avec le code 3 si > 30 j). Pour un texte de loi à jour, WebFetch reste nécessaire.
Solution proposée : l'API officielle Légifrance (PISTE), qui demande un compte créé par Ichai.
2026-10-18 — Mail Ruben du 16/10 : mauvais niveau d'accès Search Console
Le mail de proposition demandait l'autorisation « Restreint » ; la page d'aide Google (answer/7687615) dit que « accès limité » ne permet ni de demander l'indexation ni d'envoyer un sitemap. Il faut « accès complet ». Corrigé dans le message des étapes (sites/_prompts/AB-ruben-etapes-acces-18-10.md). Solution : toute procédure d'accès envoyée à un client se vérifie sur la page d'aide officielle avant rédaction ; runbook à écrire (runbooks/acces-client.md) à partir de ce message.
2026-10-18 — Next dev refuse un node_modules en lien symbolique dans un worktree
npx next dev dans /root/apps/_wt/geniusprep-* avec node_modules -> /root/apps/geniusprep/node_modules : Turbopack panique (« Symlink node_modules is invalid, it points out of the filesystem root ») ; --webpack casse sur node:fs/promises (src/lib/torExits.ts). Solution : mkdir node_modules && mount --bind /root/apps/geniusprep/node_modules node_modules, puis umount + rmdir à la fin. ESLint du dépôt Genius Prep plante aussi (config circulaire) même dans le dépôt principal.
2026-10-19 — capture.mjs : une capture pleine page très haute sort vide passé ~6 000 px
- Repro :
node scripts/capture.mjs https://geniusprep.com/data/nyc-high-school-regents-pass-rates --mobile→ image 780×118 790, le tableau des écoles est blanc (avant comme après L632). Une capture fenêtre après défilement montre le tableau rempli. - Conséquence : « identique au pixel près » entre deux captures pleine page ne prouve rien sur le bas d'une page longue (gp-regents l'a affirmé de bonne foi).
- Solution proposée : capture.mjs coupe en tuiles de fenêtre au-delà de ~10 000 px (ou option
--vers=<sélecteur>qui défile jusqu'à un élément), et le dit dans sa sortie.
2026-10-19 — ariane seo annonce « Not open to working sessions today » alors que les verbes répondent
- Repro :
ARIANE_PROJECT=seo ariane seoaffiche la phrase en tête ;ariane seo.cannibal '{"site":"geniusprep"}'répond ensuite normalement (rc 0, 30 lignes). - Conséquence : un agent qui s'arrête à la page d'aide déclare l'accès fermé (même famille que L502).
- Solution proposée : toujours un appel témoin avant de conclure ; signaler à Ariane que l'en-tête ne suit pas l'accès réel.
- Au passage :
seo.cannibalne rend que les 30 premières requêtes en concurrence ; une mesure de cannibalisation entre deux pages se fait surqueryPagesdu relevésites/<site>/gsc/<date>.json.
2026-10-19 — impots.gouv a retiré ses pages thématiques « professionnel/ »
- Repro :
/professionnel/cotisation-fonciere-des-entreprises-cfe,/professionnel/cfe,/professionnel/contribution-economique-territoriale-cetrendent 404 ; seules les pages/professionnel/questions/<question>répondent (liées depuis/professionnel). - Solution : chercher la page de remplacement dans les liens de
https://www.impots.gouv.fr/professionnel, ou citer la fiche service-public (entreprendre.service-public.gouv.fr/vosdroits/F23547pour la CFE). Un grep des autres liens impots.gouv des deux sites reste à faire.
2026-10-19 — Les « ouvertures » Brevo du kit des observatoires sont des robots, et le registre les comptait comme un signal
- Repro :
envois.jsonl, champpreuve.evenements: les 6 mails « ouverts » (C01, C04, C07, T15, D04, R01) le sont 4 à 24 s aprèsdelivered. Aucun lecteur n'ouvre un mail aussi vite : ce sont les analyseurs de liens des messageries. - Conséquence : L496 écrivait « 20 mails délivrés, 9 ouverts » comme une mesure d'intérêt ; en réalité aucune lecture humaine n'est prouvée, et 20 des 26 mails avaient leur réponse chez Nathan.
- Solution proposée :
envoyer.mjs(ou le relevé qui lit Brevo) ne compte une ouverture que si elle arrive plus de 60 s après la remise, et l'affiche à part ; un envoi n'a de résultat que sur une réponse lue ou une mention trouvée.
2026-10-19 — Consigne contradictoire : la mémoire du 15/10 interdit de proposer des messages à valider, la mission du jour en demande 8
Repro : mémoire
dire-ce-que-je-fais.md(« n'envoyer rien à personne, ne pas lui proposer de messages à valider ») contre la mission de la session principale du 19/10 (« pour les 8 premières, le message prêt »).Fait : 8 brouillons écrits et rattachés au registre (L665-L672), rien envoyé ; la partie « sans message » du plan est mise en avant.
Solution proposée : qu'Ichai tranche une fois si les messages de mentions (hors client) sont une exception à la règle du 15/10, et l'écrire dans la mémoire.
2026-10-21 — Plus aucune tendance 7 j vs 7 j depuis le 22/09 : les snapshots tirés d'Ariane (
gsc-from-ariane.mjs) n'ont pas detimeseries,gsc-diff.mjsimprime « indisponible » puis « ✅ pas de régression » (faux vert). Le même jour,ariane vps.proxy_visitsexpire (> 60 s) sur emetexpertise.fr, donc l'entonnoir d'Emet ne compte plus les arrivées Google. Reproduire :node scripts/gsc-diff.mjs emetexpertise;timeout 60 ariane vps.proxy_visits '{"host":"emetexpertise.fr","since":"2026-10-18T00:00:00Z","referrer":"google","group":"day"}'. Repli utilisé : comparer les totaux 28 j de snapshots successifs (Emet 1 616 clics au 16/10 → 1 387 au 21/10). Fait : demandes Ariane 697 (proxy_visits) et 698 (série quotidienne). À faire :gsc-diffdoit échouer en rouge, pas afficher « pas de régression », quand la série manque.2026-10-21 —
sources.mjs lirerefuse www.schools.nyc.gov (« domaine hors périmètre ») : l'agent GP-B a dû lire la page SHSAT du DOE en curl, sans copie datée. Reproduire :node scripts/sources.mjs lire https://www.schools.nyc.gov/enrollment/enroll-grade-by-grade/high-school/specialized-high-schools. Solution : ajouter les domaines officiels de Genius Prep (schools.nyc.gov, nysed.gov, infohub.nyced.org) au périmètre, avec un dossiersites/geniusprep/sources/.2026-10-21 —
sources.mjsgardait une page « site en maintenance » d'impots.gouv comme copie valable (HTTP 200, même page sur toutes les URL ce jour-là). Reproduire :index.jsond'Emet, entrée « quelle-date-vais-je-recevoir-mon-avis… taxe d'habitation », titre « Impôts.gouv.fr – site en maintenance ». Corrigé le jour même :estMaintenance(titre contenant « maintenance ») fait passer à web.archive.org, en direct comme dans les copies d'archive ; la copie a été refaite parsources.mjs verifier(archive du 16/09). Reste : une copie déjà en cache du jour est rendue telle quelle parlire; il fautverifierpour la remplacer.2026-10-21 — Un fichier neuf de
public/créé par un agent en droits 600 est servi en 404 par nginx (le processus de nginx ne peut pas le lire ; Emet transforme le 403 en 404). Cas :public/emet-expertise.vcfd'EM-A, en ligne en 404 après la construction ; les images webp d'EM-B (644) passaient. Reproduire :docker exec emetexpertise ls -la /usr/share/nginx/html/*.vcf. Corrigé : chmod 644 dans le dépôt et dans le conteneur, purge Cloudflare de l'URL (le 404 était en cache). Règle : avant de construire,find public -type f ! -perm -o=rdans le dépôt doit être vide.
2026-10-22 — Remplacement par sous-chaîne : la ligne à 4 espaces a matché celle à 6
- Ajout d'une ligne au pied de page de
nav.ts: l'ancre «{ label: 'Vérification…' }» (4 espaces) est une sous-chaîne de la ligne du menu (6 espaces). Résultat : doublon dans le menu, rien dans le pied de page ; vu par l'agent de vérification avant déploiement. - Solution : ancrer avec le saut de ligne qui précède (
"\n { label…") et vérifiercount == 1avant de remplacer.
2026-10-22 — Gabarit de brief envoyé avec {ANGLE} et {SLUG} non remplis
- Le premier des 8 agents du panel est parti sans angle ; rattrapé par SendMessage.
- Solution : écrire chaque brief en entier, jamais un gabarit à trous dans un appel Agent.
2026-10-22 — Votes des sondages WhatsApp invisibles
- Six sondages envoyés au groupe Emet ; Ichai dit qu'ils ont tous été votés ; Ariane n'a aucun fait
whatsapp.poll_vote(0 en base, jamais) et le code écritoptions: [](le choix n'est pas déchiffré).whatsapp.threadne montre pas les votes. - Conséquence : j'ai annoncé « aucun vote » sur la foi d'une veille qui ne peut pas voir un vote.
- Solution : demande faite à Ariane (
ariane request). En attendant, ne plus poser une question par sondage au client : questions en texte numéroté (réponse lisible dans le fil), ou capture du sondage.
INDEXMémoire — INDEX (à lire en premier)
Mémoire — INDEX (à lire en premier)
Source de vérité du projet SEO. Une ligne par entrée ; le détail est dans index-detail.md, mot pour mot.
À lire seulement pour l'entrée qui concerne la tâche (grep -n "<date>" memory/index-detail.md).
Fichiers
- 🧾 backlog.md — le registre : toute idée / actif / reco, statut et condition de réveil (généré par
scripts/ledger.mjs, sourceledger.json) - ⏳ waiting-on.md — qui doit quoi, depuis combien de jours ; au-delà de 7 jours = relance (généré)
- decisions.md — décisions actuellement vraies + rationale
- decisions-historique.md — 📕 archive, contient des affirmations réfutées : jamais y sourcer un fait
- credentials.md — carte des accès Google (chemins, méthodes par site, sans secrets)
- sites.md — état SEO par site (stack, GSC, GA4, findings récurrents)
- clients/ — profil par client ; un agent reçoit
sites/<site>/depart.mdà la place, la fiche s'ouvre pour un besoin précis - frictions.md — frictions rencontrées + solutions
- repetitive.md — manips répétitives → candidats skills/scripts
- references.md — où trouver quoi ; infos sur Ichai → app
/root/apps/chat/(lecture seule) - objectives.md — cap du projet
- ../runbooks/ — procédures réutilisables
- 🔭 ../veille/ — veille SEO/GEO : comment Google et les IA classent et citent (2026-10-18 :
SYNTHESE.md, 11 thèmes sourcés,VERIF.md) — à relire avant un /propose - ../.claude/skills/seo/references/ — l'histoire des règles du /seo (incidents datés), un fichier par thème
États successifs (détail : index-detail.md, section du même titre)
- 2026-09-22 — le trafic d'Emet a été multiplié par 25, et 94,7 % ne peut pas devenir client
- 2026-09-17 — DataForSEO rechargé, et deux constats qui changent la stratégie des deux sites
- 2026-09-16 (soir) — /seo puis « fais tout » : 9 agents, deux sites redéployés
- 2026-09-15 (soir) — « fais tout » exécuté sur les deux sites
- 2026-09-15 — /seo : le carnet, et deux fausses alertes désamorcées
- 2026-09-07 — le carnet exécuté, et deux capteurs qui manquaient
- 2026-09-07 (fin de journée) — deux sites refaits, et trois fois où ma vérification a menti
- 2026-09-07 (vague 2) — le parcours de réservation réparé, et 601 URLs ouvertes
- 2026-09-03 — nos capteurs mentaient, et le mécanisme qui marche sert une fois sur cinq
- 2026-08-25 (soir) — le skill /seo s'auto-répare, et le diagnostic Emet est clos
- 2026-08-25 — le système sait désormais PROPOSER
- 2026-06-26
sitesÉtat SEO par site
État SEO par site
⚠️ PÉRIMÉ, corrigé le 2026-09-02. Ce bandeau imposait « lecture seule / coordination, jamais de déploiement ». C'est faux depuis le 2026-06-26 : Ichai a écrit dans
CLAUDE.mdque ces sites sont gérés exclusivement depuis ce command center et que « plus aucun autre Claude n'y travaille ». On agit et on déploie directement.scripts/claude-sessions.shreste utile pour distinguer un état transitoire (un 502 pendant un déploiement) d'un incident — ce n'est plus un verrou de coexistence.
Genius Prep — https://geniusprep.com (Next.js + PocketBase, ~3 mois)
- 🔌 GSC en direct depuis le 2026-08-25 (méthode
oauth,/root/.secrets/geniusprep-gsc-oauth.env) — wizytics abandonné et arrêté. Données du 25/08 : 159 clics · 19 583 impressions · pos 15,2 (28 j). C'est, de loin, le site à volume réel du portefeuille. Pause client LEVÉE le 2026-08-25 (le client a payé) → travail actif repris,monitor:true, audit de reprise 91/100 (0 finding sérieux), tendance 7j : clics +33 % (33→44). - 🎯 Cohorte de mots-clés créée le 2026-08-25 (
sites/geniusprep/keywords.json, baseline du jour) : 4/10 en top 10. - Audit vérifié 2026-06-25 (2 agents) → technique 90/100, schema complet (Course/FAQ/Breadcrumb/Service/Org/Article tous présents), maillage interne fort, robots/sitemap OK (51 URLs). Détails + backlog priorisé :
sites/geniusprep/reports/2026-06-25-gaps.md. - Vrais gaps : cannibalisation ISEE/SSAT (6 doublons), E-E-A-T (aucun auteur nommé), money pages absentes du header, money pages manquantes (Regents, SSAT dédiée, G&T, AP, college counseling), 15 pages écoles sans og:image, titles/metas longs.
- Déploiement : manuel,
docker compose up -d --build. Blog = ISR depuis PocketBase (articles publiés sans rebuild). 3 dossiers existent — canonique = geniusprep.com (genius-prep/geniusprep-dashboard= wizycode.fr).
Emet Expertise — https://emetexpertise.fr (Astro statique, neuf ~10j)
- Audit vérifié 2026-06-25 (2 agents) → on-page excellent (100/100 après fix moteur). JSON-LD AccountingService top, CWV AVIF/WebP, NAP strict, E-E-A-T solide. Détails :
sites/emetexpertise/reports/2026-06-25-gaps.md. - ❌ Corrections de mes affirmations initiales : robots.txt = 200 (pas 404), sitemap via
sitemap-index.xml(20 URLs) — mon premier audit a tapé/sitemap.xmlen dur (faux positif, corrigé dans audit.mjs). Meta description « 9c » = résolue (commitddbd331). - Vrais gaps : hors-site (Google Business Profile + avis absents = plus gros levier), GPS
[A CONFIRMER], couverture de pages (⛔ PAS le BTP — corrigé le 2026-09-02 : cette ligne le donnait comme « opportunité locale n°1 » alors que Nathan a explicitement exigé de retirer toute mention BTP et de rester généraliste ; c'est une contre-indication client, pas une opportunité. Paie & création sans page, pas de pages villes — et les pages villes sont à éviter avant le déménagement), blog non surfacé sur la home. - Déploiement : build dans l'image Docker,
docker compose up -d --build. WIP par un autre Claude : migration contact → Brevo/mailer.
2026-07-21 — Emet : résilience Cloudflare (survit à une panne serveur)
Contexte : Ichai a demandé si ses sites survivent à une panne du VPS. Vérifié : NON par défaut — Cloudflare
est un proxy DEVANT le serveur (cf-cache-status: DYNAMIC sur le HTML), pas un hébergeur. Tout est sur UN seul
VPS (82.165.41.252, 79 conteneurs). Données sauvegardées (restic local + Backblaze B2 off-site, backup nocturne 3h),
mais disponibilité non couverte.
Fait pour emet (site STATIQUE) — via le token dans /root/.secrets/cloudflare.env :
- Always Online = ON (zones emet + geniusprep) : sert une copie archivée si l'origine tombe.
- Cache Rule "Cache HTML statique" (zone emet
509ca2441ae2434279e8a4bc3f9313fb) : cache le HTML 4 h à l'edge, exclut/api/(mailer). Testé : HTML → HIT,/api/contact→ reste DYNAMIC (formulaire intact). ⇒ emet reste consultable même VPS éteint (edge cache + serve-stale + Always Online).
⚠️ NOUVELLE RÈGLE DE DÉPLOIEMENT EMET (obligatoire) : le HTML étant caché 4 h, déployer sans purger sert
l'ancienne version. TOUJOURS déployer via bash /root/apps/emetexpertise/deploy.sh (build + up + purge Cloudflare).
Ne plus faire docker compose up -d app seul.
PAS fait pour geniusprep (Next.js DYNAMIQUE) : Always Online oui, mais PAS de cache HTML (casserait les pages dynamiques). Idem pour toutes les apps dynamiques (lync, dashboard, PocketBase).
Non résolu : les apps DYNAMIQUES et les sites en direct-VPS (vpsdashboard.space, lync, cartable, wizycode.fr — IP 82.165.41.252 exposée) ne survivent PAS à une panne serveur. Seule vraie parade = 2e serveur (coût récurrent). Le backup couvre la perte de données, pas l'uptime. RTO estimé pour tout remonter : ~1/2 à 1 journée (69 Go d'apps).
2026-08-25 — Genius Prep : faits durables découverts par le panel /seo
- ⚠️ Les articles sont servis depuis PocketBase (
https://pb.geniusprep.com, collectionblog_posts), pas depuis le repo. Les 3.mdxde/root/apps/geniusprep/content/blog/sont morts et trompeurs. Toute édition d'article passe par PB. Le champcontentn'est pas exposé par l'API publique → mesurer sur le HTML live. - Mesure de conversion maison, elle FONCTIONNE :
ConversionBeacon.tsx→/api/track→ PBconversion_events(131 événements au 25/08, typesbook_cta/pricing/phone/email, sans IP ni visitor ID). C'est supérieur à GA4 ici (ni consentement, ni adblock) → ne pas installer GA4. Il manque seulementreferrer+landing_pathsur le lead pour attribuer au SEO.ga4.status: not_installedest exact mais trompeur. - Zombies wizytics à tuer :
CookieConsent.tsx:11(injectet.jsd'un service éteint après consentement),Contact.tsx:40-50,route.ts:150. Le bandeau cookie n'existe QUE pour justifier wizytics → il part avec. - Le formulaire n'existe que sur la home (
Contactimporté seulement danssrc/app/page.tsx:7).RÉFUTÉ le 2026-09-02 : depuis le commitcurl /shsat-prep | grep -c "<form"→ 07bbf33edu 31/08 le formulaire est présent sur les 6 pages money et sur/pricing,/about,/faq, à 12-22 % de la profondeur de page (vérifié en live). Or 62 % des clics arrivent sur des articles de blog. IdemTestimonials.tsx: monté seulement sur la home → aucune preuve sociale sur les pages qui captent les clics. - Piège d'architecture :
api/contact/route.ts:113met Resend en point de défaillance unique —return 502avantpersistLead()etnotifySignal(). Un e-mail qui échoue = un lead perdu en silence. - Dé-cannibalisation existante :
NOINDEX_SLUGSdanssrc/lib/blog.ts(13 slugs) + redirectsnext.config.ts. Piège documenté : garder le slug listé pour quesitemap.tsle filtre. - Zone Cloudflare :
geniusprep.com=df62b71dcb1c52ed9ad302ec8a5f272f, notre token la voit (accès vérifié). Jamais collecté sur ce site :— RÉFUTÉ le 2026-09-02, les deux répertoires existent (sites/geniusprep/n'a nibacklinks/nigbp/ls sites/geniusprep/). L'état hors-site est désormais documenté : admin de la fiche depuis le 26/08, 9 avis, UENI toujours connecté.
2026-08-25 — Emet Expertise : faits durables découverts par le panel /seo
- Le sitemap est
/sitemap-index.xml→/sitemap-0.xml(28 URLs)./sitemap.xmlrenvoie 404 — ne pas le curler par réflexe, on croit à tort que le blog est absent./avismanque au sitemap (pos 2,9). - Google retient
/contactsur « expert comptable sarcelles », pas la page money : le poids interne décide, pas le title (la dé-optimisation des titles du 19/07 n'a rien changé)./contact= 5 liens + nav sitewide ;/expert-comptable-sarcelles= 2 liens, absente de la nav (src/data/nav.ts:44-45, footer « ressources »). - La traîne « trimestre » (25 impr, pos 3,8, 0 clic) vient d'une seule phrase de
/services/fiscalite(« Acomptes trimestriels d'impôt sur les sociétés… »). La supprimer appauvrit la page pour un gain nul → ne pas toucher. - La DGFiP a abandonné « PDP » pour « plateforme agréée (PA) », et ne publie pas de table en ligne : deux fichiers ODS/XLSX à télécharger. D'où des chiffres publics qui divergent le même mois (146 / 147 / 137 / 29).
- BODACC ne porte pas le code NAF (champs réels :
familleavis,ville,numerodepartement, SIREN) → toute maille sectorielle exige une jointure SIREN → NAF via Sirene. C'est ça qui coûte, pas la mise en page. - Cache ISR de 5 min sur le blog (
export const revalidate = 300danssrc/app/blog/[slug]/page.tsx) : une modification d'article dans PocketBase n'apparaît en live qu'après ~5 minutes. Ne pas conclure « la modification n'a pas pris » avant d'avoir attendu, et ne pas re-patcher dans l'intervalle. - Le rendu markdown des articles décale les niveaux :
##sort en<h3>,###en<h4>. Le<h1>est le champtitlede PocketBase. Ne pas remettre un##répétant le titre en tête de corps : ça fait doublon. - schools.nyc.gov est une SPA : WebFetch renvoie la navigation, pas le contenu. Passer par
curl -A "Mozilla/5.0"puis extraire le texte — c'est comme ça qu'on a récupéré la citation verbatim du DOE sur le passage au test computer-adaptive (fall 2026).
2026-08-27 — Réponses aux avis Google : ce qui est vrai et ce qui ne l'est pas
Vérifié sur la doc officielle (support.google.com/business/answer/3474050) après une affirmation fausse
de ma part (« publier une par une ») :
- Aucune limite ni filtre anti-rafale sur les RÉPONSES du gérant. La cadence « 1 par semaine » vaut pour les demandes d'avis aux clients, jamais pour les réponses. Ne pas confondre les deux.
- Chaque réponse passe en modération de contenu : « Replies usually take up to 10 minutes to review, but sometimes a review can take up to 30 days ». Une réponse refusée doit être éditée. ⇒ toujours revenir vérifier qu'elles sont apparues, ne jamais supposer qu'une réponse publiée est une réponse visible.
- Un utilisateur GESTIONNAIRE suffit pour répondre (pas besoin d'être propriétaire), et la réponse s'affiche comme venant de l'établissement : « your personal name won't be shown ».
2026-08-27 — Images générées : le modèle invente des marques de CONCURRENTS sur les murs
Constaté sur le premier lot GPT Image 2 pour Genius Prep, et c'est un piège sérieux sur un site client. Le modèle remplit spontanément les murs de posters et de signalétique de marques réelles :
- le hero du site portait « Flatiron Tutoring » peint sur le mur — un autre nom d'entreprise ;
- la page SHSAT affichait un poster « Success On the SHSAT » signé « A-List Education » ;
- la page SAT affichait « Target Test Prep ».
A-List Education et Target Test Prep sont de vrais concurrents new-yorkais. On était à un déploiement près de mettre la marque de deux concurrents sur les pages money d'un client.
Règle : tout prompt d'image destiné à un site client porte une interdiction explicite de texte de marque — no company names, no logos, no brand marks, no posters or signs with headlines, no lettering on walls, no titles on book spines. On autorise en revanche l'écriture manuscrite, les calculs et les équations à l'écran : ils ajoutent du réalisme et ne portent aucune marque.
Et surtout : on OUVRE chaque image générée avant de la livrer. Un lot « 8/8 OK » ne veut rien dire, le script ne juge que le téléchargement. Ces trois défauts étaient invisibles dans la sortie console.
referencesRéférences externes (où trouver quoi)
Références externes (où trouver quoi)
Infos sur l'utilisateur (Ichai) → app chat
Pour TOUT ce qui concerne Ichai (qui il est, objectifs, contexte perso/business, finances, façon de
fonctionner), aller voir /root/apps/chat/ — son CRM/assistant perso, géré par un autre Claude.
⚠️ Lecture seule : ne JAMAIS écrire dans /root/apps/chat/ (coexistence, un Claude y tourne souvent).
Fichiers utiles :
profile.md— qui est Ichai (contexte, objectifs)psychologie.md— comment il fonctionne / le coacherjournal.md— historiqueleads.md— pipeline commercial (dont les clients SEO : Avi Cohen / Emet, Ruben Toledano)finances.md— situation & objectifs de CAplaybook.md— sa méthode de venteCLAUDE.md— instructions de ce projet
Règle : le commercial vit là-bas ; ici (/root/apps/seo) on gère la delivery SEO. On référence, on ne duplique pas.
⚠️ Stack GSC/SEO PRÉEXISTANTE sur geniusprep (NE PAS dupliquer)
Vérifié 2026-06-26 — plusieurs systèmes GSC tournent déjà (crons matin) :
6h/root/apps/geniusprep/scripts/seo-snapshot.sh(snapshot CSVdocs/seo-log.csv)8h/root/scripts/gsc-daily-briefing.sh(briefing — fait DÉJÀ le 7j-vs-7j correct + striking-distance)9h/root/scripts/gsc-monitor.sh(health) · +/root/scripts/gsc-indexing-monitor.sh7hmonitor.sh= le mien, ajouté le 25/06 → DOUBLON à dédupliquer (faire skipper geniusprep, ne garder que l'agrégation cross-site/emet, ou adoptergsc-daily-briefing.sh). Leçon : pour trouver ces crons, grepgsc|seo|brief|snapshot(pas seulementseo).
Rapports d'analyse (workflow 18 agents, 2026-06-26)
reports/2026-06-26-system-review.md— revue système (où faire mieux)reports/2026-06-26-management-plan.md— modèle cible gestion multi-sitesreports/2026-06-26-dashboard-contract.md— design system du dashboard
Field log
Oct 19
Journal — 2026-10-19
- 07:54
[audit]— Veille des skills SEO publics (claude-seo 19k★, marketingskills 55k★, geo-seo-claude, localseoskills, claude-blog, seo-ops…) comparée à notre outillage. Notre machine couvre déjà l'essentiel et va plus loin sur mesure IA répétée, leads, open data. Écarts notés : L677 exactitude des réponses IA, L678 pages en déclin, L679 grille pack Maps Genius Prep, L680 élagage familles Emet, L681 skills seo-monitor/opportunities sur gsc.mjs cassé. Next : Ichai choisit lesquels lancer. - 09:03
[outil]— Lancé les 5 écarts de la veille skills. L681 fait (seo-monitor/opportunities + monitor.sh sur Ariane, snapshot 19/10 régénéré). L678 fait (scripts/declin.mjs : 7 pages GP en baisse à position stable, CTR qui chute ; 84 j dispo le 09/11). L677 fait (ia-exactitude.mjs + config/faits-clients.json : Emet rien de faux ; GP : ChatGPT dit 9 avis au lieu de 14, Gemini recopiait « all nine » de /shsat-prep → corrigé et déployé, vérifié en ligne). L679 fait (grille-maps.mjs, 1,20 $ : top 3 Maps 2 % sur SHSAT, 0 % sur ISEE prep → L682). L680 gelé jusqu'au 15/11 : rien à couper sur Emet, plan par lots écrit. - 09:59
[client]— Carnet du 19/10 exécuté : 5 pages GP en déclin réécrites et en ligne (titles + réponse directe sourcée NYSED/DOE) ; article G&T corrigé (plus d'examen depuis 2025-26), 2 experts inventés retirés, chiffres resourcés Chalkbeat ; /shsat-registration à jour ; page CFE Emet réécrite (CTR) ; demande de stage écartée des leads Emet (exclusion par horodatage) ; ISEE/SSAT + catégorie examens ajoutés à la fiche Google GP (en attente Google) ; grille Maps Brooklyn/Queens : GP absent partout. À relire : clics des pages le 16/11, grille ISEE fin novembre. Risque ouvert : d'autres articles GP peuvent contenir des experts inventés. - 10:57
[client]— Genius Prep, L683 : 15 articles du blog nettoyés dans PocketBase (experts, familles et chiffres inventés retirés ou sourcés, 4 titres success stories changés, liens internes alignés), sauvegardes b8615dc, preuve curl 0 nom restant. Pipeline d'origine (orchestrateur IA) introuvable sur le serveur. Next : relevé CTR des 2 anciens success stories indexés au 16/11 ; décider du sort de la branche L631. - 11:00
[client]— L683 : 15 articles GP nettoyés (11 du scan + 4 noindex) — ~40 experts et familles inventés retirés, études mal citées corrigées et sourcées, 4 « success stories » retitrées avec 2 vrais avis Google. Vérifié en ligne : 15 pages en 200, aucun nom restant. Branche run/gp-articles du 18/10 désormais périmée sur ces articles. Règle ajoutée à decisions.md.
Oct 18
Journal — 2026-10-18
- 17:38
[outil]— Chantier « moins de tokens, moins de temps » (mesuré sur 32 sessions : les agents = 2/3 des tokens). Fait par 3 agents Opus + 2 relectures adverses Sonnet : /seo 169 Ko → 37 Ko (le reste mot pour mot dans references/, 216/217 paragraphes + 1 corrigé), INDEX 33 → 3 Ko (index-detail.md) ; dossier de départ par site (depart.mjs → sites//depart.md, 8 Ko) donné aux agents au lieu de la fiche + skill + code ; leads.mjs --aide ; sources.mjs (158 copies datées des textes officiels d'Emet, 58 d'archive) ; attendre.sh, gsc-alertes.mjs, origine.sh, capture.mjs, deployer.sh (déploie puis verify.mjs) ; verify.mjs rend 2 sur une page en 404 ou non indexable (rendait 0). Corrigé : /seo disait que deploy.sh d'Emet prévient IndexNow (faux). Trouvé : lien impots.gouv mort sur l'observatoire CFE (L624). Ouvert : deployer.sh jamais lancé en vrai ; Légifrance bloqué (L625, compte API). Next : mesurer la consommation sur les 3 prochaines sessions. - 18:17
[audit]— Veille SEO/GEO (11 agents Sonnet de recherche + synthèse Opus + relecture adverse Sonnet) dans veille/2026-10-18/ : SYNTHESE.md, 11 thèmes sourcés avec niveau de preuve, extraits des docs primaires dans docs/. Constat principal : Emet = 9 310 pages générées sur 9 359 et 1 593 des 1 616 clics ; la doc Google de mai 2026 vise « une page par valeur de paramètre » → mesurer l'indexation par famille (L626) avant toute nouvelle famille (L627, L117/L118/L411 gelés jusque-là). La relecture a retiré une fausse citation de Google et recadré les consignes des évaluateurs ; GP = 10 demandes de parents depuis le 25/09 (~0,7 %), pas 2. Registre L626-L644. Mythes tranchés : llms.txt, schema pour l'IA, pénalité IA. X bloque la lecture depuis le serveur (signets seulement). Next : L626 en premier. - 19:23
[audit]— L626 fait : scripts/indexation-familles.mjs (sitemap servi → familles → 100 URL tirées par famille, graine datée → url-inspect --json → sites//indexation/ .json+.md). Mesure du 18/10 : Emet majoration 94 % indexée, CFE 77 %, cessions 58 %, taxe de séjour 54 %, délais 45 %, VM 43 %, TASCOM 31 % ; GP SHSAT 98 %, SAT 100 %, privées 80 %, Regents (lot du 11/10) 29 %. Explorée-non-indexée ≈ 0 partout : le manque d'Emet est de la découverte (inconnue de Google 15-39 %), pas un doute sur la valeur. Next : L645, refaire le 15/11. - 19:25
[chiffres]— chiffres de sites/_prompts/AB-ruben-etapes-acces-18-10.md re-dérivés et vérifiés (0 chiffre(s), déclarations re-dérivées) - 19:25
[client]— Ruben : étapes d'accès vérifiées sur les pages d'aide officielles (Search Console = accès complet, pas restreint comme dans le mail du 16/10), zone Cloudflare globalfi-expert.fr créée, 19 enregistrements OVH recopiés en DNS seul (site OVH, mails Microsoft 365 et Brevo intacts), message prêt et tamponné (sites/_prompts/AB-ruben-etapes-acces-18-10.md). Next : envoi sur WhatsApp, puis vérifier chaque accès à réception. - 19:36
[client]— Ruben : étapes d'accès envoyées sur WhatsApp sur l'ordre d'Ichai (18/10 22:36, id 3EB0D9A6C9710260DC69AC). Next : à son retour, vérifier chaque accès (admin WordPress, Search Console accès complet, invitation fiche Google à accepter, serveurs DNS Cloudflare actifs). - 19:55
[outil]— seo-outillage : L641 maj-google.mjs (dashboard + changelog Google, comparaison par famille 28 j/28 j, cron 08:15), L642 robots-ia.mjs (journal du proxy lu sur disque, proxy_visits trop limité ; OAI-SearchBot exécute le JS sur Genius Prep), L643 test d'apport dans /seo, L644 hors marque dans le rapport PDF (filtre de marque absent de l'API). Next : comparaison de la September spam update vers le 06/11. - 19:57
[client]— Ruben a fait les accès (WordPress : mail de création du compte ichai reçu à 22:52 sur ichaiwizlol ; Search Console et fiche Google : aucun mail d'invitation vu à 22:58). Réponse envoyée : pas de mot de passe par WhatsApp, explication du changement OVH (domaine et site restent chez OVH, mails intacts, non bloquant). Next : Ichai définit son mot de passe WordPress par le lien du mail ; vérifier les accès Search Console et fiche Google. - 20:09
[client]— Ruben : accès vérifiés par Codex sur le PC — WordPress ichai Administrateur (identifiants dans /root/.secrets/globalfi-wordpress.env), Search Console sc-domain:globalfi-expert.fr en accès complet (sitemap et demande d'indexation actifs), fiche Google invitation acceptée (Administrateur). Ruben a changé les serveurs DNS chez OVH vers Cloudflare (capture 23:06, en cours d'activation). Veille de bascule lancée. Next : à l'activation, vérifier site + mails, puis Full strict / proxy. - 20:15
[client]— Ruben : domaine globalfi-expert.fr passé sur Cloudflare (serveurs vus chez 1.1.1.1 et 8.8.8.8), site 200 servi par OVH, MX Outlook, autodiscover et vérification Google intacts. Ruben a demandé s'il doit signer le devis : oui, renvoi signé attendu. - 20:20
[client]— Ruben : copie complète du site globalfi-expert.fr récupérée sur le VPS (sites/ruben/site/, hors git : fichiers WordPress + db.sql, 9 821 fichiers, 131 Mo zip), via un extrait Code Snippets temporaire (export puis nettoyage, fichier d'export supprimé du serveur OVH, extrait n°17 désactivé — la suppression par l'API refuse). Mot de passe d'application WordPress seo-vps créé, dans /root/.secrets/globalfi-wordpress.env. Next : décider le déménagement de l'hébergement sur le VPS. - 21:20
[client]— Run du 18/10 au soir : L632 hub Regents en ligne (1 641 927 → 721 984 octets servis). Aperçu Emet fusionné (run/apercu : maillage, bloc méthode, taxe de séjour forfait + délibérations échues, outil plateforme avec période de perception et lien depuis 2 494 pages commune, L648 3 taux hors fourchette) + outil GP which-test + 166 corrections d'articles GP : page de relecture sandbox relecture-18-10, rien de visible en ligne. Next : relecture d'Ichai puis merge et un déploiement par site ; hub SHSAT allégé (en cours). - 21:46
[client]— Hub SHSAT allégé en ligne : 1 313 611 → 888 568 octets servis, 897 liens, recherche vérifiée sur le site. Relevé GP mois 2 régénéré (10 parents, 470 $, non envoyé). L126 fermé (/contact → /book). - 22:09
[outil]— seo-registre 19/10 : registre ouvert passé au crible (136 lignes) ; faits L041 (alertes Search Console dans system-check), L089 (interdits de marque dans kie-image), L414 (verify.mjs pages fortes), L044 mesurée et fermée (pas de cannibalisation SAT/ACT) ; branches run/gp-registre 9d79bfa (eslint, L410) et run/em-registre 35ad2b5 ada7035 c58cca4 (liens morts impots.gouv, commentaire L427) à fusionner par la session principale. Next : fusion + construction Emet, relecture des 23 lignes visibles. - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/01-insideschools-insidetools.md re-dérivés et vérifiés (0 chiffre(s), déclarations re-dérivées) - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/02-epicenter-nyc.md re-dérivés et vérifiés (11 chiffre(s), déclarations re-dérivées) - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/03-queens-daily-eagle.md re-dérivés et vérifiés (12 chiffre(s), déclarations re-dérivées) - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/04-west-side-rag.md re-dérivés et vérifiés (10 chiffre(s), déclarations re-dérivées) - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/05-tribeca-citizen.md re-dérivés et vérifiés (10 chiffre(s), déclarations re-dérivées) - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/06-school-search-nyc.md re-dérivés et vérifiés (1 chiffre(s), déclarations re-dérivées) - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/07-smart-city-kids.md re-dérivés et vérifiés (1 chiffre(s), déclarations re-dérivées) - 23:30
[chiffres]— chiffres de sites/geniusprep/outreach/mentions-2026-10/08-lab-middle-school-pa.md re-dérivés et vérifiés (0 chiffre(s), déclarations re-dérivées) - 23:32
[client]— Genius Prep, plan de mentions (sites/geniusprep/outreach/2026-10-19-plan-mentions.md) : bilan relu dans la boîte et DataForSEO — 28/09 : 0 réponse sur 12, 11/10 : 2 réponses sur 5 (les deux commerciales), 0 lien gagné ; 1 799 domaines de fermes de liens depuis le 25/09. 15 cibles neuves vérifiées (Epicenter-NYC, Queens Daily Eagle, Tribeca Citizen, West Side Rag, School Search NYC, Smart City Kids, PA de Lab MS, galerie NYC Open Data, BKReader, Brick Underground, NYC School Secrets, Chalkbeat en mars, Flatiron NoMad, enchères de PA, Manhattan Youth) + relance Inside Schools sur /insidetools/shsat. 8 brouillons tamponnés par verify.mjs (mentions-2026-10/), registre L650-L664. Rien envoyé. Next : oui d'Ichai, envois 1-5 avant le 26/10, mesure des liens le 09/11. - 23:38
[client]— Plan de mentions Emet (sites/emetexpertise/outreach/2026-10-19-plan-mentions.md). Diagnostic des 39 lignes d'envoi : 33 partis, 0 réponse connue, 0 lien, 6 ouvertures toutes de robots (4-24 s), 20 mails répondaient chez Nathan, aucune relance. 22 cibles neuves vérifiées en 200, choisies sur les domaines que citent les aperçus IA (La Micro by Flo, Propulse by CA, LégiFiscal, WikiCréa, Espace Auto-Entrepreneur, Welkomz, Jason Marinho, Chevalier PMS…). 8 brouillons dans outreach/brouillons/ (L665-L672) : M01-M05 passent verify.mjs chiffres en essai le 19/10, M06-M08 gelés jusqu'à L636. Relecture adverse appliquée. Règle de l'Ordre lue (art. 152, copie d'archive 01/2025). Dégel demandé : L532 (L504 fait). Neuf : Zenodo/GitHub (L674), data.europa à relire le 15/11 (L675), licence ODbL du jeu versement mobilité (L676). Next : Ichai tranche le périmètre (blogs/médias hors du oui d'Avi du 23/09), puis M01, M02, M05 semaine du 26/10 et M03 le 28/10.
Oct 17
Journal — 2026-10-17
- 16:23
[client]— Ruben : contre-proposition reçue (vocal 16/10 19:14, alerte Telegram partie), réponse d'Ichai envoyée sur WhatsApp le 17/10 19:23 : 500 € nov-déc, 560 € janv-avril, 6 mois fermes. Veille de réponse toujours active. - 19:06
[client]— Ruben a accepté par vocal le 17/10 22:03 : 6 mois de novembre à avril, 500 € nov-déc puis 560 € janv-avril. Il demande s'il y a un contrat à signer. Next : contrat ou devis à lui envoyer, procédure de démarrage (accès WordPress, domaine OVH). - 19:10
[client]— Devis Ruben DEV-2026-015 généré (GlobalFi Expert SAS, 6 mois, 500 € nov-déc puis 560 € janv-avril, total 3 240 €), envoyé à Ichai sur Telegram. Message WhatsApp proposé : devis par mail + accès à envoyer dès maintenant pour démarrer le 1er novembre. - 19:38
[client]— Devis DEV-2026-015 envoyé à Ruben sur WhatsApp le 17/10 (signé par Ichai, sans total), avec la demande d'envoyer les accès dès maintenant pour démarrer le 1er novembre. Attente : devis signé + accès.
Oct 16
Journal — 2026-10-16
- 11:58
[client]— Ruben Toledano rouvert : prep d'appel (fil WhatsApp lu par Ariane, site relevé : une dizaine de pages indexées, 30 pages par ville absentes, hors top 50, 2 domaines référents), lien de sa fiche Google, puis brouillon du mail de proposition après l'appel (sites/_prompts/AA-ruben-proposition-16-10.md, 400 €/mois sur 6 mois proposé, chiffres tamponnés, NON envoyé). Next : Ichai valide tarif/engagement/articles, puis envoi + devis PDF. Friction : gsc.mjs emetexpertise casse (clé du service account absente depuis le passage par Ariane), donc un chiffre GSC ne peut pas se re-dériver par verify.mjs. - 12:10
[chiffres]— chiffres de sites/_prompts/AA-ruben-proposition-16-10.md re-dérivés et vérifiés (13 chiffre(s), déclarations re-dérivées) - 12:10
[client]— Mail de proposition envoyé à Ruben Toledano sur l'ordre d'Ichai (15:10, depuis ichaiwizlol@gmail.com, id 1a1449eccae0b3a4, vu dans les envois) : 560 €/mois sur 6 mois, observatoires, procédure de démarrage. Chiffre Avi remis au relevé du jour (1 616 clics), chiffres tamponnés. Next : sa réponse ; s'il choisit Cloudflare, créer la zone en recopiant les enregistrements OVH, MX compris. - 12:32
[outil]— Veille de réponse Ruben lancée hors session (fond veille-ruben, 7 jours) : scripts/veille-reponse.sh + veille-reponse-check.py, déclenchés par ariane watch --stream (WhatsApp de Ruben + mails dont l'expéditeur contient toledano/globalfi), alerte Telegram par Ariane, vocal signalé une fois transcrit. Essai à blanc OK sur les deux branches. Preuve que le flux --stream émet bien hors session : en cours. - 12:48
[outil]— Veille Ruben : flux --stream prouvé hors session (2 mails vus passer), recherche étendue à la boîte ichaiwizm, veille relancée (fond veille-ruben, jusqu'au 23/10). Friction : arrêter une tâche fond laisse ses enfants (notée).