[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fqfLJuF_LgyTEFl8giYuiq871uAHgKNPLW0CcMQ6lcCY":3,"$fk1oB-e5O_BgwF2c-QwDgsx2Y7SMPhf77z9g2mrpoBuM":23},{"chapter":4,"prev":13,"next":18},{"slug":5,"chapterNum":6,"title":7,"titleEn":8,"summary":9,"summaryEn":10,"contentHtml":11,"contentHtmlEn":12},"automates","05","Automates, crons & runs","Automations, crons & runs","Décrit la couche d'exécution non-conversationnelle de Synedre OS : les 209 façades Python, leur enrobage cron, le registre un composant interne et la frontière entre doctrine run et unité agent.","Describes the non-conversational execution layer of Synedre OS: the 209 Python facades, their cron wrapper, the un composant interne registry, and the boundary between run doctrine and agent unit.","\u003Ch2 id=\"automates-crons-runs\">Automates, crons &amp; runs\u003C\u002Fh2>\n\n\u003Cp>Cette page décrit la couche d'exécution \u003Cstrong>non-conversationnelle\u003C\u002Fstrong> de Synedre OS : les façades Python, leur enrobage cron, le registre des automates, le double système d'ordonnancement (scheduler Nitro + filet crontab Linux) et la frontière entre la doctrine \u003Cem>run\u003C\u002Fem> (unité de travail pilotée) et l'unité d'exécution agent (tâche déléguée). Public visé : ingénieur reprenant le harness.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Note de sécurité\u003C\u002Fstrong> : le fichier crontab de production peut contenir des secrets en clair (mots de passe base de données, jetons d'authentification). Ils ne sont pas reproduits ici. Cette dette de hardening — secrets inline dans le crontab — est signalée en fin de page.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch2 id=\"modele-mental-agent-vs-automate\">1. Modèle mental : agent (pense) vs automate (exécute)\u003C\u002Fh2>\n\n\u003Cp>Le harness sépare deux registres :\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>\u003C\u002Fth>\n      \u003Cth>\u003Cstrong>Agent\u003C\u002Fstrong>\u003C\u002Fth>\n      \u003Cth>\u003Cstrong>Automate\u003C\u002Fstrong>\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Rôle\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Pense, décide, délègue (boucle ReAct)\u003C\u002Ftd>\n      \u003Ctd>Exécute une routine déterministe\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Support\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Persona LLM (Claude \u002F Mistral)\u003C\u002Ftd>\n      \u003Ctd>Script Python dans le dépôt\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Stockage\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Table des agents (référentiel personas)\u003C\u002Ftd>\n      \u003Ctd>Registre des automates (table dédiée)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Déclenchement\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Spawn orchestrateur, outil de tâche, unité de tâche agent\u003C\u002Ftd>\n      \u003Ctd>Cron, CLI, scheduler Nitro\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Lien entre les deux\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd colspan=\"2\">Une table de liaison relie chaque automate à son agent propriétaire ; un agent peut engendrer un automate ou une tâche déléguée.\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Un automate ne « réfléchit » pas : il peut \u003Cem>appeler\u003C\u002Fem> un LLM (génération de contenu, classification) mais son flux de contrôle est codé en dur. La pensée vit dans les personas et l'orchestrateur Atlas ; l'exécution répétable vit dans les automates.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>   ALEX \u002F EMAIL \u002F CRON\n          │ trigger\n          ▼\n   ┌──────────────┐  délègue   ┌──────────────┐  spawn   ┌──────────────┐\n   │  Atlas (run) │ ─────────► │  tâche agent │ ───────► │ LLM CLI \u002F    │\n   │  (run piloté)│            │  (déléguée)  │          │ script Python│\n   └──────────────┘            └──────────────┘          └──────────────┘\n          ▲                                                     │\n          │                                  cron\u002FNitro ────────┘\n          │                                  (automate planifié)\n   \u002Fhub\u002Fruns                              registre automates + logs automates\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2 id=\"facades-python\">2. Les façades Python\u003C\u002Fh2>\n\n\u003Cp>Le dépôt contient plusieurs centaines de fichiers Python. Ce ne sont pas tous des automates planifiés : la majorité sont des \u003Cstrong>façades\u003C\u002Fstrong> (point d'entrée unique pour une capacité), certaines étant des bibliothèques partagées, des outils manuels ou des scripts à usage unique.\u003C\u002Fp>\n\n\u003Cp>Le registre canonique de classification vit en base de données dans le \u003Cstrong>registre des automates\u003C\u002Fstrong>, pas dans les fichiers eux-mêmes. Deux axes structurent chaque entrée :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>\u003Ccode>kind\u003C\u002Fcode>\u003C\u002Fstrong> — nature technique :\n    \u003Cul>\n      \u003Cli>\u003Ccode>recurring\u003C\u002Fcode> — planifié (cron \u002F Nitro)\u003C\u002Fli>\n      \u003Cli>\u003Ccode>oneshot\u003C\u002Fcode> — déclenché à la demande\u003C\u002Fli>\n      \u003Cli>\u003Ccode>tool\u003C\u002Fcode> — outil invoqué par un agent ou un skill\u003C\u002Fli>\n      \u003Cli>\u003Ccode>lib\u003C\u002Fcode> — bibliothèque partagée, pas exécutable seule\u003C\u002Fli>\n      \u003Cli>\u003Ccode>meta\u003C\u002Fcode> — méta-outillage (ex. le wrapper cron lui-même)\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>\u003Ccode>caste\u003C\u002Fcode>\u003C\u002Fstrong> — groupe d'agents propriétaire. Casernes observées : \u003Cem>Vigies\u003C\u002Fem> (audits\u002FQA), \u003Cem>Scribes\u003C\u002Fem> (rédaction), \u003Cem>Oracles\u003C\u002Fem> (veille\u002Freporting), \u003Cem>Horlogers\u003C\u002Fem> (infra\u002Fsession\u002Fbackup), \u003Cem>Bâtisseurs\u003C\u002Fem> (build\u002Fprovision), \u003Cem>Tisserands\u003C\u002Fem> (SEO\u002Fmaillage), plus des castes techniques (\u003Ccode>execution\u003C\u002Fcode>, \u003Ccode>library\u003C\u002Fcode>, \u003Ccode>veille\u003C\u002Fcode>). \u003Cstrong>⚠ Dette\u003C\u002Fstrong> : la casse est incohérente entre entrées — normalisation à planifier.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>Taxonomie fonctionnelle des familles de façades\u003C\u002Fh3>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Famille\u003C\u002Fth>\n      \u003Cth>Rôle\u003C\u002Fth>\n      \u003Cth>Notes\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Orchestrateur Atlas\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Pipeline inbox → intention → spawn ; santé et monitoring de l'orchestrateur\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Audits\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Détection de dérives (schéma, SEO, accessibilité, sécurité, cannibalisation de mots-clés)\u003C\u002Ftd>\n      \u003Ctd>Exit non-zéro = \u003Cem>findings\u003C\u002Fem> d'audit, pas un crash (voir \u003Ccode>SOFT_FAIL_SCRIPTS\u003C\u002Fcode> §3)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Sauvegardes\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Dump base de données et fichiers vers stockage objet ; test de restauration mensuel\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Brainstorm\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Jobs asynchrones de génération d'idées (worker boucle + filet cron ponctuel)\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Blog \u002F SEO\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Génération et hygiène de contenu, SEO technique\u003C\u002Ftd>\n      \u003Ctd>Le moteur de maillage interne est archivé, plus actif\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Email \u002F inbox\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Lecture et écriture mail ; façade unique d'envoi client\u003C\u002Ftd>\n      \u003Ctd>Envoi client = façade only (pas d'accès SMTP direct depuis les agents)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Navigateur\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Automatisation navigateur (worker headful résidentiel, captures d'écran, QA)\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Banque \u002F facturation\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Synchronisation bancaire, facturation récurrente, relances\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Veille marque\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Surveillance marque, veille technologique, avis Google\u003C\u002Ftd>\n      \u003Ctd>Le moteur de newsletters est archivé, plus actif\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Mémoire \u002F apprentissage\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Indexation vectorielle (RAG pgvector), consolidation, post-mortems, détection de patterns\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>SRE \u002F garde-fous\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Surveillance coûts, détection de runaway, alerting, garde-fou écriture production\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Auto-maintenance documentation\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Miroir doc↔code, couverture des angles morts, réparation déterministe des chemins morts, régénération profonde, publication vers synedre.com\u003C\u002Ftd>\n      \u003Ctd>Voir §7\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Autonomie\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Seeder quotidien de chantiers en mode automatique ; amorce le pipeline de tâches agents\u003C\u002Ftd>\n      \u003Ctd>Voir §9\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Bibliothèques \u002F infra\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Briques partagées (connexion DB, logger, variables d'environnement, fournisseur IA, rotation de logs, wrapper cron) — non planifiées\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch2 id=\"wrapper-cron\">3. Le wrapper cron — watchdog d'enrobage\u003C\u002Fh2>\n\n\u003Cp>Tout automate planifié via crontab est lancé \u003Cstrong>à travers un wrapper de supervision\u003C\u002Fstrong>. Ce composant est le gardien de la stabilité de la couche cron : il protège chaque script contre les boucles d'erreur, tente des réparations automatiques et journalise chaque incident en base.\u003C\u002Fp>\n\n\u003Cp>Le déclenchement typique ressemble à :\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>*\u002F2 * * * *  &lt;chemin-wrapper&gt; &lt;nom-du-script&gt; &gt;&gt; &lt;fichier-log&gt; 2&gt;&amp;1\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Les responsabilités du wrapper sont les suivantes :\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Chargement des variables d'environnement\u003C\u002Fstrong> : le wrapper lit les fichiers \u003Ccode>.env\u003C\u002Fcode> et \u003Ccode>.env.host\u003C\u002Fcode> au démarrage et injecte les variables dans l'environnement du sous-processus sans écraser les valeurs déjà présentes.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Disjoncteur\u003C\u002Fstrong> : avant chaque lancement, le wrapper consulte le journal des erreurs en base. Si un script dépasse \u003Cstrong>10 échecs consécutifs\u003C\u002Fstrong>, il est désactivé automatiquement (le wrapper s'arrête sans exécuter le script). Une seule ligne est loggée au moment du franchissement du seuil — anti-spam qui évite des centaines de lignes répétitives par jour.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Exécution supervisée\u003C\u002Fstrong> : le script est lancé en sous-processus avec un \u003Cem>timeout\u003C\u002Fem> par défaut de \u003Cstrong>300 secondes\u003C\u002Fstrong>. Une liste de scripts à longue durée d'exécution (spawns LLM complexes, etc.) bénéficie d'un timeout étendu jusqu'à \u003Cstrong>1 800 secondes\u003C\u002Fstrong>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Auto-réparation\u003C\u002Fstrong> : en cas d'erreur, le wrapper analyse la sortie d'erreur standard, classe l'incident et tente un correctif automatique avant de relancer :\n    \u003Cul>\n      \u003Cli>\u003Cem>Import manquant\u003C\u002Fem> → injection de l'import depuis une liste connue\u003C\u002Fli>\n      \u003Cli>\u003Cem>Permission sur les logs\u003C\u002Fem> → redirection vers un dossier accessible\u003C\u002Fli>\n      \u003Cli>\u003Cem>Dossier introuvable\u003C\u002Fem> → création du dossier (sandboxé sous la racine du dépôt)\u003C\u002Fli>\n      \u003Cli>\u003Cem>Module Python absent\u003C\u002Fem> → installation via \u003Ccode>pip\u003C\u002Fcode> dans l'environnement virtuel\u003C\u002Fli>\n      \u003Cli>\u003Cem>Conflit de nom de variable log\u003C\u002Fem> → renommage automatique de l'alias conflictuel\u003C\u002Fli>\n      \u003Cli>\u003Cem>Erreur réseau\u003C\u002Fem> → 3 tentatives avec backoff exponentiel (2 s, 5 s, 10 s)\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Soft-fail pour les audits\u003C\u002Fstrong> : les scripts d'audit sont déclarés dans une liste spéciale. Pour eux, un code de sortie non-nul signifie \u003Cem>des findings ont été détectés\u003C\u002Fem>, pas un crash. Le compteur d'erreurs consécutives est remis à zéro et le code de sortie est propagé aux consommateurs en aval.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Journalisation en base\u003C\u002Fstrong> : chaque incident est enregistré dans la table de journal des erreurs cron (table append-only) avec les champs : type d'erreur, trace, indicateur de correction automatique, description du correctif appliqué, succès du retry, nombre d'échecs consécutifs, statut de désactivation.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Point de vigilance — absence de lock\u003C\u002Fstrong> : le wrapper lui-même n'implémente pas de verrou exclusif (\u003Ccode>flock\u003C\u002Fcode>). Quelques crons ajoutent un verrou à la main (notamment les workers brainstorm et certains extracteurs ponctuels). La \u003Cstrong>majorité des entrées wrapper n'ont pas de protection contre le recouvrement\u003C\u002Fstrong> : la sécurité repose uniquement sur le timeout et la fréquence de planification. C'est une dette d'architecture à traiter.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>La connexion à la base de données utilisée par le wrapper passe par la façade de connexion partagée du dépôt, qui pointe vers la base principale du vaisseau-mère. Les constantes de connexion à une ancienne base MariaDB présentes dans le code sont du \u003Cstrong>legacy mort\u003C\u002Fstrong> (supprimé lors d'un chantier d'infrastructure antérieur) et peuvent être ignorées.\u003C\u002Fp>\n\u003Ch2 id=\"systeme-runs\">4. Le système de runs\u003C\u002Fh2>\n\n\u003Ch3>4.1 Le run — unité d'exécution scopée\u003C\u002Fh3>\n\n\u003Cp>Un \u003Cstrong>run\u003C\u002Fstrong> désigne une exécution pilotée par l'orchestrateur central sur un périmètre défini (vaisseau-mère ou client). Au démarrage, le périmètre charge automatiquement son contexte depuis la base de données : infrastructure, interlocuteur, boîte de réception.\u003C\u002Fp>\n\n\u003Cp>La table des runs comporte les colonnes suivantes : identifiant, source, déclencheur, périmètre, titre, statut, type et identifiant de référence, horodatages de début et de fin.\u003C\u002Fp>\n\n\u003Cp>Deux déclencheurs sont actifs en production :\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Déclencheur\u003C\u002Fth>\n      \u003Cth>Source\u003C\u002Fth>\n      \u003Cth>Porte d'entrée\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>email\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Boîte de réception Atlas\u003C\u002Ftd>\n      \u003Ctd>Transfert vers l'adresse Atlas de l'orchestrateur, classifié par intention (run, question, chantier, bruit)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>chat\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Console\u003C\u002Ftd>\n      \u003Ctd>Console scopée accessible depuis le cadran des runs (sélection de périmètre + conversation Atlas)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Un troisième déclencheur planifié (\u003Ccode>cron\u003C\u002Fcode>) est documenté en doctrine mais n'est pas encore actif en base de données.\u003C\u002Fp>\n\n\u003Cp>L'interface de pilotage des runs est accessible depuis le cadran principal de l'orchestrateur, qui expose la liste des runs et une vue détaillée par item.\u003C\u002Fp>\n\n\u003Ch3>4.2 La tâche d'exécution agent — unité déléguée\u003C\u002Fh3>\n\n\u003Cp>\u003Cstrong>La tâche d'exécution agent n'est pas un run.\u003C\u002Fstrong> C'est l'unité de travail déléguée à un agent par l'orchestrateur. Elle vit sous le cadran des tâches et sous les chantiers. Un run \u003Cem>peut engendrer\u003C\u002Fem> une tâche d'exécution agent lorsque l'orchestrateur délègue.\u003C\u002Fp>\n\n\u003Cp>La table des tâches d'exécution (environ 209 lignes) comporte les colonnes clés suivantes : identifiant agent, titre, prompt, gabarit, périmètre, critères de sortie, statut, créateur, journal de sortie, code de sortie, tokens consommés, coût en USD, modèle utilisé.\u003C\u002Fp>\n\n\u003Cp>Statuts observés :\u003C\u002Fp>\n\u003Cul>\n  \u003Cli>\u003Cstrong>Terminé\u003C\u002Fstrong> : 182 tâches\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Échoué\u003C\u002Fstrong> : 23 tâches\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Annulé\u003C\u002Fstrong> : 4 tâches\u003C\u002Fli>\n  \u003Cli>États transitoires : en attente, en cours\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Gabarits d'exécution : \u003Ccode>code\u003C\u002Fcode> (208 tâches), \u003Ccode>audit\u003C\u002Fcode> (1 tâche).\u003C\u002Fp>\n\n\u003Cp>Origines de création observées : chaîne automatique de l'exécuteur de tâches (121), déclenchement manuel par opérateur (37), API de lancement automatique de chantier (29), chaîne automatique de travail (22).\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Exécuteur :\u003C\u002Fstrong> un worker planifié à la minute consomme les tâches en attente. Sa logique : il sélectionne les tâches dont le statut est \u003Cem>en attente\u003C\u002Fem> par ordre d'ancienneté, les marque \u003Cem>en cours\u003C\u002Fem>, lance le sous-processus selon le gabarit, diffuse la sortie standard vers le journal, puis marque \u003Cem>terminé\u003C\u002Fem> ou \u003Cem>échoué\u003C\u002Fem>.\u003C\u002Fp>\n\n\u003Cp>Deux modes d'exécution :\u003C\u002Fp>\n\u003Cul>\n  \u003Cli>\u003Cstrong>Mode simulation\u003C\u002Fstrong> (défaut) : aucun spawn réel, placeholder enregistré.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Mode live\u003C\u002Fstrong> : spawn d'un processus d'inférence. Les permissions accordées varient selon le gabarit :\n    \u003Cul>\n      \u003Cli>Gabarit \u003Cem>research\u003C\u002Fem> : lecture seule (fichiers, recherche, historique de révisions).\u003C\u002Fli>\n      \u003Cli>Gabarit \u003Cem>audit\u003C\u002Fem> : lecture + écriture du rapport.\u003C\u002Fli>\n      \u003Cli>Gabarit \u003Cem>code\u003C\u002Fem> : permissions étendues, sandboxées au répertoire de périmètre déclaré.\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>4.3 Le journal d'exécution des automates\u003C\u002Fh3>\n\n\u003Cp>Le journal d'exécution des automates (environ 124 000 lignes, croissance continue, mode append-only) trace chaque exécution d'automate de façon verbeuse. Colonnes : nom de l'automate, environnement, résultat, durée en secondes, nombre d'étapes, nombre d'erreurs, nombre d'avertissements, compteurs, détail des étapes, erreurs, avertissements, détail du résultat, contexte.\u003C\u002Fp>\n\n\u003Cp>Ce journal est distinct du registre des crashs du wrapper, qui ne consigne que les échecs du niveau enveloppe (wrapper) — non les exécutions nominales.\u003C\u002Fp>\n\n\u003Cp>Relation entre les composants :\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>Run orchestrateur (scopé)\n   └─ engendre ─► Tâche d'exécution agent ──log──► journal de sortie (output_log)\n\nRegistre automates ──exécution planifiée──► Journal verbeux automates\n                                        └─► Registre crashs wrapper (crashs uniquement)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Chr>\n\n\u003Ch2 id=\"ordonnancement\">5. Ordonnancement : deux schedulers en parallèle\u003C\u002Fh2>\n\n\u003Ch3>5.1 Scheduler applicatif Nitro (couche vaisseau-mère)\u003C\u002Fh3>\n\n\u003Cp>L'application vaisseau-mère expose un scheduler de tâches intégré au framework applicatif (fonctionnalité expérimentale activée dans la configuration). Les tâches de la famille \u003Cem>audit\u003C\u002Fem> sont définies dans un module dédié fusionné automatiquement par le framework — elles ne résident pas dans le répertoire de tâches racine.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Historique :\u003C\u002Fstrong> ce scheduler a été inactif pendant environ trois jours en mai 2026, puis restauré dans le cadre d'un chantier de remise en service. Les tâches sont actives depuis lors, sous réserve qu'un rebuild du container soit déclenché après toute modification de la configuration (le container doit être reconstruit pour refléter les changements).\u003C\u002Fp>\n\n\u003Cp>Tâches actives :\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Rôle\u003C\u002Fth>\n      \u003Cth>Fréquence cron\u003C\u002Fth>\n      \u003Cth>Guard\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Traitement file d'attente e-mail\u003C\u002Ftd>\n      \u003Ctd>Toutes les 2 min\u003C\u002Ftd>\n      \u003Ctd>—\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Surveillance de disponibilité\u003C\u002Ftd>\n      \u003Ctd>Toutes les 15 min\u003C\u002Ftd>\n      \u003Ctd>Environnement interne uniquement\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Veille dictionnaire\u003C\u002Ftd>\n      \u003Ctd>Toutes les 30 min\u003C\u002Ftd>\n      \u003Ctd>Environnement interne uniquement\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Veille dépendances\u003C\u002Ftd>\n      \u003Ctd>Quotidienne à 2h\u003C\u002Ftd>\n      \u003Ctd>Environnement interne uniquement\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Synthèse quotidienne\u003C\u002Ftd>\n      \u003Ctd>Quotidienne à 8h\u003C\u002Ftd>\n      \u003Ctd>Environnement interne uniquement\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Surveillance SSL\u003C\u002Ftd>\n      \u003Ctd>Quotidienne à 9h\u003C\u002Ftd>\n      \u003Ctd>Environnement interne uniquement\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Veille marque\u003C\u002Ftd>\n      \u003Ctd>Quotidienne à 12h\u003C\u002Ftd>\n      \u003Ctd>Environnement interne uniquement\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Le guard \u003Cem>environnement interne uniquement\u003C\u002Fem> court-circuite la tâche si une variable d'activation n'est pas positionnée dans l'environnement du container (transmise via le fichier d'environnement de l'hôte).\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Tâches toujours désactivées :\u003C\u002Fstrong> la synchronisation de boîte de réception et la synchronisation e-mail client. Le client IMAP actuel monopolise la boucle événementielle Node et provoque des timeouts en cascade sur la base de données ; la réactivation est conditionnée à la livraison d'un client IMAP non-bloquant (chantier en cours, phase B).\u003C\u002Fp>\n\n\u003Cp>Tâches présentes dans la codebase mais \u003Cstrong>non planifiées intentionnellement\u003C\u002Fstrong> : hygiène de blog, audit de page, veille sitemap, veille Synedre, vérification de faits, veille schéma.\u003C\u002Fp>\n\n\u003Ch3>5.2 Filet crontab système\u003C\u002Fh3>\n\n\u003Cp>Le crontab système du serveur porte l'essentiel de la charge d'ordonnancement et constitue un filet indépendant du scheduler applicatif. Sur 175 lignes totales (état au 7 juin 2026), \u003Cstrong>55 sont actives\u003C\u002Fstrong>. Les lignes commentées portent des marqueurs d'archéologie (gel temporaire, désactivation explicite) — le crontab fait office de journal de bord opérationnel.\u003C\u002Fp>\n\n\u003Cp>Familles actives :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Via enveloppe de surveillance (wrapper) :\u003C\u002Fstrong> monitoring (toutes les 2 min), sauvegarde (4h), audit des automates (3h), audit des sauvegardes (4h30), synchronisation bancaire (6h15), facturation récurrente (6h30), écoute de la boîte Atlas (toutes les min), orchestration de spawn (toutes les 5 min avec délai), synchronisation de boîte de réception (toutes les min), boucle nocturne de maintenance documentaire (4h), détection de dérive documentaire (toutes les 6h), correction automatique de documentation (toutes les 6h, +10 min), boucle de maintenance de nuit (5h), boucle d'autonomie (5h), synchronisation des embeddings (5h40).\n    \u003Cbr>\u003Cem>Ces scripts bénéficient du logging centralisé des erreurs wrapper et du mécanisme d'auto-réparation.\u003C\u002Fem>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Hors wrapper, modules Python :\u003C\u002Fstrong> indexation mémoire (toutes les 15 min), worker de tâches d'exécution agent en mode live (toutes les min), indexation des compétences (toutes les 15 min), détecteur de blocages (toutes les 15 min), alertes SRE (toutes les 30 min), alertes de coût (toutes les 6 min), détecteur de dérive (toutes les 15 min), extraction d'événements de négociation avec verrouillage flock (toutes les 2 min), analyse de leads (7h30 et 19h30), rafraîchissement de KPIs (5h15), synchronisation des avis Google (1h20), traitement des revues documentaires externes (toutes les 30 min).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Hors wrapper, scripts Python directs :\u003C\u002Fstrong> publication documentaire avec limite (3h30), test de restauration de sauvegarde (5h le 1er du mois), rotation des logs (6h), métriques mémoire (6h), indexation de sessions (toutes les heures), surveillance de propositions de compétences (toutes les heures), détection de propositions de compétences (4h le dimanche), détection de patterns (3h le dimanche), synthèse quotidienne de réactions (3h30).\n    \u003Cblockquote>\u003Cstrong>⚠️ Attention :\u003C\u002Fstrong> ces scripts s'exécutent sans enveloppe de surveillance. Ils ne bénéficient pas du logging centralisé des erreurs wrapper ni du mécanisme d'auto-réparation.\u003C\u002Fblockquote>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Worker de réflexion (brainstorm) :\u003C\u002Fstrong> lancé au démarrage du serveur en mode boucle persistante (processus détaché) ; un filet toutes les 2 min relance le processus s'il est absent ; un filet toutes les 3 min avec verrouillage flock rattrape les exécutions ponctuelles manquées.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Scripts shell et Node :\u003C\u002Fstrong> scan de flotte (4h30), analyse des dépendances TypeScript (4h05), sauvegarde base de données principale vers stockage objet S3 (3h30), sauvegarde fichiers vers S3 (3h), sauvegardes des bases de données des VPS clients vers S3 à des horaires décalés — tous avec fallback de notification en cas d'échec. Test de restauration mensuel (1h le 1er du mois), synchronisation mémoire (toutes les 30 min), nettoyage des verrous de chantier (toutes les 5 min), chien de garde des verrous d'orchestrateur (toutes les 15 min).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Appels directs HTTP :\u003C\u002Fstrong> drain de la file d'attente mail toutes les minutes en POST vers l'API locale (double sécurité avec la tâche Nitro correspondante) ; synchronisation de rejeu de session toutes les 5 minutes.\n    \u003Cblockquote>\u003Cstrong>⚠️ Dette P0 :\u003C\u002Fstrong> une de ces lignes crontab expose un jeton d'authentification en clair dans la commande. Ce token doit être sorti de la ligne crontab et chargé depuis une variable d'environnement ou un fichier sécurisé — action corrective à planifier.\u003C\u002Fblockquote>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Consolidation mémoire :\u003C\u002Fstrong> script de consolidation mensuel (minuit le 1er du mois, avec verrouillage flock de 600 s).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Quarantaine OSS :\u003C\u002Fstrong> purge des paquets en quarantaine selon une fenêtre calendaire définie.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Scripts existants mais \u003Cstrong>retirés du crontab\u003C\u002Fstrong> (façades actives, non planifiées) : notification fondateur, veille sectorielle — aucune ligne active ni commentée dans l'état courant du crontab.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cstrong>⚠️ Réconciliation registre ↔ ordonnancement (audit à produire) :\u003C\u002Fstrong> le registre canonique des automates est la source de vérité de classification, mais aucun mapping automatisé ne relie aujourd'hui chaque automate de type \u003Cem>récurrent\u003C\u002Fem> à sa ligne crontab ou à sa tâche Nitro. Il est impossible de savoir combien d'automates sont \u003Cstrong>orphelins\u003C\u002Fstrong> (enregistrés comme récurrents mais non planifiés). Un audit est nécessaire : jointure entre le registre des automates (filtrés sur le type récurrent), les lignes actives du crontab système, et les tâches déclarées dans la configuration du scheduler applicatif.\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"browser-worker-automate-distribue\">Le browser-worker : automate distribué hors datacenter\u003C\u002Fh2>\n\n\u003Cp>Le sous-système de navigation web est le seul composant de la plateforme dont l'exécution sort physiquement du datacenter. Deux raisons distinctes justifient cette sortie, et donc \u003Cstrong>deux topologies d'egress\u003C\u002Fstrong> qu'il ne faut pas confondre.\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Topologie\u003C\u002Fth>\n\u003Cth>Composant\u003C\u002Fth>\n\u003Cth>Où tourne le navigateur\u003C\u002Fth>\n\u003Cth>Ce qu'elle traite\u003C\u002Fth>\n\u003Cth>Mode\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Proxy résidentiel\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Agent navigateur (côté VPS)\u003C\u002Ftd>\n\u003Ctd>Navigateur headless sur le VPS du vaisseau-mère\u003C\u002Ftd>\n\u003Ctd>La \u003Cstrong>réputation IP\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Headless + profil furtif\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Worker headful distant\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Worker navigateur (côté machine résidentielle)\u003C\u002Ftd>\n\u003Ctd>Chrome headful sur la machine hôte résidentielle\u003C\u002Ftd>\n\u003Ctd>Le \u003Cstrong>fingerprint navigateur\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Fenêtre visible, IP résidentielle directe\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch3>6.1 Pourquoi un navigateur headful résidentiel plutôt qu'un navigateur headless sur VPS\u003C\u002Fh3>\n\n\u003Cp>Un tunnel proxy résidentiel fait sortir le trafic du VPS via une adresse IP résidentielle. Cela suffit pour les sites qui discriminent \u003Cstrong>uniquement sur la réputation IP datacenter\u003C\u002Fstrong>. Un mécanisme de garde vérifie avant chaque lancement que le proxy est actif \u003Cem>et\u003C\u002Fem> que l'adresse IP de sortie effective n'est pas celle du datacenter — toute sortie accidentelle par le datacenter est bloquée.\u003C\u002Fp>\n\n\u003Cp>Cependant, certains systèmes de protection tiers (notamment les challenges gérés de type Turnstile) ne jugent \u003Cstrong>pas\u003C\u002Fstrong> l'IP : ils analysent le \u003Cstrong>fingerprint du navigateur\u003C\u002Fstrong> — signaux JavaScript révélant l'automatisation (\u003Ccode>navigator.webdriver\u003C\u002Fcode>, absence de plugins, marqueurs headless, WebRTC, etc.). Un navigateur headless conserve un fingerprint de bot même derrière une IP résidentielle propre.\u003C\u002Fp>\n\n\u003Cp>Conséquence doctrinale : \u003Cstrong>classifier le type de protection avant de coder le flow\u003C\u002Fstrong>. Si la protection cible le fingerprint navigateur, un navigateur headful sur machine résidentielle est obligatoire ; le mode headless furtif ne passe pas.\u003C\u002Fp>\n\n\u003Cp>D'où le worker headful : un vrai Chrome (fenêtre visible, mode non-headless) tourne en permanence sur une machine hôte résidentielle toujours allumée, avec IP résidentielle directe. Aucun proxy n'est nécessaire côté worker : on est déjà sur la bonne IP, et les challenges de type Turnstile passent naturellement.\u003C\u002Fp>\n\n\u003Ch3>6.2 File de jobs et cycle de vie d'un job navigateur\u003C\u002Fh3>\n\n\u003Cp>La file de travaux navigateur est stockée en base de données selon le même pattern enqueue \u002F claim \u002F finish \u002F get que les autres workers de la plateforme. Chaque entrée porte : un identifiant de job, un type (\u003Cem>kind\u003C\u002Fem>), une charge utile JSON, un statut (\u003Ccode>queued\u003C\u002Fcode>, \u003Ccode>running\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>failed\u003C\u002Fcode>), un résultat JSON, un message d'erreur, l'identifiant de la machine exécutante, le nombre de tentatives, et les horodatages de début et fin. Des index garantissent la performance du claim ordonné.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>L'inversion de contrôle est le principe central\u003C\u002Fstrong> : le VPS du vaisseau-mère n'a aucun accès entrant vers la machine résidentielle (NAT, IP dynamique). C'est le worker distant qui \u003Cstrong>interroge activement\u003C\u002Fstrong> le VPS par connexion sortante. Le cycle complet est le suivant :\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>\u003Cstrong>Enqueue (VPS)\u003C\u002Fstrong> — Un agent ou un skill dépose un job dans la file. Seuls les types de job figurant dans une liste blanche stricte sont acceptés ; la charge utile JSON est validée avant insertion. L'injection SQL est prévenue par un mécanisme de dollar-quoting aléatoire.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Claim atomique (worker → VPS)\u003C\u002Fstrong> — Toutes les 5 secondes, le worker interroge le VPS via SSH sortant pour réclamer le job le plus ancien en statut \u003Ccode>queued\u003C\u002Fcode>. Le claim utilise un verrou transactionnel (\u003Ccode>FOR UPDATE SKIP LOCKED\u003C\u002Fcode>) : deux workers concurrents ne peuvent pas s'approprier le même job.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Dispatch (worker, headful)\u003C\u002Fstrong> — Le worker route le job vers le handler métier correspondant selon son type. La charge utile ne contient que des paramètres métier (limites, quantités) ; aucune évaluation dynamique de code n'est effectuée.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Récupération de code 2FA (worker → VPS → worker)\u003C\u002Fstrong> — Pour certains flows nécessitant une authentification à deux facteurs, le code n'arrive jamais directement sur la machine résidentielle. Le worker rappelle le VPS via SSH pour que celui-ci lise le code depuis la boîte mail côté serveur et le retourne en clair sur la sortie standard. Le code 2FA n'est jamais écrit dans les journaux.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Finish (worker → VPS)\u003C\u002Fstrong> — Une fois le job terminé, le worker transmet le résultat et l'éventuelle erreur en un unique objet JSON passé sur l'entrée standard de la commande SSH (jamais en argument shell, pour éviter les ruptures sur caractères spéciaux). Le VPS met à jour le statut, le résultat et l'horodatage de fin.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Auto-mise à jour du worker\u003C\u002Fstrong> — En mode boucle, lorsque le worker est inactif (jamais en cours de job), il vérifie périodiquement si le dépôt a évolué. En cas de mise à jour, il se redémarre automatiquement via remplacement de processus (\u003Ccode>os.execv\u003C\u002Fcode>), garantissant que la machine résidentielle tourne toujours sur le code le plus récent sans intervention manuelle.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Ch3>6.3 Le portail de commande SSH (défense en profondeur si la clé venait à fuiter)\u003C\u002Fh3>\n\n\u003Cp>La clé SSH de la machine résidentielle est enregistrée côté VPS avec une restriction stricte : elle n'ouvre \u003Cstrong>pas\u003C\u002Fstrong> un shell libre. À la place, chaque connexion SSH est interceptée par un portail de commande dédié, configuré directement dans le fichier d'autorisation SSH.\u003C\u002Fp>\n\n\u003Cp>Ce portail lit la commande SSH demandée, la découpe en tokens de façon sûre (sans jamais passer par un interpréteur shell), puis n'autorise l'exécution que si :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>le préfixe de la commande correspond \u003Cstrong>exactement\u003C\u002Fstrong> au module de gestion de la file de jobs ;\u003C\u002Fli>\n\u003Cli>le sous-commande appartient à l'ensemble autorisé (\u003Ccode>--claim\u003C\u002Fcode>, \u003Ccode>--finish\u003C\u002Fcode>, \u003Ccode>--get\u003C\u002Fcode>, \u003Ccode>--enqueue\u003C\u002Fcode>, récupération de code 2FA) ;\u003C\u002Fli>\n\u003Cli>chaque valeur de paramètre correspond à un motif strict prédéfini (identifiant numérique, statut \u003Ccode>done|failed\u003C\u002Fcode>, type de job alphanumérique borné, etc.).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Tout token inconnu ou valeur non conforme entraîne un refus immédiat avec journalisation. Le worker ne préfixe jamais ses commandes SSH d'un changement de répertoire : le portail gère lui-même le répertoire de travail. \u003Cstrong>Dans le pire cas d'une clé compromise\u003C\u002Fstrong>, l'attaquant peut au plus polluer la file de jobs — il ne peut pas exécuter de code arbitraire sur le VPS.\u003C\u002Fp>\n\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>Données personnelles et captures d'écran.\u003C\u002Fstrong> Les résultats de certains jobs (noms, extraits de messages) sont stockés dans la base de données privée en JSON structuré et ne sont jamais écrits en clair dans les journaux — seul un compteur est tracé. Les captures d'écran de débogage sont purgées en fin de run par le module métier concerné, pas par le worker lui-même. Un mode \u003Cem>conservation des captures\u003C\u002Fem> existe mais est réservé au débogage local. Le rapatriement structuré des captures fait l'objet d'un chantier en cours.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>⚠️ Bug latent connu :\u003C\u002Fstrong> le délai d'expiration des jobs navigateur n'est actuellement pas appliqué — un job dont le navigateur se bloque peut rester en statut \u003Ccode>running\u003C\u002Fcode> indéfiniment. Un correctif est à confirmer.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"boucle-auto-maintenance-doc\">Boucle d'auto-maintenance de la documentation\u003C\u002Fh2>\n\n\u003Cp>Depuis un chantier récent, le harness dispose d'un sous-système cohérent qui mesure l'écart entre sa propre documentation et son code, puis le corrige de façon autonome avec des portes de validation (\u003Cem>gates\u003C\u002Fem>). Ce sous-système constitue la couche d'automates la plus récente et la plus intriquée.\u003C\u002Fp>\n\n\u003Ch3>Architecture du pipeline\u003C\u002Fh3>\n\n\u003Cp>La boucle nocturne s'articule autour d'un orchestrateur central qui se déclenche chaque matin à 5 h et enchaîne huit étapes dans l'ordre :\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Percevoir\u003C\u002Fstrong> — le détecteur de dérive inspecte l'écart entre la documentation et le code.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Couvrir\u003C\u002Fstrong> — le détecteur d'angles morts vérifie la complétude de la documentation.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Réparer\u003C\u002Fstrong> — le correcteur déterministe résout les références mortes.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Soigner\u003C\u002Fstrong> — le bilan de santé agrège toutes les dimensions.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Régénérer\u003C\u002Fstrong> — le régénérateur profond réécrit en profondeur (avec gate).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Proposer\u003C\u002Fstrong> — le module de staging prépare les chapitres et produit un brief.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Publier-doc\u003C\u002Fstrong> — le module de publication valide et active les chapitres.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Publier\u003C\u002Fstrong> — le module de synchronisation publique pousse un snapshot assaini vers le site public.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>Par ailleurs, le détecteur de dérive et le correcteur mécanique tournent chacun sur leur propre rythme indépendant (toutes les six heures), sans dépendre de l'orchestrateur. Un module de revue externe s'exécute quant à lui toutes les trente minutes.\u003C\u002Fp>\n\n\u003Ch2 id=\"detecteur-derive\">Le détecteur de dérive (Phase 0 — percevoir)\u003C\u002Fh2>\n\n\u003Cp>Ce composant s'exécute toutes les six heures en mode \u003Cstrong>lecture seule stricte\u003C\u002Fstrong> : il n'écrit jamais le code ni la documentation, mais enregistre uniquement ses observations dans la base de données.\u003C\u002Fp>\n\n\u003Cp>Il mesure trois types de dérive :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Doc périmée\u003C\u002Fstrong> — le fichier source référencé a été modifié après la rédaction du document : la documentation décrit un corps obsolète.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Référence morte\u003C\u002Fstrong> — le document cite un fichier qui n'existe plus dans le dépôt.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Publication en retard\u003C\u002Fstrong> — le chapitre public diffère du document interne (divergence de contenu détectée par empreinte).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Le composant est idempotent : il ne produit qu'une seule observation par chapitre et par jour. Il retourne un signal d'absence de dérive ou un signal de dérive détectée.\u003C\u002Fp>\n\n\u003Ch2 id=\"detecteur-angles-morts\">Le détecteur d'angles morts (Phase 0bis — couverture)\u003C\u002Fh2>\n\n\u003Cp>Lancé juste après le détecteur de dérive, ce composant vérifie la \u003Cstrong>complétude\u003C\u002Fstrong> de la documentation — là où le détecteur de dérive vérifie la \u003Cstrong>fidélité\u003C\u002Fstrong>. La question posée est : « Existe-t-il une partie du harness que la documentation ne mentionne jamais ? »\u003C\u002Fp>\n\n\u003Cp>La méthode repose sur une différence d'ensembles :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Corps documentable\u003C\u002Fstrong> — l'ensemble des automates et des structures de données du harness.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Couverture actuelle\u003C\u002Fstrong> — ceux mentionnés dans au moins un chapitre de documentation interne.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Angles morts\u003C\u002Fstrong> — la différence : composants présents mais jamais documentés, regroupés par famille.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Un orphelin isolé n'est pas préoccupant. En revanche, plusieurs orphelins d'une même famille constituent un candidat à une nouvelle section ou un nouveau chapitre, créé par le régénérateur profond en mode création. Ce composant est lui aussi \u003Cstrong>lecture seule\u003C\u002Fstrong> et sa dimension « couverture » est consolidée dans le bilan de santé.\u003C\u002Fp>\n\n\u003Ch2 id=\"correcteur-deterministe\">Le correcteur déterministe de références mortes (Phase 1bis — réparer)\u003C\u002Fh2>\n\n\u003Cp>Ce composant comble le trou entre le détecteur de dérive — qui diagnostique les références mortes — et le régénérateur LLM — qui corrige la prose mais ignore qu'un fichier a été déplacé. La résolution est \u003Cstrong>déterministe, sans hallucination possible\u003C\u002Fstrong>.\u003C\u002Fp>\n\n\u003Cp>Pour chaque référence morte trouvée dans la documentation interne, le composant recherche le vrai emplacement par le nom de base du fichier :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Un seul résultat\u003C\u002Fstrong> (certitude) → la référence est corrigée en place.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Aucun résultat\u003C\u002Fstrong> (composant supprimé) → signalement, rapport, notification au fondateur pour avis humain.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Plusieurs résultats\u003C\u002Fstrong> (ambiguïté) → signalement et rapport, \u003Cstrong>jamais de réécriture automatique\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Garde-fous : ce composant ne touche qu'aux fichiers de documentation interne, jamais au code ni aux chapitres publiés. Il est idempotent et produit un commit unique et réversible par run.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Distinction importante :\u003C\u002Fstrong> ce correcteur traite \u003Cem>uniquement les chemins\u003C\u002Fem> de façon déterministe et est déclenché par l'orchestrateur. Le correcteur mécanique décrit ci-après tente d'autres types de corrections structurelles et tourne sur son propre rythme indépendant.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch2 id=\"bilan-sante\">Le bilan de santé (Phase 2 — soigner)\u003C\u002Fh2>\n\n\u003Cp>Ce composant agrège quatre dimensions sans en modifier aucune :\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Dimension\u003C\u002Fth>\n      \u003Cth>Ce qui est mesuré\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Proprioception\u003C\u002Ftd>\n      \u003Ctd>Écart documentation ↔ code (drift détecté par le détecteur de dérive)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Dette\u003C\u002Ftd>\n      \u003Ctd>Backlog de tâches prioritaires ouvertes\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Apprentissage\u003C\u002Ftd>\n      \u003Ctd>Signal issu des cicatrices accumulées\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Automates\u003C\u002Ftd>\n      \u003Ctd>Santé des crons (erreurs détectées)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Le bilan produit un snapshot idempotent par dimension et par jour, ainsi qu'un item pour le compte-rendu quotidien. Il retourne un signal : OK, avertissement, ou état critique.\u003C\u002Fp>\n\n\u003Ch2 id=\"correcteur-mecanique\">Le correcteur mécanique (indépendant de l'orchestrateur)\u003C\u002Fh2>\n\n\u003Cp>Ce composant tourne toutes les six heures sur son propre rythme, décalé de dix minutes par rapport au détecteur de dérive. Il ne touche qu'aux fichiers de documentation interne — jamais au code, jamais aux chapitres publiés.\u003C\u002Fp>\n\n\u003Cp>Règle de sûreté : une référence morte n'est corrigée que si son nom de base correspond à \u003Cstrong>exactement un\u003C\u002Fstrong> fichier suivi par le dépôt. Zéro candidat (composant supprimé) ou deux candidats ou plus (ambiguïté) → proposition soumise à validation humaine, sans aucune écriture automatique.\u003C\u002Fp>\n\n\u003Cp>Le composant est réversible (un seul commit par run, annulable en une opération), plafonné par un maximum configurable, et doté d'un coupe-circuit global. Il fonctionne en mode simulation par défaut ; le mode actif doit être explicitement demandé.\u003C\u002Fp>\n\n\u003Ch2 id=\"regenerateur-profond\">Le régénérateur profond (Phase 4 — régénérer)\u003C\u002Fh2>\n\n\u003Cp>Ce composant est appelé par l'orchestrateur avec un budget-temps maximum (défaut : 4 500 secondes). Il lance un processus Claude en mode sans interface qui : (a) réécrit le document interne en lisant le code réel, (b) produit directement la version publique bilingue FR+EN avec ses métadonnées dans un espace temporaire.\u003C\u002Fp>\n\n\u003Cp>Le document interne est commité, puis une porte anti-fuite (vérification regex multi-couches) est appliquée avant tout staging public. Cette porte est indépendante de l'orchestrateur.\u003C\u002Fp>\n\n\u003Ch2 id=\"staging-brief\">Le module de staging et de brief (Phase 5 — proposer)\u003C\u002Fh2>\n\n\u003Cp>Lancé par l'orchestrateur après la régénération profonde, ce composant re-stage les chapitres dont le document interne devance la version publiée, déclenche la publication unitaire, et place les chapitres assainis et bilingues dans la file d'attente de publication. Une porte anti-fuite indépendante bloque tout chapitre contenant une fuite détectée. Le composant produit également un brief de régénération pour les cas ambigus nécessitant une attention humaine.\u003C\u002Fp>\n\n\u003Ch2 id=\"orchestrateur\">L'orchestrateur nocturne (cron 5 h)\u003C\u002Fh2>\n\n\u003Cp>L'orchestrateur déclenche les huit étapes dans l'ordre, sous plusieurs garde-fous globaux :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Verrou anti-recouvrement\u003C\u002Fstrong> — une seule instance à la fois.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Coupe-circuit base de données\u003C\u002Fstrong> — si le kill-switch est activé pour cet automate, le tick s'arrête immédiatement.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Budget-temps configurable\u003C\u002Fstrong> pour le régénérateur profond ; un timeout externe plus large englobe l'ensemble.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Isolation des étapes\u003C\u002Fstrong> — si une étape échoue, le tick continue et note l'échec.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>En fin de run, l'orchestrateur appelle le module de synchronisation publique pour pousser un snapshot assaini vers le site public.\u003C\u002Fp>\n\n\u003Cp>Invocation typique (avec simulation ou sans régénération profonde) :\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>python3 [orchestrateur] [--dry-run] [--no-deep] [--deep-budget-sec N]\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2 id=\"sync-public\">La synchronisation publique\u003C\u002Fh2>\n\n\u003Cp>Appelé en fin de tick, ce composant lit les données de la boucle en lecture seule, les assainit (zéro nom de client, adresse IP, secret, chemin interne), puis pousse un snapshot JSON vers la base de données du site public par upsert idempotent. Ce snapshot alimente le cadran de maintenance accessible depuis la section documentation du site.\u003C\u002Fp>\n\n\u003Ch2 id=\"publication-chapitres\">La publication des chapitres vers le site public\u003C\u002Fh2>\n\n\u003Cp>Ce composant est invoqué selon deux chemins :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Planification autonome\u003C\u002Fstrong> chaque nuit à 3 h 30, avec activation automatique.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Invocation par l'orchestrateur\u003C\u002Fstrong> à l'étape « publier-doc », avec les mêmes paramètres — rendant l'orchestrateur auto-suffisant sans dépendre du cron séparé.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Le composant détecte les chapitres dont l'empreinte du document interne diverge de celle du chapitre publié, les re-stage dans la file d'attente de publication (transport sécurisé vers la base du site public), avec une pré-vérification anti-fuite regex multi-couches.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Logique d'activation :\u003C\u002Fstrong> un geste humain délibéré (\u003Ccode>--activate\u003C\u002Fcode>) ou l'activation automatique (\u003Ccode>--auto-activate\u003C\u002Fcode>) peut publier un chapitre en attente. En mode automatique, les chapitres passant tous les gates machine — y compris la vérification anti-gourou définie dans le registre public §VII — sont publiés sans intervention humaine. Les chapitres refusés restent en file d'attente et génèrent une notification au fondateur.\u003C\u002Fp>\n\n\u003Ch2 id=\"revue-externe\">Le regard des autres (revue externe — toutes les 30 min)\u003C\u002Fh2>\n\n\u003Cp>Ce composant s'exécute toutes les trente minutes indépendamment de l'orchestrateur. Il dépile les revues soumises par un opérateur humain (réponse d'un modèle externe), les évalue via un processus Claude sandboxé avec délimiteurs anti-injection, puis parse sélectivement le verdict : validité globale et détection de tentative d'injection.\u003C\u002Fp>\n\n\u003Cp>Si la revue est valide, le composant émet un \u003Cstrong>signal uniquement\u003C\u002Fstrong> (note dans le compte-rendu quotidien) sans déclencher de régénération en ligne. La régénération est consommée lors du prochain cycle de l'orchestrateur, selon un couplage lâche qui évite les timeouts et la perte de calcul.\u003C\u002Fp>\n\n\u003Ch2 id=\"tick-autonomie\">Le tick d'autonomie — amorçage du pipeline\u003C\u002Fh2>\n\n\u003Cp>Ce composant s'exécute quotidiennement à 5 h (simultanément mais indépendamment de l'orchestrateur de documentation). Sa doctrine : \u003Cstrong>simulation par défaut\u003C\u002Fstrong> — il observe sans rien semer ; le mode actif doit être explicitement demandé.\u003C\u002Fp>\n\n\u003Cp>Son rôle est de combler le \u003Cstrong>cold-start\u003C\u002Fstrong> du pipeline d'autonomie. Le moteur de tâches se chaîne lui-même \u003Cem>à l'intérieur\u003C\u002Fem> d'un travail en cours, mais rien n'amorce le pipeline : un travail en mode automatique dont toutes les tâches sont en attente et dont aucun run n'est actif reste dormant. Ce tick crée un run en attente pour la prochaine tâche éligible de chaque travail actif sans run en cours.\u003C\u002Fp>\n\n\u003Cp>Garde-fous :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Plafond de coût\u003C\u002Fstrong> — si le coût cumulé d'un chantier atteint 100 % de son budget, il est gelé et aucun run n'est semé.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Double coupe-circuit\u003C\u002Fstrong> — deux variables d'environnement permettent d'interrompre totalement le composant sans aucune action.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Déploiement\u003C\u002Fstrong> — signalé uniquement, jamais exécuté dans le tick ; le déploiement en production reste un geste humain délibéré.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Règle constitutionnelle :\u003C\u002Fstrong> le tick peut aller jusqu'à la préproduction (\u003Ccode>.\u002Fdeploy\u003C\u002Fcode>) mais \u003Cstrong>n'appelle jamais la mise en production\u003C\u002Fstrong> (\u003Ccode>.\u002Fship\u003C\u002Fcode>). La décision de mise en production reste un geste humain explicite.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"garde-fous-operations\">Garde-fous &amp; opérations\u003C\u002Fh2>\n\n\u003Cp>Cette section recense les mécanismes de protection, de surveillance et de maintenance opérationnelle qui encadrent l'exécution des automates sur le vaisseau-mère.\u003C\u002Fp>\n\n\u003Ch3>Mécanismes de protection actifs\u003C\u002Fh3>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Disjoncteur automatique :\u003C\u002Fstrong> un composant de supervision enveloppe chaque automate planifié. Après dix crashs consécutifs, il désactive automatiquement l'automate concerné et consigne l'état dans la table de suivi des erreurs de planification, évitant ainsi toute boucle d'échec infinie.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Audit quotidien :\u003C\u002Fstrong> chaque nuit, un automate d'audit relit les journaux d'erreurs de planification ainsi que les journaux d'exécution des automates afin de détecter toute anomalie persistante.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Surveillance des coûts et des emballements :\u003C\u002Fstrong> trois automates de garde tournent en continu — l'un toutes les six heures pour alerter sur les dérives de coût, un autre toutes les quinze minutes pour détecter les exécutions en emballement, un troisième toutes les trente minutes pour les alertes de fiabilité de service.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Sauvegardes et test de restauration :\u003C\u002Fstrong> des dumps nightly sont poussés vers le stockage objet distant (base de données du vaisseau-mère, fichiers, bases distantes des clients). Un automate mensuel rejoue un test de restauration complet pour valider la cohérence des sauvegardes.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Déblocage automatique :\u003C\u002Fstrong> toutes les quinze minutes, un automate détecte les exécutions de tâches bloquées et les soumet à nouveau, dans la limite de quatre tentatives. Un interrupteur d'urgence permet de désactiver ce mécanisme sans modifier le planificateur.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Boucle de maintenance documentaire :\u003C\u002Fstrong> un ensemble de trois automates assure la surveillance de la dérive documentaire (toutes les six heures), la correction automatique des anomalies détectées (décalé de dix minutes sur le même cycle), et un cadran de maintenance nocturne (chaque jour à 5 h). Ces mécanismes sont décrits en détail dans la section dédiée à la régénération de la documentation.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>Dette technique signalée\u003C\u002Fh3>\n\n\u003Cul>\n  \u003Cli>\n    \u003Cstrong>✅ Automate d'audit supprimé sans remplaçant (résolu le 2026-06-06) :\u003C\u002Fstrong> un automate d'audit de fuites d'URL avait été retiré du dépôt sans qu'un successeur soit mis en place. La ligne de planification correspondante a été supprimée du planificateur. Seul subsiste un dossier de journaux périmés. Aucune façade d'audit de fuite d'URL n'existe à ce jour ; elle devra être recréée via l'enveloppe de planification standard si le besoin réapparaît.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Jeton d'authentification en clair dans le planificateur :\u003C\u002Fstrong> une entrée du planificateur embarque un bearer token directement dans la ligne de commande, sans passer par le fichier d'environnement sécurisé. La migration vers une lecture par variable d'environnement est à réaliser — le patron propre est déjà appliqué sur les autres entrées du planificateur (lecture du mot de passe base de données depuis le fichier \u003Ccode>.env\u003C\u002Fcode>).\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Double exécution de la file d'e-mails :\u003C\u002Fstrong> deux entrées de planification déclenchent indépendamment le traitement de la file d'envoi d'e-mails chaque minute. Le mécanisme est idempotent grâce à une clause \u003Ccode>LIMIT\u003C\u002Fcode> côté file, mais la redondance reste à nettoyer pour éviter toute confusion opérationnelle.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Incohérence de casse sur le champ de classification des automates :\u003C\u002Fstrong> le champ désignant le type d'exécution présente une capitalisation non uniforme (\u003Ccode>execution\u003C\u002Fcode> vs \u003Ccode>Execution\u003C\u002Fcode>) dans la table des automates. Une normalisation est à planifier.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Absence de verrou générique dans l'enveloppe de planification :\u003C\u002Fstrong> l'enveloppe qui encapsule les automates planifiés ne pose pas de verrou système par défaut. Des recouvrements d'exécution restent possibles sur les jobs lents qui ne gèrent pas eux-mêmes leur exclusion mutuelle.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Automate d'autonomie toujours en mode observation :\u003C\u002Fstrong> l'automate de seeding autonome tourne actuellement en mode \u003Cem>dry-run\u003C\u002Fem> — il observe et journalise sans effectuer d'actions réelles. Le mode actif n'est pas encore câblé dans le planificateur. Par ailleurs, un délai de garde-fou sur les jobs navigateur est défini dans le code mais non appliqué à l'exécution : un job navigateur peut théoriquement rester suspendu indéfiniment. Un correctif est à confirmer.\n  \u003C\u002Fli>\n\u003C\u002Ful>","\u003Ch2 id=\"automates-crons-runs\">Automations, crons &amp; runs\u003C\u002Fh2>\n\n\u003Cp>This page describes the \u003Cstrong>non-conversational\u003C\u002Fstrong> execution layer of Synedre OS: the Python facades, their cron wrapping, the automation registry, the dual scheduling system (Nitro scheduler + Linux crontab safety net), and the boundary between the \u003Cem>run\u003C\u002Fem> doctrine (a driven unit of work) and the agent execution unit (a delegated task). Intended audience: engineers taking over the harness.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Security note\u003C\u002Fstrong>: the production crontab file may contain secrets in plain text (database passwords, authentication tokens). They are not reproduced here. This hardening debt — inline secrets in the crontab — is flagged at the end of this page.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch2 id=\"modele-mental-agent-vs-automate\">1. Mental model: agent (thinks) vs. automation (executes)\u003C\u002Fh2>\n\n\u003Cp>The harness separates two registers:\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>\u003C\u002Fth>\n      \u003Cth>\u003Cstrong>Agent\u003C\u002Fstrong>\u003C\u002Fth>\n      \u003Cth>\u003Cstrong>Automation\u003C\u002Fstrong>\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Role\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Thinks, decides, delegates (ReAct loop)\u003C\u002Ftd>\n      \u003Ctd>Executes a deterministic routine\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Substrate\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>LLM persona (Claude \u002F Mistral)\u003C\u002Ftd>\n      \u003Ctd>Python script in the repository\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Storage\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Agents table (persona registry)\u003C\u002Ftd>\n      \u003Ctd>Automation registry (dedicated table)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Triggering\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Orchestrator spawn, task tool, agent task unit\u003C\u002Ftd>\n      \u003Ctd>Cron, CLI, Nitro scheduler\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Link between the two\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd colspan=\"2\">A join table links each automation to its owning agent; an agent can spawn an automation or a delegated task.\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>An automation does not \"reason\": it may \u003Cem>call\u003C\u002Fem> an LLM (content generation, classification) but its control flow is hard-coded. Reasoning lives in the personas and the Atlas orchestrator; repeatable execution lives in the automations.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>   ALEX \u002F EMAIL \u002F CRON\n          │ trigger\n          ▼\n   ┌──────────────┐  delegates  ┌──────────────┐  spawn   ┌──────────────┐\n   │  Atlas (run) │ ──────────► │  agent task  │ ───────► │ LLM CLI \u002F    │\n   │  (driven run)│             │  (delegated) │          │ Python script│\n   └──────────────┘             └──────────────┘          └──────────────┘\n          ▲                                                     │\n          │                               cron\u002FNitro ──────────┘\n          │                               (scheduled automation)\n   \u002Fhub\u002Fruns                           automation registry + automation logs\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2 id=\"facades-python\">2. Python facades\u003C\u002Fh2>\n\n\u003Cp>The repository contains several hundred Python files. Not all of them are scheduled automations: the majority are \u003Cstrong>facades\u003C\u002Fstrong> (single entry point for a capability), some being shared libraries, manual tools, or one-off scripts.\u003C\u002Fp>\n\n\u003Cp>The canonical classification registry lives in the database in the \u003Cstrong>automation registry\u003C\u002Fstrong>, not in the files themselves. Two axes structure each entry:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>\u003Ccode>kind\u003C\u002Fcode>\u003C\u002Fstrong> — technical type:\n    \u003Cul>\n      \u003Cli>\u003Ccode>recurring\u003C\u002Fcode> — scheduled (cron \u002F Nitro)\u003C\u002Fli>\n      \u003Cli>\u003Ccode>oneshot\u003C\u002Fcode> — triggered on demand\u003C\u002Fli>\n      \u003Cli>\u003Ccode>tool\u003C\u002Fcode> — tool invoked by an agent or a skill\u003C\u002Fli>\n      \u003Cli>\u003Ccode>lib\u003C\u002Fcode> — shared library, not standalone-executable\u003C\u002Fli>\n      \u003Cli>\u003Ccode>meta\u003C\u002Fcode> — meta-tooling (e.g. the cron wrapper itself)\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>\u003Ccode>caste\u003C\u002Fcode>\u003C\u002Fstrong> — owning agent group. Observed castes: \u003Cem>Vigies\u003C\u002Fem> (audits\u002FQA), \u003Cem>Scribes\u003C\u002Fem> (writing), \u003Cem>Oracles\u003C\u002Fem> (monitoring\u002Freporting), \u003Cem>Horlogers\u003C\u002Fem> (infra\u002Fsession\u002Fbackup), \u003Cem>Bâtisseurs\u003C\u002Fem> (build\u002Fprovisioning), \u003Cem>Tisserands\u003C\u002Fem> (SEO\u002Finterlinking), plus technical castes (\u003Ccode>execution\u003C\u002Fcode>, \u003Ccode>library\u003C\u002Fcode>, \u003Ccode>veille\u003C\u002Fcode>). \u003Cstrong>⚠ Debt\u003C\u002Fstrong>: casing is inconsistent across entries — normalisation to be planned.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>Functional taxonomy of facade families\u003C\u002Fh3>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Family\u003C\u002Fth>\n      \u003Cth>Role\u003C\u002Fth>\n      \u003Cth>Notes\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Atlas orchestrator\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Inbox → intent → spawn pipeline; orchestrator health and monitoring\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Audits\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Drift detection (schema, SEO, accessibility, security, keyword cannibalisation)\u003C\u002Ftd>\n      \u003Ctd>Non-zero exit = audit \u003Cem>findings\u003C\u002Fem>, not a crash (see \u003Ccode>SOFT_FAIL_SCRIPTS\u003C\u002Fcode> §3)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Backups\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Database and file dump to object storage; monthly restore test\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Brainstorm\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Asynchronous idea-generation jobs (loop worker + punctual cron safety net)\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Blog \u002F SEO\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Content generation and hygiene, technical SEO\u003C\u002Ftd>\n      \u003Ctd>Internal interlinking engine is archived, no longer active\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Email \u002F inbox\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Mail reading and writing; single client-facing send facade\u003C\u002Ftd>\n      \u003Ctd>Client send = facade only (no direct SMTP access from agents)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Browser\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Browser automation (residential headful worker, screenshots, QA)\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Banking \u002F billing\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Bank synchronisation, recurring billing, payment reminders\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Brand monitoring\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Brand surveillance, technology watch, Google reviews\u003C\u002Ftd>\n      \u003Ctd>Newsletter engine is archived, no longer active\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Memory \u002F learning\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Vector indexing (RAG pgvector), consolidation, post-mortems, pattern detection\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>SRE \u002F guardrails\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Cost monitoring, runaway detection, alerting, production write guardrail\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Documentation self-maintenance\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Doc↔code mirroring, blind-spot coverage, deterministic dead-path repair, deep regeneration, publication to synedre.com\u003C\u002Ftd>\n      \u003Ctd>See §7\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Autonomy\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Daily workstream seeder in automatic mode; bootstraps the agent task pipeline\u003C\u002Ftd>\n      \u003Ctd>See §9\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>Libraries \u002F infra\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Shared building blocks (DB connection, logger, environment variables, AI provider, log rotation, cron wrapper) — not scheduled\u003C\u002Ftd>\n      \u003Ctd>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch2 id=\"wrapper-cron\">3. The cron wrapper — supervisory watchdog\u003C\u002Fh2>\n\n\u003Cp>Every automation scheduled via crontab is launched \u003Cstrong>through a supervisory wrapper\u003C\u002Fstrong>. This component is the stability guardian of the cron layer: it shields each script against error loops, attempts automatic repairs, and logs every incident to the database.\u003C\u002Fp>\n\n\u003Cp>A typical invocation looks like:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>*\u002F2 * * * *  &lt;wrapper-path&gt; &lt;script-name&gt; &gt;&gt; &lt;log-file&gt; 2&gt;&amp;1\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>The wrapper's responsibilities are as follows:\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Environment variable loading\u003C\u002Fstrong>: the wrapper reads the \u003Ccode>.env\u003C\u002Fcode> and \u003Ccode>.env.host\u003C\u002Fcode> files at startup and injects the variables into the subprocess environment without overwriting values that are already set.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Circuit breaker\u003C\u002Fstrong>: before each launch, the wrapper queries the error log in the database. If a script exceeds \u003Cstrong>10 consecutive failures\u003C\u002Fstrong>, it is automatically disabled (the wrapper exits without executing the script). Only a single line is logged at the moment the threshold is crossed — an anti-spam measure that prevents hundreds of repetitive lines per day.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Supervised execution\u003C\u002Fstrong>: the script is launched as a subprocess with a default timeout of \u003Cstrong>300 seconds\u003C\u002Fstrong>. A list of long-running scripts (complex LLM spawns, etc.) benefits from an extended timeout of up to \u003Cstrong>1,800 seconds\u003C\u002Fstrong>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Self-repair\u003C\u002Fstrong>: on error, the wrapper analyses standard error output, classifies the incident, and attempts an automatic fix before re-running:\n    \u003Cul>\n      \u003Cli>\u003Cem>Missing import\u003C\u002Fem> → injection of the import from a known list\u003C\u002Fli>\n      \u003Cli>\u003Cem>Log permission error\u003C\u002Fem> → redirection to an accessible directory\u003C\u002Fli>\n      \u003Cli>\u003Cem>Directory not found\u003C\u002Fem> → directory creation (sandboxed under the repository root)\u003C\u002Fli>\n      \u003Cli>\u003Cem>Absent Python module\u003C\u002Fem> → installation via \u003Ccode>pip\u003C\u002Fcode> into the virtual environment\u003C\u002Fli>\n      \u003Cli>\u003Cem>Log variable name conflict\u003C\u002Fem> → automatic renaming of the conflicting alias\u003C\u002Fli>\n      \u003Cli>\u003Cem>Network error\u003C\u002Fem> → 3 retries with exponential backoff (2 s, 5 s, 10 s)\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Soft-fail for audits\u003C\u002Fstrong>: audit scripts are declared in a special list. For these, a non-zero exit code means \u003Cem>findings were detected\u003C\u002Fem>, not a crash. The consecutive error counter is reset and the exit code is propagated to downstream consumers.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Database logging\u003C\u002Fstrong>: each incident is recorded in the cron error log table (append-only table) with the following fields: error type, traceback, automatic-fix indicator, description of the applied fix, retry success, consecutive failure count, disabled status.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Caution — no lock\u003C\u002Fstrong>: the wrapper itself does not implement an exclusive lock (\u003Ccode>flock\u003C\u002Fcode>). A few crons add a lock manually (notably the brainstorm workers and certain punctual extractors). The \u003Cstrong>majority of wrapper entries have no overlap protection\u003C\u002Fstrong>: safety relies solely on the timeout and scheduling frequency. This is an architectural debt to be addressed.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>The database connection used by the wrapper goes through the repository's shared connection facade, which points to the mothership's primary database. The MariaDB connection constants present in the code are \u003Cstrong>dead legacy\u003C\u002Fstrong> (removed during a previous infrastructure workstream) and can be ignored.\u003C\u002Fp>\n\u003Ch2 id=\"systeme-runs\">4. The Run System\u003C\u002Fh2>\n\n\u003Ch3>4.1 The Run — Scoped Execution Unit\u003C\u002Fh3>\n\n\u003Cp>A \u003Cstrong>run\u003C\u002Fstrong> designates an execution driven by the central orchestrator over a defined scope (mother-ship or client). At startup, the scope automatically loads its context from the database: infrastructure, contact, inbox.\u003C\u002Fp>\n\n\u003Cp>The runs table contains the following columns: identifier, source, trigger, scope, title, status, type and reference identifier, start and end timestamps.\u003C\u002Fp>\n\n\u003Cp>Two triggers are active in production:\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Trigger\u003C\u002Fth>\n      \u003Cth>Source\u003C\u002Fth>\n      \u003Cth>Entry point\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>email\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Atlas inbox\u003C\u002Ftd>\n      \u003Ctd>Forwarded to the orchestrator's Atlas address, classified by intent (run, question, workstream, noise)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>chat\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Console\u003C\u002Ftd>\n      \u003Ctd>Scoped console accessible from the runs dashboard (scope selection + Atlas conversation)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>A third scheduled trigger (\u003Ccode>cron\u003C\u002Fcode>) is documented in doctrine but is not yet active in the database.\u003C\u002Fp>\n\n\u003Cp>The run management interface is accessible from the orchestrator's main dashboard, which exposes the list of runs and a detailed view per item.\u003C\u002Fp>\n\n\u003Ch3>4.2 The Agent Execution Task — Delegated Unit\u003C\u002Fh3>\n\n\u003Cp>\u003Cstrong>The agent execution task is not a run.\u003C\u002Fstrong> It is the unit of work delegated to an agent by the orchestrator. It lives under the tasks dashboard and under workstreams. A run \u003Cem>may spawn\u003C\u002Fem> an agent execution task when the orchestrator delegates.\u003C\u002Fp>\n\n\u003Cp>The execution tasks table (approximately 209 rows) contains the following key columns: agent identifier, title, prompt, template, scope, exit criteria, status, creator, output log, exit code, tokens consumed, cost in USD, model used.\u003C\u002Fp>\n\n\u003Cp>Observed statuses:\u003C\u002Fp>\n\u003Cul>\n  \u003Cli>\u003Cstrong>Completed\u003C\u002Fstrong>: 182 tasks\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Failed\u003C\u002Fstrong>: 23 tasks\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Cancelled\u003C\u002Fstrong>: 4 tasks\u003C\u002Fli>\n  \u003Cli>Transient states: pending, running\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Execution templates: \u003Ccode>code\u003C\u002Fcode> (208 tasks), \u003Ccode>audit\u003C\u002Fcode> (1 task).\u003C\u002Fp>\n\n\u003Cp>Observed creation origins: automatic task executor chain (121), manual operator trigger (37), automated workstream launch API (29), automatic work chain (22).\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Executor:\u003C\u002Fstrong> a worker scheduled at one-minute intervals consumes pending tasks. Its logic: it selects tasks with \u003Cem>pending\u003C\u002Fem> status in age order, marks them as \u003Cem>running\u003C\u002Fem>, spawns the subprocess according to the template, streams standard output to the log, then marks them as \u003Cem>completed\u003C\u002Fem> or \u003Cem>failed\u003C\u002Fem>.\u003C\u002Fp>\n\n\u003Cp>Two execution modes:\u003C\u002Fp>\n\u003Cul>\n  \u003Cli>\u003Cstrong>Simulation mode\u003C\u002Fstrong> (default): no real spawn, placeholder recorded.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Live mode\u003C\u002Fstrong>: spawns an inference process. Granted permissions vary by template:\n    \u003Cul>\n      \u003Cli>\u003Cem>research\u003C\u002Fem> template: read-only (files, search, revision history).\u003C\u002Fli>\n      \u003Cli>\u003Cem>audit\u003C\u002Fem> template: read + report write.\u003C\u002Fli>\n      \u003Cli>\u003Cem>code\u003C\u002Fem> template: extended permissions, sandboxed to the declared scope directory.\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>4.3 The Automaton Execution Log\u003C\u002Fh3>\n\n\u003Cp>The automaton execution log (approximately 124,000 rows, continuously growing, append-only mode) verbosely traces each automaton execution. Columns: automaton name, environment, result, duration in seconds, step count, error count, warning count, counters, step detail, errors, warnings, result detail, context.\u003C\u002Fp>\n\n\u003Cp>This log is distinct from the wrapper crash registry, which records only envelope-level (wrapper) failures — not nominal executions.\u003C\u002Fp>\n\n\u003Cp>Component relationships:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>Orchestrator run (scoped)\n   └─ spawns ─► Agent execution task ──log──► output log (output_log)\n\nAutomaton registry ──scheduled execution──► Verbose automaton log\n                                        └─► Wrapper crash registry (crashes only)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Chr>\n\n\u003Ch2 id=\"ordonnancement\">5. Scheduling: Two Parallel Schedulers\u003C\u002Fh2>\n\n\u003Ch3>5.1 Nitro Application Scheduler (Mother-Ship Layer)\u003C\u002Fh3>\n\n\u003Cp>The mother-ship application exposes a task scheduler integrated into the application framework (experimental feature enabled in the configuration). Tasks belonging to the \u003Cem>audit\u003C\u002Fem> family are defined in a dedicated module automatically merged by the framework — they do not reside in the root tasks directory.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>History:\u003C\u002Fstrong> this scheduler was inactive for approximately three days in May 2026, then restored as part of a reactivation workstream. Tasks have been active since then, provided that a container rebuild is triggered after any configuration change (the container must be rebuilt to reflect changes).\u003C\u002Fp>\n\n\u003Cp>Active tasks:\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Role\u003C\u002Fth>\n      \u003Cth>Cron frequency\u003C\u002Fth>\n      \u003Cth>Guard\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Email queue processing\u003C\u002Ftd>\n      \u003Ctd>Every 2 min\u003C\u002Ftd>\n      \u003Ctd>—\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Availability monitoring\u003C\u002Ftd>\n      \u003Ctd>Every 15 min\u003C\u002Ftd>\n      \u003Ctd>Internal environment only\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Dictionary watch\u003C\u002Ftd>\n      \u003Ctd>Every 30 min\u003C\u002Ftd>\n      \u003Ctd>Internal environment only\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Dependency watch\u003C\u002Ftd>\n      \u003Ctd>Daily at 2:00\u003C\u002Ftd>\n      \u003Ctd>Internal environment only\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Daily digest\u003C\u002Ftd>\n      \u003Ctd>Daily at 8:00\u003C\u002Ftd>\n      \u003Ctd>Internal environment only\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>SSL monitoring\u003C\u002Ftd>\n      \u003Ctd>Daily at 9:00\u003C\u002Ftd>\n      \u003Ctd>Internal environment only\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Brand watch\u003C\u002Ftd>\n      \u003Ctd>Daily at 12:00\u003C\u002Ftd>\n      \u003Ctd>Internal environment only\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>The \u003Cem>internal environment only\u003C\u002Fem> guard short-circuits the task if an activation variable is not set in the container environment (passed via the host environment file).\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Still-disabled tasks:\u003C\u002Fstrong> inbox synchronisation and client email synchronisation. The current IMAP client monopolises the Node event loop and causes cascading database timeouts; reactivation is contingent on the delivery of a non-blocking IMAP client (workstream in progress, phase B).\u003C\u002Fp>\n\n\u003Cp>Tasks present in the codebase but \u003Cstrong>intentionally unscheduled\u003C\u002Fstrong>: blog hygiene, page audit, sitemap watch, Synedre watch, fact checking, schema watch.\u003C\u002Fp>\n\n\u003Ch3>5.2 System Crontab Safety Net\u003C\u002Fh3>\n\n\u003Cp>The server's system crontab carries the bulk of the scheduling load and constitutes a safety net independent of the application scheduler. Out of 175 total lines (state as of 7 June 2026), \u003Cstrong>55 are active\u003C\u002Fstrong>. Commented lines carry archaeology markers (temporary freeze, explicit deactivation) — the crontab serves as an operational logbook.\u003C\u002Fp>\n\n\u003Cp>Active families:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Via monitoring wrapper:\u003C\u002Fstrong> monitoring (every 2 min), backup (4h), automaton audit (3h), backup audit (4h30), bank synchronisation (6h15), recurring billing (6h30), Atlas inbox polling (every min), orchestration spawn (every 5 min with delay), inbox synchronisation (every min), nightly documentation maintenance loop (4h), documentation drift detection (every 6h), automatic documentation correction (every 6h, +10 min), nightly maintenance loop (5h), autonomy loop (5h), embeddings synchronisation (5h40).\n    \u003Cbr>\u003Cem>These scripts benefit from centralised wrapper error logging and the self-repair mechanism.\u003C\u002Fem>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Outside wrapper, Python modules:\u003C\u002Fstrong> memory indexing (every 15 min), live-mode agent execution task worker (every min), skill indexing (every 15 min), deadlock detector (every 15 min), SRE alerts (every 30 min), cost alerts (every 6 min), drift detector (every 15 min), negotiation event extractor with flock locking (every 2 min), lead analysis (7:30 and 19:30), KPI refresh (5:15), Google review synchronisation (1:20), external document review processing (every 30 min).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Outside wrapper, direct Python scripts:\u003C\u002Fstrong> document publication with limit (3:30), backup restoration test (5h on the 1st of the month), log rotation (6h), memory metrics (6h), session indexing (every hour), skill proposal monitoring (every hour), skill proposal detection (4h on Sundays), pattern detection (3h on Sundays), daily reaction digest (3:30).\n    \u003Cblockquote>\u003Cstrong>⚠️ Warning:\u003C\u002Fstrong> these scripts run without a monitoring wrapper. They do not benefit from centralised wrapper error logging or the self-repair mechanism.\u003C\u002Fblockquote>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Brainstorm worker:\u003C\u002Fstrong> launched at server startup in persistent loop mode (detached process); a safety net every 2 min restarts the process if absent; a safety net every 3 min with flock locking catches missed one-off executions.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Shell and Node scripts:\u003C\u002Fstrong> fleet scan (4:30), TypeScript dependency analysis (4:05), primary database backup to S3 object storage (3:30), file backup to S3 (3h), client VPS database backups to S3 at staggered times — all with notification fallback on failure. Monthly restoration test (1h on the 1st of the month), memory synchronisation (every 30 min), workstream lock cleanup (every 5 min), orchestrator lock watchdog (every 15 min).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Direct HTTP calls:\u003C\u002Fstrong> mail queue drain every minute via POST to the local API (redundancy with the corresponding Nitro task); session replay synchronisation every 5 minutes.\n    \u003Cblockquote>\u003Cstrong>⚠️ P0 Debt:\u003C\u002Fstrong> one of these crontab lines exposes an authentication token in plaintext in the command. This token must be removed from the crontab line and loaded from an environment variable or a secured file — corrective action to be scheduled.\u003C\u002Fblockquote>\n  \u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Memory consolidation:\u003C\u002Fstrong> monthly consolidation script (midnight on the 1st of the month, with 600 s flock locking).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>OSS quarantine:\u003C\u002Fstrong> purge of quarantined packages according to a defined calendar window.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Existing scripts \u003Cstrong>removed from the crontab\u003C\u002Fstrong> (active facades, unscheduled): founder notification, sector watch — no active or commented line in the current state of the crontab.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cstrong>⚠️ Registry ↔ Scheduling reconciliation (audit to produce):\u003C\u002Fstrong> the canonical automaton registry is the authoritative classification source, but no automated mapping currently links each \u003Cem>recurring\u003C\u002Fem>-type automaton to its crontab line or Nitro task. It is impossible to determine how many automata are \u003Cstrong>orphaned\u003C\u002Fstrong> (registered as recurring but unscheduled). An audit is required: join between the automaton registry (filtered on recurring type), the active lines of the system crontab, and the tasks declared in the application scheduler configuration.\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"browser-worker-automate-distribue\">The browser-worker: distributed automaton outside the datacenter\u003C\u002Fh2>\n\n\u003Cp>The web navigation subsystem is the only platform component whose execution physically leaves the datacenter. Two distinct reasons justify this exit, and therefore \u003Cstrong>two egress topologies\u003C\u002Fstrong> that must not be confused.\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Topology\u003C\u002Fth>\n\u003Cth>Component\u003C\u002Fth>\n\u003Cth>Where the browser runs\u003C\u002Fth>\n\u003Cth>What it handles\u003C\u002Fth>\n\u003Cth>Mode\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Residential proxy\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Browser agent (VPS side)\u003C\u002Ftd>\n\u003Ctd>Headless browser on the mothership VPS\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>IP reputation\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Headless + stealth profile\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Remote headful worker\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Browser worker (residential machine side)\u003C\u002Ftd>\n\u003Ctd>Headful Chrome on the residential host machine\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>Browser fingerprint\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>Visible window, direct residential IP\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch3>6.1 Why a residential headful browser rather than a headless browser on a VPS\u003C\u002Fh3>\n\n\u003Cp>A residential proxy tunnel routes traffic from the VPS through a residential IP address. This is sufficient for sites that discriminate \u003Cstrong>solely on datacenter IP reputation\u003C\u002Fstrong>. A guard mechanism verifies before each launch that the proxy is active \u003Cem>and\u003C\u002Fem> that the effective egress IP address is not that of the datacenter — any accidental egress through the datacenter is blocked.\u003C\u002Fp>\n\n\u003Cp>However, certain third-party protection systems (notably managed challenges of the Turnstile type) do \u003Cstrong>not\u003C\u002Fstrong> evaluate the IP: they analyse the \u003Cstrong>browser fingerprint\u003C\u002Fstrong> — JavaScript signals revealing automation (\u003Ccode>navigator.webdriver\u003C\u002Fcode>, absence of plugins, headless markers, WebRTC, etc.). A headless browser retains a bot fingerprint even behind a clean residential IP.\u003C\u002Fp>\n\n\u003Cp>Doctrinal consequence: \u003Cstrong>classify the protection type before coding the flow\u003C\u002Fstrong>. If the protection targets the browser fingerprint, a headful browser on a residential machine is mandatory; stealth headless mode does not pass.\u003C\u002Fp>\n\n\u003Cp>Hence the headful worker: a real Chrome instance (visible window, non-headless mode) runs permanently on a residential host machine that is always on, with a direct residential IP. No proxy is needed on the worker side: the correct IP is already in use, and Turnstile-type challenges pass naturally.\u003C\u002Fp>\n\n\u003Ch3>6.2 Job queue and lifecycle of a browser job\u003C\u002Fh3>\n\n\u003Cp>The browser job queue is stored in the database following the same enqueue \u002F claim \u002F finish \u002F get pattern as the other platform workers. Each entry carries: a job identifier, a type (\u003Cem>kind\u003C\u002Fem>), a JSON payload, a status (\u003Ccode>queued\u003C\u002Fcode>, \u003Ccode>running\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>failed\u003C\u002Fcode>), a JSON result, an error message, the identifier of the executing machine, the retry count, and start and end timestamps. Indexes guarantee the performance of ordered claiming.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Inversion of control is the central principle\u003C\u002Fstrong>: the mothership VPS has no inbound access to the residential machine (NAT, dynamic IP). It is the remote worker that \u003Cstrong>actively polls\u003C\u002Fstrong> the VPS via an outbound connection. The complete cycle is as follows:\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>\u003Cstrong>Enqueue (VPS)\u003C\u002Fstrong> — An agent or a skill deposits a job into the queue. Only job types appearing on a strict whitelist are accepted; the JSON payload is validated before insertion. SQL injection is prevented by a random dollar-quoting mechanism.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Atomic claim (worker → VPS)\u003C\u002Fstrong> — Every 5 seconds, the worker queries the VPS via outbound SSH to claim the oldest job in \u003Ccode>queued\u003C\u002Fcode> status. The claim uses a transactional lock (\u003Ccode>FOR UPDATE SKIP LOCKED\u003C\u002Fcode>): two concurrent workers cannot claim the same job.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Dispatch (worker, headful)\u003C\u002Fstrong> — The worker routes the job to the corresponding business handler according to its type. The payload contains only business parameters (limits, quantities); no dynamic code evaluation is performed.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>2FA code retrieval (worker → VPS → worker)\u003C\u002Fstrong> — For certain flows requiring two-factor authentication, the code never arrives directly on the residential machine. The worker calls back the VPS via SSH so that the VPS reads the code from the server-side mailbox and returns it in plaintext on standard output. The 2FA code is never written to logs.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Finish (worker → VPS)\u003C\u002Fstrong> — Once the job is complete, the worker transmits the result and any error as a single JSON object passed on the standard input of the SSH command (never as a shell argument, to avoid breakage on special characters). The VPS updates the status, the result, and the end timestamp.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Worker self-update\u003C\u002Fstrong> — In loop mode, when the worker is idle (never mid-job), it periodically checks whether the repository has been updated. If an update is detected, it restarts automatically via process replacement (\u003Ccode>os.execv\u003C\u002Fcode>), ensuring that the residential machine always runs the latest code without manual intervention.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Ch3>6.3 The SSH command portal (defence in depth should the key ever leak)\u003C\u002Fh3>\n\n\u003Cp>The SSH key of the residential machine is registered on the VPS side with a strict restriction: it does \u003Cstrong>not\u003C\u002Fstrong> open a free shell. Instead, each SSH connection is intercepted by a dedicated command portal, configured directly in the SSH authorization file.\u003C\u002Fp>\n\n\u003Cp>This portal reads the requested SSH command, tokenises it safely (without ever passing through a shell interpreter), and only permits execution if:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>the command prefix matches \u003Cstrong>exactly\u003C\u002Fstrong> the job queue management module;\u003C\u002Fli>\n\u003Cli>the subcommand belongs to the authorised set (\u003Ccode>--claim\u003C\u002Fcode>, \u003Ccode>--finish\u003C\u002Fcode>, \u003Ccode>--get\u003C\u002Fcode>, \u003Ccode>--enqueue\u003C\u002Fcode>, 2FA code retrieval);\u003C\u002Fli>\n\u003Cli>each parameter value matches a strict predefined pattern (numeric identifier, \u003Ccode>done|failed\u003C\u002Fcode> status, bounded alphanumeric job type, etc.).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Any unknown token or non-conforming value results in an immediate rejection with logging. The worker never prefixes its SSH commands with a directory change: the portal manages the working directory itself. \u003Cstrong>In the worst case of a compromised key\u003C\u002Fstrong>, the attacker can at most pollute the job queue — they cannot execute arbitrary code on the VPS.\u003C\u002Fp>\n\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>Personal data and screenshots.\u003C\u002Fstrong> The results of certain jobs (names, message excerpts) are stored in the private database as structured JSON and are never written in plaintext to logs — only a counter is recorded. Debug screenshots are purged at the end of the run by the relevant business module, not by the worker itself. A \u003Cem>screenshot retention\u003C\u002Fem> mode exists but is reserved for local debugging. Structured retrieval of screenshots is the subject of an ongoing workstream.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>⚠️ Known latent bug:\u003C\u002Fstrong> the expiry timeout for browser jobs is not currently enforced — a job whose browser hangs may remain in \u003Ccode>running\u003C\u002Fcode> status indefinitely. A fix is pending confirmation.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"boucle-auto-maintenance-doc\">Documentation Self-Maintenance Loop\u003C\u002Fh2>\n\n\u003Cp>Since a recent project, the harness includes a coherent subsystem that measures the gap between its own documentation and its code, then corrects it autonomously using validation gates (\u003Cem>gates\u003C\u002Fem>). This subsystem constitutes the most recent and most intricate layer of automation.\u003C\u002Fp>\n\n\u003Ch3>Pipeline Architecture\u003C\u002Fh3>\n\n\u003Cp>The nightly loop is structured around a central orchestrator that triggers every morning at 5:00 AM and chains eight steps in sequence:\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Perceive\u003C\u002Fstrong> — the drift detector inspects the gap between documentation and code.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Cover\u003C\u002Fstrong> — the blind-spot detector checks documentation completeness.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Repair\u003C\u002Fstrong> — the deterministic fixer resolves dead references.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Assess\u003C\u002Fstrong> — the health report aggregates all dimensions.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Regenerate\u003C\u002Fstrong> — the deep regenerator rewrites in depth (with gate).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Propose\u003C\u002Fstrong> — the staging module prepares chapters and produces a brief.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Publish-doc\u003C\u002Fstrong> — the publication module validates and activates chapters.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Publish\u003C\u002Fstrong> — the public synchronization module pushes a sanitized snapshot to the public site.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>Additionally, the drift detector and the mechanical fixer each run on their own independent schedule (every six hours), without depending on the orchestrator. An external review module runs every thirty minutes.\u003C\u002Fp>\n\n\u003Ch2 id=\"detecteur-derive\">The Drift Detector (Phase 0 — Perceive)\u003C\u002Fh2>\n\n\u003Cp>This component runs every six hours in \u003Cstrong>strict read-only mode\u003C\u002Fstrong>: it never writes to code or documentation, but records its observations solely in the database.\u003C\u002Fp>\n\n\u003Cp>It measures three types of drift:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Stale doc\u003C\u002Fstrong> — the referenced source file was modified after the document was written: the documentation describes an outdated body.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Dead reference\u003C\u002Fstrong> — the document cites a file that no longer exists in the repository.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Delayed publication\u003C\u002Fstrong> — the public chapter differs from the internal document (content divergence detected by fingerprint).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>The component is idempotent: it produces only one observation per chapter per day. It returns either a no-drift signal or a drift-detected signal.\u003C\u002Fp>\n\n\u003Ch2 id=\"detecteur-angles-morts\">The Blind-Spot Detector (Phase 0bis — Coverage)\u003C\u002Fh2>\n\n\u003Cp>Launched immediately after the drift detector, this component checks documentation \u003Cstrong>completeness\u003C\u002Fstrong> — whereas the drift detector checks \u003Cstrong>fidelity\u003C\u002Fstrong>. The question it poses is: \"Does any part of the harness go entirely unmentioned in the documentation?\"\u003C\u002Fp>\n\n\u003Cp>The method relies on a set difference:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Documentable body\u003C\u002Fstrong> — the set of all automations and data structures in the harness.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Current coverage\u003C\u002Fstrong> — those mentioned in at least one internal documentation chapter.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Blind spots\u003C\u002Fstrong> — the difference: components present but never documented, grouped by family.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>An isolated orphan is not a concern. However, multiple orphans from the same family constitute a candidate for a new section or chapter, created by the deep regenerator in creation mode. This component is also \u003Cstrong>read-only\u003C\u002Fstrong> and its \"coverage\" dimension is consolidated into the health report.\u003C\u002Fp>\n\n\u003Ch2 id=\"correcteur-deterministe\">The Deterministic Dead-Reference Fixer (Phase 1bis — Repair)\u003C\u002Fh2>\n\n\u003Cp>This component bridges the gap between the drift detector — which diagnoses dead references — and the LLM regenerator — which corrects prose but is unaware that a file has been moved. Resolution is \u003Cstrong>deterministic, with no possibility of hallucination\u003C\u002Fstrong>.\u003C\u002Fp>\n\n\u003Cp>For each dead reference found in the internal documentation, the component searches for the actual location by base filename:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Single result\u003C\u002Fstrong> (certainty) → the reference is corrected in place.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>No result\u003C\u002Fstrong> (component deleted) → flagged, reported, and the founder is notified for human review.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Multiple results\u003C\u002Fstrong> (ambiguity) → flagged and reported, \u003Cstrong>no automatic rewrite under any circumstances\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Safety constraints: this component only touches internal documentation files — never code, never published chapters. It is idempotent and produces a single, reversible commit per run.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Important distinction:\u003C\u002Fstrong> this fixer handles \u003Cem>paths only\u003C\u002Fem> in a deterministic manner and is triggered by the orchestrator. The mechanical fixer described below attempts other types of structural corrections and runs on its own independent schedule.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch2 id=\"bilan-sante\">The Health Report (Phase 2 — Assess)\u003C\u002Fh2>\n\n\u003Cp>This component aggregates four dimensions without modifying any of them:\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Dimension\u003C\u002Fth>\n      \u003Cth>What is measured\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Proprioception\u003C\u002Ftd>\n      \u003Ctd>Documentation ↔ code gap (drift detected by the drift detector)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Debt\u003C\u002Ftd>\n      \u003Ctd>Backlog of open priority tasks\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Learning\u003C\u002Ftd>\n      \u003Ctd>Signal derived from accumulated scars\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Automations\u003C\u002Ftd>\n      \u003Ctd>Cron health (detected errors)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>The health report produces an idempotent snapshot per dimension per day, as well as an item for the daily summary. It returns a signal: OK, warning, or critical state.\u003C\u002Fp>\n\n\u003Ch2 id=\"correcteur-mecanique\">The Mechanical Fixer (Independent of the Orchestrator)\u003C\u002Fh2>\n\n\u003Cp>This component runs every six hours on its own schedule, offset by ten minutes relative to the drift detector. It only touches internal documentation files — never code, never published chapters.\u003C\u002Fp>\n\n\u003Cp>Safety rule: a dead reference is corrected only if its base filename matches \u003Cstrong>exactly one\u003C\u002Fstrong> file tracked by the repository. Zero candidates (component deleted) or two or more candidates (ambiguity) → submission for human validation, with no automatic write under any circumstances.\u003C\u002Fp>\n\n\u003Cp>The component is reversible (a single commit per run, undoable in one operation), capped by a configurable maximum, and equipped with a global circuit breaker. It operates in simulation mode by default; active mode must be explicitly requested.\u003C\u002Fp>\n\n\u003Ch2 id=\"regenerateur-profond\">The Deep Regenerator (Phase 4 — Regenerate)\u003C\u002Fh2>\n\n\u003Cp>This component is called by the orchestrator with a maximum time budget (default: 4,500 seconds). It launches a headless Claude process that: (a) rewrites the internal document by reading the actual code, (b) directly produces the bilingual FR+EN public version with its metadata in a temporary workspace.\u003C\u002Fp>\n\n\u003Cp>The internal document is committed, then an anti-leak gate (multi-layer regex verification) is applied before any public staging. This gate is independent of the orchestrator.\u003C\u002Fp>\n\n\u003Ch2 id=\"staging-brief\">The Staging and Brief Module (Phase 5 — Propose)\u003C\u002Fh2>\n\n\u003Cp>Launched by the orchestrator after deep regeneration, this component re-stages chapters whose internal document is ahead of the published version, triggers unit publication, and places sanitized bilingual chapters into the publication queue. An independent anti-leak gate blocks any chapter containing a detected leak. The component also produces a regeneration brief for ambiguous cases requiring human attention.\u003C\u002Fp>\n\n\u003Ch2 id=\"orchestrateur\">The Nightly Orchestrator (5:00 AM Cron)\u003C\u002Fh2>\n\n\u003Cp>The orchestrator triggers the eight steps in sequence, under several global safeguards:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Overlap lock\u003C\u002Fstrong> — only one instance at a time.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Database circuit breaker\u003C\u002Fstrong> — if the kill-switch is activated for this automation, the tick stops immediately.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Configurable time budget\u003C\u002Fstrong> for the deep regenerator; a broader external timeout encompasses the entire run.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Step isolation\u003C\u002Fstrong> — if a step fails, the tick continues and records the failure.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>At the end of the run, the orchestrator calls the public synchronization module to push a sanitized snapshot to the public site.\u003C\u002Fp>\n\n\u003Cp>Typical invocation (with simulation or without deep regeneration):\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>python3 [orchestrateur] [--dry-run] [--no-deep] [--deep-budget-sec N]\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2 id=\"sync-public\">Public Synchronization\u003C\u002Fh2>\n\n\u003Cp>Called at the end of the tick, this component reads loop data in read-only mode, sanitizes it (zero client names, IP addresses, secrets, internal paths), then pushes a JSON snapshot to the public site database via idempotent upsert. This snapshot feeds the maintenance dashboard accessible from the documentation section of the site.\u003C\u002Fp>\n\n\u003Ch2 id=\"publication-chapitres\">Chapter Publication to the Public Site\u003C\u002Fh2>\n\n\u003Cp>This component is invoked via two paths:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Autonomous scheduling\u003C\u002Fstrong> every night at 3:30 AM, with automatic activation.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Orchestrator invocation\u003C\u002Fstrong> at the \"publish-doc\" step, with the same parameters — making the orchestrator self-sufficient without depending on the separate cron.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>The component detects chapters whose internal document fingerprint diverges from that of the published chapter, re-stages them into the publication queue (secure transport to the public site database), with a multi-layer regex anti-leak pre-check.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Activation logic:\u003C\u002Fstrong> a deliberate human gesture (\u003Ccode>--activate\u003C\u002Fcode>) or automatic activation (\u003Ccode>--auto-activate\u003C\u002Fcode>) can publish a pending chapter. In automatic mode, chapters passing all machine gates — including the anti-guru verification defined in public register §VII — are published without human intervention. Rejected chapters remain in the queue and generate a notification to the founder.\u003C\u002Fp>\n\n\u003Ch2 id=\"revue-externe\">External Review — Every 30 Minutes\u003C\u002Fh2>\n\n\u003Cp>This component runs every thirty minutes independently of the orchestrator. It dequeues reviews submitted by a human operator (response from an external model), evaluates them via a sandboxed Claude process with anti-injection delimiters, then selectively parses the verdict: overall validity and injection-attempt detection.\u003C\u002Fp>\n\n\u003Cp>If the review is valid, the component emits a \u003Cstrong>signal only\u003C\u002Fstrong> (a note in the daily summary) without triggering an inline regeneration. Regeneration is consumed during the next orchestrator cycle, following a loose coupling pattern that avoids timeouts and wasted computation.\u003C\u002Fp>\n\n\u003Ch2 id=\"tick-autonomie\">The Autonomy Tick — Pipeline Bootstrap\u003C\u002Fh2>\n\n\u003Cp>This component runs daily at 5:00 AM (simultaneously but independently of the documentation orchestrator). Its doctrine: \u003Cstrong>simulation by default\u003C\u002Fstrong> — it observes without seeding anything; active mode must be explicitly requested.\u003C\u002Fp>\n\n\u003Cp>Its role is to address the \u003Cstrong>cold-start\u003C\u002Fstrong> of the autonomy pipeline. The task engine chains itself \u003Cem>within\u003C\u002Fem> an active job, but nothing bootstraps the pipeline: a job in automatic mode whose tasks are all pending and whose no run is active remains dormant. This tick creates a pending run for the next eligible task of each active job with no current run.\u003C\u002Fp>\n\n\u003Cp>Safeguards:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Cost ceiling\u003C\u002Fstrong> — if the cumulative cost of a project reaches 100% of its budget, it is frozen and no run is seeded.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Dual circuit breaker\u003C\u002Fstrong> — two environment variables allow the component to be fully halted without any action.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Deployment\u003C\u002Fstrong> — signaled only, never executed within the tick; production deployment remains a deliberate human gesture.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Constitutional rule:\u003C\u002Fstrong> the tick may proceed up to pre-production (\u003Ccode>.\u002Fdeploy\u003C\u002Fcode>) but \u003Cstrong>never calls production deployment\u003C\u002Fstrong> (\u003Ccode>.\u002Fship\u003C\u002Fcode>). The production deployment decision remains an explicit human gesture.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"garde-fous-operations\">Safeguards &amp; Operations\u003C\u002Fh2>\n\n\u003Cp>This section lists the protection, monitoring, and operational maintenance mechanisms that govern the execution of automations on the mother ship.\u003C\u002Fp>\n\n\u003Ch3>Active Protection Mechanisms\u003C\u002Fh3>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Automatic circuit breaker:\u003C\u002Fstrong> a supervision component wraps each scheduled automation. After ten consecutive crashes, it automatically disables the automation in question and records the state in the scheduling error tracking table, thereby preventing any infinite failure loop.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Daily audit:\u003C\u002Fstrong> every night, an audit automation re-reads the scheduling error logs as well as the automation execution logs in order to detect any persistent anomaly.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Cost and runaway monitoring:\u003C\u002Fstrong> three watchdog automations run continuously — one every six hours to alert on cost drift, another every fifteen minutes to detect runaway executions, and a third every thirty minutes for service reliability alerts.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Backups and restore testing:\u003C\u002Fstrong> nightly dumps are pushed to remote object storage (mother-ship database, files, remote client databases). A monthly automation replays a full restore test to validate backup consistency.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Automatic unblocking:\u003C\u002Fstrong> every fifteen minutes, an automation detects stuck task executions and resubmits them, up to a maximum of four attempts. An emergency switch allows this mechanism to be disabled without modifying the scheduler.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Documentation maintenance loop:\u003C\u002Fstrong> a set of three automations handles documentation drift monitoring (every six hours), automatic correction of detected anomalies (offset by ten minutes on the same cycle), and a nightly maintenance dial (each day at 5 a.m.). These mechanisms are described in detail in the section dedicated to documentation regeneration.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>Reported Technical Debt\u003C\u002Fh3>\n\n\u003Cul>\n  \u003Cli>\n    \u003Cstrong>✅ Audit automation removed without a replacement (resolved 2026-06-06):\u003C\u002Fstrong> a URL-leak audit automation had been removed from the repository without a successor being put in place. The corresponding scheduling entry was deleted from the scheduler. Only a folder of stale logs remains. No URL-leak audit facade currently exists; it will need to be recreated via the standard scheduling wrapper if the need arises again.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Authentication token in plaintext in the scheduler:\u003C\u002Fstrong> a scheduler entry embeds a bearer token directly in the command line, bypassing the secure environment file. Migration to environment-variable-based retrieval is required — the clean pattern is already applied on other scheduler entries (reading the database password from the \u003Ccode>.env\u003C\u002Fcode> file).\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Duplicate email queue execution:\u003C\u002Fstrong> two scheduling entries independently trigger the email send queue processing every minute. The mechanism is idempotent thanks to a \u003Ccode>LIMIT\u003C\u002Fcode> clause on the queue side, but the redundancy still needs to be cleaned up to avoid any operational confusion.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Case inconsistency on the automation classification field:\u003C\u002Fstrong> the field designating the execution type shows non-uniform capitalisation (\u003Ccode>execution\u003C\u002Fcode> vs \u003Ccode>Execution\u003C\u002Fcode>) in the automations table. Normalisation is to be scheduled.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ No generic lock in the scheduling wrapper:\u003C\u002Fstrong> the wrapper that encapsulates scheduled automations does not acquire a system-level lock by default. Execution overlaps remain possible on slow jobs that do not handle their own mutual exclusion.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>⚠️ Autonomous seeding automation still in observation mode:\u003C\u002Fstrong> the autonomous seeding automation is currently running in \u003Cem>dry-run\u003C\u002Fem> mode — it observes and logs without performing any real actions. Active mode is not yet wired into the scheduler. Additionally, a safeguard timeout on browser jobs is defined in the code but not enforced at runtime: a browser job could theoretically remain suspended indefinitely. A fix is to be confirmed.\n  \u003C\u002Fli>\n\u003C\u002Ful>",{"slug":14,"chapterNum":15,"title":16,"titleEn":17},"chantiers","04","Chantiers, travaux & tâches — modèle de données et API","Worksites, Jobs & Tasks — Data Model and API",{"slug":19,"chapterNum":20,"title":21,"titleEn":22},"hub","06","Le Hub (\u002Fhub\u002F*)","The Hub (\u002Fhub\u002F*)",{"chapters":24},[25,32,39,46,49,50,53,60,67,74,81],{"slug":26,"chapterNum":27,"title":28,"titleEn":29,"summary":30,"summaryEn":31},"overview","01","Vue d'ensemble du harness agentique","Agentic harness overview","Le harness transforme une demande (courriel, console, cron) en action déployée en enchaînant classification, spawn LLM via pseudo-TTY, validation qualité à deux niveaux et boucle d'apprentissage, le tout sans état métier hors base de données.","The harness transforms a request (email, console, cron) into a deployed action by chaining intent classification, pseudo-TTY LLM spawn, two-level quality validation, and a learning loop — with no business state stored outside the database.",{"slug":33,"chapterNum":34,"title":35,"titleEn":36,"summary":37,"summaryEn":38},"data-layer","02","La couche données","The Data Layer","Décrit l'architecture données de Synedre OS : une base PostgreSQL unique (un composant interne\u002Fun composant interne), les trois chemins d'accès (Nuxt\u002FNitro, Python agentique, Drizzle ORM DDL), les familles de tables par préfixe et les conventions de nommage associées.","Describes the Synedre OS data architecture: a single PostgreSQL database (un composant interne\u002Fun composant interne), the three access paths (Nuxt\u002FNitro, agentic Python, Drizzle ORM DDL), the table families by prefix, and the associated naming conventions.",{"slug":40,"chapterNum":41,"title":42,"titleEn":43,"summary":44,"summaryEn":45},"agentic-core","03","Le cœur agentique : Atlas et les agents","The Agentic Core: Atlas and the Agents","Décrit l'architecture du moteur agentique : classification LLM des emails entrants, spawn headless de Claude Code via pseudo-TTY, injection des personas agents et orchestration post-spawn (deploy, QA, récap).","Describes the architecture of the agentic engine: LLM classification of incoming emails, headless spawning of Claude Code via pseudo-TTY, injection of agent personas, and post-spawn orchestration (deploy, QA, recap).",{"slug":14,"chapterNum":15,"title":16,"titleEn":17,"summary":47,"summaryEn":48},"Décrit la hiérarchie chantier\u002Ftravail\u002Ftâche de Synedre OS, le modèle de données DB, les statuts\u002Fscopes canoniques et l'API Python associée.","Describes the worksite\u002Fjob\u002Ftask hierarchy in Synedre OS, the DB data model, canonical statuses\u002Fscopes, and the associated Python API.",{"slug":5,"chapterNum":6,"title":7,"titleEn":8,"summary":9,"summaryEn":10},{"slug":19,"chapterNum":20,"title":21,"titleEn":22,"summary":51,"summaryEn":52},"Présentation de l'interface d'administration privée Synedre OS (mothership-app), son architecture en couches Nuxt, la carte de ses modules et pages, ainsi que le système d'authentification et de session.","Presentation of the Synedre OS private administration interface (mothership-app), its Nuxt layered architecture, the map of its modules and pages, as well as the authentication and session management system.",{"slug":54,"chapterNum":55,"title":56,"titleEn":57,"summary":58,"summaryEn":59},"email","07","Inbox hub & Atlas Inbox — deux pipelines email","Inbox hub & Atlas Inbox — two email pipelines","Décrit les deux pipelines IMAP→DB du harness (contact@ trié pour le hub, atlas@ déclencheur d'actions agentiques), la façade d'envoi sortant et les doctrines bloquantes associées (scan AV, zéro contact client direct).","Describes the two IMAP→DB pipelines of the harness (contact@ sorted for the hub, atlas@ triggering agentic actions), the outbound sending facade, and the associated blocking doctrines (AV scan, zero direct client contact).",{"slug":61,"chapterNum":62,"title":63,"titleEn":64,"summary":65,"summaryEn":66},"memory","08","Mémoire & apprentissage — architecture à trois niveaux","Memory & Learning — Three-Level Architecture","Ce chapitre décrit les trois niveaux de mémoire du harness Synedre OS (réflexe, Zettelkasten, vectoriel pgvector), les automates d'indexation associés et la boucle de capitalisation des erreurs en règles réutilisables.","This chapter describes the three memory levels of the Synedre OS harness (reflex, Zettelkasten, vector pgvector), the associated indexing automata, and the loop for capitalizing errors into reusable rules.",{"slug":68,"chapterNum":69,"title":70,"titleEn":71,"summary":72,"summaryEn":73},"deploy","09","Déploiement & infrastructure","Deployment & infrastructure","Décrit le pipeline de mise en ligne Synedre OS : asymétrie entre .\u002Fdeploy (IA, preprod) et .\u002Fship (Alex, production), le dispatcher YAML, le pattern build-host sans build VPS, et les règles associées (secrets, commit-avant-deploy, drift, smoke).","Describes the Synedre OS release pipeline: asymmetry between .\u002Fdeploy (AI, preprod) and .\u002Fship (Alex, production), the YAML dispatcher, the build-host pattern without build VPS, and the associated rules (secrets, commit-before-deploy, drift, smoke).",{"slug":75,"chapterNum":76,"title":77,"titleEn":78,"summary":79,"summaryEn":80},"facades","10","catalogue des façades et points d'entrée du harness","harness facade and entry-point catalogue","comment le harness synedre organise ses centaines d'automates en familles fonctionnelles, avec les garde-fous en temps réel qui encadrent chaque action.","how the synedre harness organizes its hundreds of automations into functional families, with the real-time guard-rails that frame every action.",{"slug":82,"chapterNum":83,"title":84,"titleEn":85,"summary":86,"summaryEn":87},"skills-hooks","11","Skills, agents & hooks — harness agentique Synedre OS","Skills, agents & hooks — Synedre OS agentic harness","Décrit le câblage complet du harness Claude Code de synedre-os : skills disponibles, sous-agents délégables et hooks d'événements qui imposent la doctrine commit-en-flux, garde-fous prod\u002Femail et injection mémoire.","Describes the complete wiring of the synedre-os Claude Code harness: available skills, delegatable sub-agents, and event hooks that enforce the commit-in-flow doctrine, prod\u002Femail guardrails, and memory injection."]