[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fyHX32m6bg42BV39jjqIBMU2PYn_AprOCFbaXvsnpA8M":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},"chantiers","04","Chantiers, travaux & tâches — modèle de données et API","Worksites, Jobs & Tasks — Data Model and API","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.","\u003Ch2 id=\"chantiers-travaux-taches\">Chantiers, travaux &amp; tâches\u003C\u002Fh2>\n\n\u003Ch3>À quoi ça sert\u003C\u002Fh3>\n\n\u003Cp>Le harness Synedre OS organise tout travail de développement en une hiérarchie persistée en base — \u003Cstrong>1 chantier = N travaux = N tâches\u003C\u002Fstrong> — afin que l'orchestrateur (Atlas) et les agents puissent créer, suivre, paralléliser et clôturer du travail sans état hors-base. Cette page documente le modèle de données, l'interface Python qui le manipule, la procédure de création en sept étapes, le mécanisme de verrou multi-session, et les cascades de statut (dont la résolution dite « travail-bis » d'un travail en pause).\u003C\u002Fp>\n\n\u003Ch2 id=\"modele-de-donnees\">1. Modèle de données\u003C\u002Fh2>\n\n\u003Ch3>1.1 Hiérarchie\u003C\u002Fh3>\n\n\u003Cp>Trois niveaux imbriqués structurent tout travail :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Chantier\u003C\u002Fstrong> — unité de pilotage de haut niveau\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Travail\u003C\u002Fstrong> — sous-ensemble d'un chantier, assignable à un agent\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Tâche\u003C\u002Fstrong> — action atomique au sein d'un travail\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Trois entités Python principales encapsulent ces niveaux. Chacune étend une classe de base commune et expose des méthodes de validation, de création et de mise à jour. Deux entités secondaires gèrent respectivement les \u003Cstrong>verrous multi-session\u003C\u002Fstrong> et les \u003Cstrong>dépendances entre tâches\u003C\u002Fstrong>.\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Entité\u003C\u002Fth>\n      \u003Cth>Rôle\u003C\u002Fth>\n      \u003Cth>Clé primaire\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Chantier\u003C\u002Ftd>\n      \u003Ctd>Unité de pilotage\u003C\u002Ftd>\n      \u003Ctd>identifiant chantier\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Travail\u003C\u002Ftd>\n      \u003Ctd>Sous-ensemble d'un chantier\u003C\u002Ftd>\n      \u003Ctd>identifiant travail\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Tâche\u003C\u002Ftd>\n      \u003Ctd>Action atomique\u003C\u002Ftd>\n      \u003Ctd>identifiant tâche\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Journal d'auto-décomposition\u003C\u002Ftd>\n      \u003Ctd>Audit des runs de décomposition automatique\u003C\u002Ftd>\n      \u003Ctd>identifiant run\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Verrou\u003C\u002Ftd>\n      \u003Ctd>Mutex par chantier et par type de propriétaire\u003C\u002Ftd>\n      \u003Ctd>(identifiant chantier, type propriétaire)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Dépendance tâche\u003C\u002Ftd>\n      \u003Ctd>Arc du DAG intra-travail\u003C\u002Ftd>\n      \u003Ctd>identifiant dépendance\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Note de migration\u003C\u002Fstrong> — La nomenclature des tables est hétérogène pour des raisons historiques. Ne pas renommer sans chantier dédié.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch3>1.2 Statuts\u003C\u002Fh3>\n\n\u003Cp>Les statuts canoniques sont \u003Cstrong>scindés par niveau\u003C\u002Fstrong> pour prévenir les validations croisées.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Statuts chantier\u003C\u002Fstrong> : \u003Ccode>planning\u003C\u002Fcode>, \u003Ccode>dev\u003C\u002Fcode>, \u003Ccode>test\u003C\u002Fcode>, \u003Ccode>paused\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>cancelled\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Statuts travail\u003C\u002Fstrong> : \u003Ccode>discovery\u003C\u002Fcode>, \u003Ccode>planning\u003C\u002Fcode>, \u003Ccode>dev\u003C\u002Fcode>, \u003Ccode>paused\u003C\u002Fcode>, \u003Ccode>review\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>cancelled\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>Un alias regroupant l'union des deux ensembles est conservé pour compatibilité ascendante avec certains modules internes. Tout nouveau code doit utiliser l'ensemble propre au niveau concerné.\u003C\u002Fp>\n\n\u003Cp>La validation commune accepte un paramètre \u003Ccode>status_set\u003C\u002Fcode> : l'entité Chantier lui passe les statuts chantier, l'entité Travail les statuts travail. Un \u003Ccode>INSERT\u003C\u002Fcode> SQL avec un statut hors-enum est rejeté directement par la base de données grâce à des contraintes \u003Ccode>CHECK\u003C\u002Fcode> posées sur les deux tables concernées.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Statuts tâche\u003C\u002Fstrong> : \u003Ccode>todo\u003C\u002Fcode>, \u003Ccode>doing\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>paused\u003C\u002Fcode>, \u003Ccode>cancelled\u003C\u002Fcode> — vocable distinct, sans contrainte \u003Ccode>CHECK\u003C\u002Fcode> en base à ce jour.\u003C\u002Fp>\n\n\u003Cp>Les priorités canoniques sont : \u003Ccode>P0\u003C\u002Fcode>, \u003Ccode>P1\u003C\u002Fcode>, \u003Ccode>P2\u003C\u002Fcode>, \u003Ccode>P3\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Ch3>1.3 Scopes\u003C\u002Fh3>\n\n\u003Cp>Le champ \u003Ccode>scope\u003C\u002Fcode> discrimine le périmètre de synchronisation (monolithe ou OSS) et conditionne certaines validations.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Scope chantier\u003C\u002Fstrong> (colonne \u003Ccode>scope\u003C\u002Fcode>, nullable) — valeurs autorisées par contrainte en base :\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>synedre | codemyshop-oss | codemyshop-enterprise | tenant | business | juridique | negociation | conseil\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Attention\u003C\u002Fstrong> — La documentation interne de la méthode de création ne liste que les cinq premiers scopes. La base en autorise trois de plus (\u003Ccode>juridique\u003C\u002Fcode>, \u003Ccode>negociation\u003C\u002Fcode>, \u003Ccode>conseil\u003C\u002Fcode>), ajoutés pour les intents Atlas. \u003Cstrong>La base fait foi.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>\u003Cstrong>Scope tâche\u003C\u002Fstrong> — validé à la fois en code et par contrainte en base :\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>synedre-internal | codemyshop-oss | codemyshop-enterprise | tenant-single | tenant-multi | infra | doctrine\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Un scope tâche invalide lève une \u003Ccode>ValueError\u003C\u002Fcode> dès la création de la tâche.\u003C\u002Fp>\n\n\u003Ch3>1.4 Champs notables\u003C\u002Fh3>\n\n\u003Cp>\u003Cstrong>Au niveau chantier\u003C\u002Fstrong> :\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Champ\u003C\u002Fth>\n      \u003Cth>Rôle\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>mission_letter\u003C\u002Fcode> (texte libre)\u003C\u002Ftd>\n      \u003Ctd>Lettre de mission structurée (étape 2 de la doctrine)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>preprod_test_plan\u003C\u002Fcode> (texte libre)\u003C\u002Ftd>\n      \u003Ctd>Plan de test preprod — Markdown ou YAML v2 (présence de \u003Ccode>version:\u003C\u002Fcode> déclenche la QA automatique)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>ship_command\u003C\u002Fcode> (chaîne courte)\u003C\u002Ftd>\n      \u003Ctd>Commande exacte de clôture, ex. \u003Ccode>.\u002Fship synedre-os\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>auto_explode\u003C\u002Fcode> (booléen, défaut \u003Ccode>true\u003C\u002Fcode>)\u003C\u002Ftd>\n      \u003Ctd>Kill-switch par chantier de la décomposition automatique discovery→implémentation\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>mode_auto\u003C\u002Fcode> (booléen)\u003C\u002Ftd>\n      \u003Ctd>Autonomie chantier V1 — l'agent exécute sans valider chaque action\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>max_cost_eur\u003C\u002Fcode> (numérique)\u003C\u002Ftd>\n      \u003Ctd>Plafond de coût IA par chantier (\u003Ccode>NULL\u003C\u002Fcode> = illimité)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>client_id\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Référence vers un client, ou \u003Ccode>NULL\u003C\u002Fcode> pour un chantier interne\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>\u003Cstrong>Au niveau travail\u003C\u002Fstrong> : les champs notables incluent le codename et le prompt de l'agent assigné, le périmètre de zone, les critères de sortie, des blocs JSON structurés de contexte \u002F décisions \u002F découvertes, un identifiant de dépendance inter-travaux (avec détection de cycle), l'identifiant de résolution pour le pattern travail-bis (voir §6), ainsi que des métadonnées de conduite, de modèle contractuel, de mode auto, de chemins de dépôt et de backlogs associés.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Au niveau tâche\u003C\u002Fstrong> : les champs notables incluent le codename de l'assigné, l'estimation en tokens, le modèle recommandé, la position d'affichage, le résultat du dernier test, les métriques réelles (tokens, coût), les horodatages de début et fin, ainsi que deux champs visuels (\u003Ccode>visual_intent\u003C\u002Fcode> et \u003Ccode>visual_url\u003C\u002Fcode>) utilisés par le pipeline de vérification visuelle post-déploiement — une valeur \u003Ccode>NULL\u003C\u002Fcode> indique une tâche non-visuelle.\u003C\u002Fp>\n\n\u003Ch3>1.5 Recrutement d'agents — persistance\u003C\u002Fh3>\n\n\u003Cp>Le recrutement d'agents sur un chantier (étape 3 de la doctrine) est persisté dans une table dédiée, \u003Cstrong>distincte\u003C\u002Fstrong> du champ d'assignation d'une tâche individuelle (qui ne porte que l'assignation d'une tâche isolée).\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Colonne\u003C\u002Fth>\n      \u003Cth>Rôle\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Identifiant assignation (PK)\u003C\u002Ftd>\n      \u003Ctd>Clé primaire de l'assignation\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Identifiant chantier\u003C\u002Ftd>\n      \u003Ctd>Lien vers le chantier\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Codename agent\u003C\u002Ftd>\n      \u003Ctd>Agent recruté (ex. Mitnick, Turing, Brunel…)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Rôle\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>production\u003C\u002Fcode> \u002F \u003Ccode>lead_production\u003C\u002Fcode> \u002F \u003Ccode>validation\u003C\u002Fcode> \u002F \u003Ccode>lead_validation\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Position\u003C\u002Ftd>\n      \u003Ctd>Ordre d'affichage\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Date assignation \u002F désassignation\u003C\u002Ftd>\n      \u003Ctd>Cycle de vie de l'assignation\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Notes\u003C\u002Ftd>\n      \u003Ctd>Justification du recrutement\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>La logique de cascade tâche→travail interroge cette table pour détecter la présence d'une équipe QA : si un agent avec un rôle \u003Ccode>validation\u003C\u002Fcode> ou \u003Ccode>lead_validation\u003C\u002Fcode> est présent, la cascade bascule vers un run de QA plutôt qu'un simple changement de statut (voir §5).\u003C\u002Fp>\n\n\u003Ch3>1.6 Journal de décomposition automatique\u003C\u002Fh3>\n\n\u003Cp>Une entité dédiée persiste un enregistrement d'audit par invocation du module qui transforme les décisions d'un travail de type \u003Cem>discovery\u003C\u002Fem> en travaux d'implémentation (phases A\u002FB\u002FC). Un enregistrement équivaut à une invocation. Il est créé avec le statut \u003Ccode>pending\u003C\u002Fcode> en début de pipeline, puis mis à jour en fin.\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Statut\u003C\u002Fth>\n      \u003Cth>Sens\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>pending\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Run en cours\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>success\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Travaux d'implémentation créés\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>validation_failed\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Plan LLM rejeté par la validation\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>llm_failed\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Erreur d'API LLM\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>killswitched\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>L'un des trois kill-switches a interrompu l'exécution\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Les champs clés comprennent le plan JSON produit, le nombre de travaux créés, les métriques de tokens et de coût, ainsi que des drapeaux \u003Ccode>dry_run\u003C\u002Fcode> et \u003Ccode>force\u003C\u002Fcode>. La rétention des enregistrements est configurable (défaut : 90 jours). Un quota journalier global est également configurable : si le nombre de runs des dernières 24 heures le dépasse, la décomposition est marquée \u003Ccode>killswitched\u003C\u002Fcode> sans erreur. L'historique des runs est consultable depuis le dashboard via la commande \u003Ccode>\u002Fchantier explode list\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Ch3>1.7 Dépendances tâche→tâche — DAG intra-travail\u003C\u002Fh3>\n\n\u003Cp>Le moteur de dépendances entre tâches gère un graphe orienté acyclique (DAG) N:M. \u003Cstrong>À ne pas confondre\u003C\u002Fstrong> avec les dépendances inter-travaux (colonne de l'entité Travail) : ces deux mécanismes opèrent sur des niveaux distincts.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Sémantique\u003C\u002Fstrong> : une tâche bloquée ne peut démarrer avant que la tâche bloquante ne soit au statut \u003Ccode>done\u003C\u002Fcode>. Contrainte métier forte : les deux tâches \u003Cstrong>doivent\u003C\u002Fstrong> appartenir au \u003Cstrong>même\u003C\u002Fstrong> travail — une dépendance inter-travaux lève une erreur applicative dédiée (refus en code, pas de trigger SQL).\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Opération\u003C\u002Fth>\n      \u003Cth>Méthode conceptuelle\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Ajouter une dépendance\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>add_dep(bloquée, bloquante)\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Lister les dépendances d'un travail\u003C\u002Ftd>\n      \u003Ctd>JOIN blocker\u002Fblocked avec titres des tâches\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Lister les bloqueurs d'une tâche\u003C\u002Ftd>\n      \u003Ctd>Bloqueurs directs d'une tâche donnée\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Vérifier si une tâche est débloquée\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>is_tache_unblocked(id)\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Supprimer une dépendance\u003C\u002Ftd>\n      \u003Ctd>Idempotent — sans effet si inexistante\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>\u003Cstrong>Anti-cycle\u003C\u002Fstrong> : avant tout ajout, un parcours en profondeur (DFS) est effectué depuis la tâche bloquante dans le graphe existant. Si le parcours atteint la tâche bloquée, une erreur de cycle est levée avant l'insertion. Une garde à profondeur maximale (100 niveaux) protège contre les DAG corrompus.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>Un bloqueur au statut \u003Ccode>cancelled\u003C\u002Fcode> est considéré comme \u003Cstrong>résolu\u003C\u002Fstrong> dans la vérification de déblocage, afin d'éviter les deadlocks. Ce comportement s'applique également au mode auto sélectif, qui ne lance une tâche que si tous ses bloqueurs sont résolus.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"creation-atomique\">Création atomique d'un chantier\u003C\u002Fh2>\n\n\u003Cp>La méthode de création de chantier exposée par le moteur de gestion de projets constitue l'\u003Cstrong>unique voie canonique\u003C\u002Fstrong> pour instancier un chantier : elle insère atomiquement le chantier, son premier travail et au moins une tâche, empêchant ainsi l'apparition de chantiers orphelins.\u003C\u002Fp>\n\n\u003Ch3>Signature\u003C\u002Fh3>\n\n\u003Cp>La méthode accepte les paramètres suivants :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>codename\u003C\u002Fstrong> : identifiant kebab-case du chantier.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>title\u003C\u002Fstrong> : libellé humain du chantier.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>first_travail\u003C\u002Fstrong> (obligatoire) : dictionnaire décrivant le premier travail — codename, titre, et champs optionnels tels que priorité, phase courante, tâche courante, prochaine action, blocages.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>first_taches\u003C\u002Fstrong> (préféré, doctrine v3) : liste de dictionnaires décrivant les tâches initiales. Chaque tâche porte au minimum un titre et le codename de l'agent assigné ; des champs optionnels enrichissent la description (priorité, estimation de tokens, durée estimée, description, position, modèle recommandé, portée, intention visuelle, URL visuelle).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>first_tache\u003C\u002Fstrong> : forme singulière conservée pour compatibilité ascendante — à ne pas combiner avec \u003Ccode>first_taches\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>client_id\u003C\u002Fstrong> : référence optionnelle au tenant concerné.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>priority\u003C\u002Fstrong> : niveau de priorité parmi \u003Ccode>P0\u003C\u002Fcode>, \u003Ccode>P1\u003C\u002Fcode>, \u003Ccode>P2\u003C\u002Fcode> (défaut), \u003Ccode>P3\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>scope\u003C\u002Fstrong>, \u003Cstrong>current_focus\u003C\u002Fstrong>, \u003Cstrong>notes\u003C\u002Fstrong> : champs contextuels optionnels.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>La méthode retourne un dictionnaire de confirmation (voir §&nbsp;Retour ci-dessous).\u003C\u002Fp>\n\n\u003Ch3>Validations bloquantes\u003C\u002Fh3>\n\n\u003Cp>Toutes les validations sont accumulées avant le premier INSERT ; une seule violation lève une \u003Ccode>ValidationError\u003C\u002Fcode> et annule l'ensemble :\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Format kebab-case du codename\u003C\u002Fstrong> — expression régulière \u003Ccode>^[a-z0-9][a-z0-9\\-]{2,62}[a-z0-9]$\u003C\u002Fcode> : minuscules, chiffres et tirets, 4 à 64 caractères.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Unicité du codename\u003C\u002Fstrong> — une vérification d'existence est effectuée ; si le codename est déjà utilisé, la création est refusée.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Priorité valide\u003C\u002Fstrong> — doit appartenir à \u003Ccode>{P0, P1, P2, P3}\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Travail initial valide\u003C\u002Fstrong> — codename et titre doivent être renseignés et non vides.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Au moins une tâche\u003C\u002Fstrong> — \u003Ccode>first_tache\u003C\u002Fcode> ou \u003Ccode>first_taches\u003C\u002Fcode> est obligatoire ; fournir les deux simultanément est une erreur.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Agent connu\u003C\u002Fstrong> — chaque codename d'agent assigné est vérifié dans le registre des agents actifs.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Au moins deux agents distincts si la portée est de type tenant\u003C\u002Fstrong> (doctrine v3) — si le champ \u003Ccode>scope\u003C\u002Fcode> commence par \u003Ccode>tenant\u003C\u002Fcode>, le set des assignees doit contenir au moins deux codenames distincts.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>\u003Cstrong>Avertissement non-bloquant :\u003C\u002Fstrong> l'absence d'estimation de tokens sur une tâche génère un warning mais n'empêche pas la création — un estimateur automatique complète la valeur manquante (voir §&nbsp;Estimation ci-dessous).\u003C\u002Fp>\n\n\u003Ch3>Atomicité et rollback\u003C\u002Fh3>\n\n\u003Cp>L'insertion s'effectue dans une transaction unique (\u003Ccode>BEGIN … COMMIT\u003C\u002Fcode>) avec l'option d'arrêt immédiat sur erreur. Si un seul INSERT échoue, toute la transaction est annulée : il ne peut exister ni chantier sans travail, ni travail sans tâche. Les travaux et tâches référencent leur parent via une sous-sélection sur le codename du chantier, garantissant la résolution des clés étrangères dans l'ordre d'insertion.\u003C\u002Fp>\n\n\u003Ch3>Estimation de tokens et modèle recommandé\u003C\u002Fh3>\n\n\u003Cp>Si le modèle recommandé n'est pas fourni explicitement pour une tâche, il est calculé automatiquement selon la règle suivante :\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Condition\u003C\u002Fth>\n      \u003Cth>Modèle sélectionné\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Priorité P0 ou tâche en échec récurrent (≥&nbsp;2 itérations en état \u003Ccode>fail\u003C\u002Fcode>)\u003C\u002Ftd>\n      \u003Ctd>Modèle haute capacité (ex. : opus)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Estimation ≥&nbsp;8&nbsp;000 tokens ou estimation absente\u003C\u002Ftd>\n      \u003Ctd>Modèle haute capacité (ex. : opus)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Estimation ≥&nbsp;1&nbsp;500 tokens\u003C\u002Ftd>\n      \u003Ctd>Modèle intermédiaire (ex. : sonnet)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Sinon\u003C\u002Ftd>\n      \u003Ctd>Modèle léger (ex. : haiku)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Lors de la création d'une tâche hors skeleton, si l'estimation de tokens est absente, le moteur la calcule via un estimateur dédié, puis recalcule le modèle recommandé sur la ligne persistée.\u003C\u002Fp>\n\n\u003Ch3>Compétences et outils attachés\u003C\u002Fh3>\n\n\u003Cp>Après l'insertion, le skeleton attache les compétences (\u003Cem>skills\u003C\u002Fem>) et outils (\u003Cem>tools\u003C\u002Fem>) optionnels déclarés par tâche. Un skill ou outil inconnu du registre ne provoque pas d'erreur : un warning est émis et une proposition est mise en attente de validation.\u003C\u002Fp>\n\n\u003Ch3>Retour\u003C\u002Fh3>\n\n\u003Cp>La méthode retourne un dictionnaire contenant :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>Les identifiants internes du chantier, du travail et des tâches créés.\u003C\u002Fli>\n  \u003Cli>Un champ \u003Ccode>id_tache\u003C\u002Fcode> (compatibilité ascendante, égal au premier de la liste) et \u003Ccode>id_taches\u003C\u002Fcode> (liste complète, doctrine v3).\u003C\u002Fli>\n  \u003Cli>Un objet \u003Ccode>codenames\u003C\u002Fcode> regroupant les codenames du chantier, du travail et des tâches.\u003C\u002Fli>\n  \u003Cli>Une liste \u003Ccode>warnings\u003C\u002Fcode> des avertissements non-bloquants émis pendant la création.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>Interface en ligne de commande\u003C\u002Fh3>\n\n\u003Cp>Le moteur de gestion de projets expose une interface CLI permettant de déclencher la création atomique en passant le payload JSON en entrée standard ou via un fichier. L'option \u003Ccode>--dry-run\u003C\u002Fcode> affiche le payload construit sans effectuer d'insertion. D'autres verbes sont disponibles — liste, affichage, création de travail, mise à jour, clôture, résolution — consultables via l'option \u003Ccode>--help\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>Un hook de garde détecte toute tentative d'insertion SQL directe sur les tables du moteur de projets effectuée en dehors de cette façade, et émet un avertissement.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch2 id=\"procedure-creation-chantier\">Procédure de création en 7 étapes\u003C\u002Fh2>\n\n\u003Cp>Aucun chantier ne peut être créé sans parcourir ces étapes dans l'ordre. Pour les chantiers déclenchés par email, la façade dédiée exécute les étapes \u003Cstrong>0a à 4\u003C\u002Fstrong> sous contrainte (blocage si scan d'attachement non-clean ou moins de 2 agents). Elle s'arrête à la création atomique du skeleton (étape 4) et n'effectue \u003Cstrong>ni l'étape 2\u003C\u002Fstrong> (rédaction de la lettre de mission : la façade génère uniquement des titres de tâches génériques du type \u003Cem>@agent — analyse…\u003C\u002Fem>) \u003Cstrong>ni l'étape 5\u003C\u002Fstrong> (mise à jour post-skeleton des champs de mission, de portée, du plan de test et de la commande de livraison). Ces deux étapes restent à compléter manuellement après l'appel de la façade.\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Étape\u003C\u002Fth>\n      \u003Cth>Action\u003C\u002Fth>\n      \u003Cth>Mécanisme\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>0a\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Lire la demande email verbatim\u003C\u002Ftd>\n      \u003Ctd>Lecture via l'API interne de la boîte de réception\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>0b\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Extraire et scanner les pièces jointes avant toute lecture\u003C\u002Ftd>\n      \u003Ctd>Extraction des attachements, puis analyse antivirale\u002Fantimalware. Verdict non-\u003Ccode>clean\u003C\u002Fcode> → blocage immédiat\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>1\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Audit des agents disponibles\u003C\u002Ftd>\n      \u003Ctd>Interrogation du registre des agents actifs (codename, surnom, rôle, groupe)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>2\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Rédiger la lettre de mission\u003C\u002Ftd>\n      \u003Ctd>Structure : OBJECTIF \u002F CONTEXTE \u002F PÉRIMÈTRE \u002F LIVRABLES \u002F CONTRAINTES \u002F DOCTRINE, à partir du contenu lu aux étapes 0a\u002F0b\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>3\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Recrutement explicite\u003C\u002Ftd>\n      \u003Ctd>Au moins 2 agents distincts si la portée est de type tenant\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>4\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Création atomique du skeleton\u003C\u002Ftd>\n      \u003Ctd>Voir §&nbsp;Création atomique\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>5\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Mise à jour post-skeleton\u003C\u002Ftd>\n      \u003Ctd>Persistance de la lettre de mission, de la portée, du plan de test en préprod et de la commande de livraison via UPDATE nominatif sur le codename\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch3>Façade de création depuis un email\u003C\u002Fh3>\n\n\u003Cp>La façade de création depuis un email suit le flux suivant :\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Récupération de l'email\u003C\u002Fstrong> — appel à l'API interne de la boîte de réception (jamais de SQL direct).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Extraction et scan des pièces jointes\u003C\u002Fstrong> — si un attachement n'est pas \u003Ccode>clean\u003C\u002Fcode>, la façade affiche un message de blocage et s'arrête avec le code de sortie \u003Ccode>2\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Résumé de la demande\u003C\u002Fstrong> — extraction du titre, du client, de la priorité et de la portée.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Liste des agents et recrutement\u003C\u002Fstrong> — si moins de 2 agents distincts sont sélectionnés, blocage avec le code de sortie \u003Ccode>3\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Construction et insertion du skeleton\u003C\u002Fstrong> — une tâche par agent avec des titres génériques, travail initial en phase \u003Ccode>discovery\u003C\u002Fcode>. Correspond à l'étape doctrinale 4. Aucune mise à jour de lettre de mission ni d'étape doctrinale 5 n'est effectuée.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>\u003Cstrong>Codes de sortie :\u003C\u002Fstrong> \u003Ccode>2\u003C\u002Fcode> = attachement non-clean, \u003Ccode>3\u003C\u002Fcode> = moins de 2 agents, \u003Ccode>4\u003C\u002Fcode> = erreur de validation.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Deux niveaux de contrôle distincts sur le nombre d'agents :\u003C\u002Fstrong> la façade exige \u003Cem>toujours\u003C\u002Fem> au moins 2 agents distincts, indépendamment de la portée déclarée. Le moteur de création atomique, lui, n'applique cette contrainte que lorsque le champ \u003Ccode>scope\u003C\u002Fcode> commence par \u003Ccode>tenant\u003C\u002Fcode>. Le scope par défaut \u003Ccode>tenant\u003C\u002Fcode> aligne le message de blocage de la façade, mais ne conditionne pas son propre contrôle.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cblockquote>\n  \u003Cp>La façade crée un unique travail initial en phase \u003Ccode>discovery\u003C\u002Fcode>. La transition vers les travaux d'implémentation reste manuelle ou passe par le mécanisme d'explosion automatique de travaux (voir §&nbsp;Auto-explode).\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch3>Champs plan de test et commande de livraison (étape 5)\u003C\u002Fh3>\n\n\u003Cp>Ces deux champs sont obligatoires pour tout chantier non-trivial. En leur absence, la cascade de mise à jour du chantier enregistre un avertissement non-bloquant, mais le bandeau « PRÊT À SHIP » ne peut pas être validé. Si le plan de test est rédigé au format YAML v2 (en-tête \u003Ccode>version:\u003C\u002Fcode>), la cascade déclenche automatiquement en arrière-plan une session de tests Playwright. Un interrupteur d'environnement permet de désactiver ce déclenchement automatique.\u003C\u002Fp>\n\n\u003Ch2 id=\"lock-multi-session\">Verrouillage multi-session\u003C\u002Fh2>\n\n\u003Cp>Le mécanisme de verrouillage permet d'ouvrir N chantiers \u003Cstrong>différents\u003C\u002Fstrong> en parallèle (N terminaux ou workers) tout en refusant deux sessions concurrentes sur le \u003Cstrong>même\u003C\u002Fstrong> chantier. Ce n'est pas un bloqueur ergonomique, mais un filet anti-collision.\u003C\u002Fp>\n\n\u003Ch3>Modèle de données\u003C\u002Fh3>\n\n\u003Cp>Le verrou repose sur une table dédiée avec une clé primaire composite \u003Cstrong>(identifiant du chantier, nature du propriétaire)\u003C\u002Fstrong>. La nature du propriétaire est contrainte à deux valeurs :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>user\u003C\u002Fstrong> : session interactive (par exemple, une session SSH d'un opérateur humain).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>worker\u003C\u002Fstrong> : sous-processus agent spawné par le moteur de tâches automatisées.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Les deux natures peuvent coexister sur le même chantier ; chaque nature ne conteste que son propre slot. La nature est détectée via une variable d'environnement, avec repli sur l'inspection de l'arbre de processus parents. L'identifiant de session est résolu dans l'ordre : variable d'environnement de session Claude &gt; empreinte du chemin de transcription &gt; combinaison PID\u002Futilisateur\u002Fhôte.\u003C\u002Fp>\n\n\u003Ch3>TTL et acquisition\u003C\u002Fh3>\n\n\u003Cp>La durée de vie d'un verrou inactif est de \u003Cstrong>30 minutes\u003C\u002Fstrong>. L'acquisition tente un \u003Ccode>INSERT … ON CONFLICT … DO UPDATE\u003C\u002Fcode> qui ne réussit que si l'identifiant de session correspond à l'acquéreur actuel \u003Cem>ou\u003C\u002Fem> si le verrou a expiré (dernière activité il y a plus de 30 minutes). Dans le cas contraire, la méthode retourne \u003Ccode>{acquired: False, owner: {…}}\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>Le verrou est acquis automatiquement lors de la récupération du contexte d'un chantier. En cas de refus, une exception \u003Ccode>ChantierLockedError\u003C\u002Fcode> est levée, interceptée par la compétence de gestion de chantiers. Un interrupteur d'environnement permet de désactiver entièrement le mécanisme de verrouillage.\u003C\u002Fp>\n\n\u003Ch3>Opérations disponibles\u003C\u002Fh3>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Opération\u003C\u002Fth>\n      \u003Cth>Description\u003C\u002Fth>\n      \u003Cth>Option CLI\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Inventaire des verrous actifs\u003C\u002Ftd>\n      \u003Ctd>Liste les verrous vivants avec leur propriétaire et date d'activité\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>--locks\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Libérer son verrou\u003C\u002Ftd>\n      \u003Ctd>Suppression idempotente du verrou correspondant à la session courante\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>--release &lt;codename&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Reprendre le verrou (kick)\u003C\u002Ftd>\n      \u003Ctd>Force la reprise du slot de même nature, évinçant le propriétaire précédent\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>--force-claim &lt;codename&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Maintenir le verrou vivant\u003C\u002Ftd>\n      \u003Ctd>Met à jour l'horodatage d'activité pour repousser l'expiration TTL\u003C\u002Ftd>\n      \u003Ctd>—\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch3>Trois filets de libération\u003C\u002Fh3>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Hook de fin de session\u003C\u002Fstrong> — déclaré dans la configuration des hooks Claude, libère le verrou rapidement en fin de session interactive.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Tâche cron TTL\u003C\u002Fstrong> — s'exécute toutes les 5 minutes, supprime les verrous dont la dernière activité dépasse 30 minutes. Idempotente, toujours en exit 0, silencieuse si la base de données est indisponible ou si la table est absente. C'est le vrai garde-fou contre les crashes, coupures réseau ou terminaisons brutales de processus.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Force-claim manuel\u003C\u002Fstrong> — utilisé en dernier recours pour reprendre un verrou orphelin.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"cascades-de-statut\">Cascades de statut\u003C\u002Fh2>\n\n\u003Cp>Les méthodes de mise à jour des entités sont surchargées pour propager les statuts terminaux d'un niveau au suivant. \u003Cstrong>La cascade est limitée à un cran par niveau\u003C\u002Fstrong> — chaque cran redéclenche sa propre cascade de manière autonome.\u003C\u002Fp>\n\n\u003Ch3>Logique de propagation tâche → travail\u003C\u002Fh3>\n\n\u003Cp>Lorsque toutes les tâches d'un travail atteignent un statut terminal (\u003Cstrong>terminé\u003C\u002Fstrong> ou \u003Cstrong>annulé\u003C\u002Fstrong>) et qu'au moins une tâche est terminée, le système évalue si une équipe de validation est présente sur le chantier :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Avec équipe de validation\u003C\u002Fstrong> — le moteur de réflexes délègue à un sous-processus de contrôle qualité. Si le verdict global est positif, le travail passe à l'état \u003Cem>terminé\u003C\u002Fem>. Dans le cas contraire, des tâches correctives sont créées et le travail reste actif dans sa phase de développement.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Sans équipe de validation\u003C\u002Fstrong> — le passage à \u003Cem>terminé\u003C\u002Fem> est effectué directement.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Toute exception levée par le sous-processus de contrôle qualité est \u003Cstrong>fail-safe\u003C\u002Fstrong> : elle est journalisée sans bloquer ni modifier l'état du travail.\u003C\u002Fp>\n\n\u003Ch3>Logique de propagation travail → chantier\u003C\u002Fh3>\n\n\u003Cp>Lorsque tous les travaux d'un chantier atteignent un statut terminal et qu'au moins un est terminé, le chantier est promu à l'état \u003Cem>test\u003C\u002Fem> (préprod, en attente de revue). Ce passage déclenche également, de manière non bloquante, le détecteur d'entropie décrit à la section suivante.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>⚠️ Le passage tâche → travail n'est pas systématiquement direct.\u003C\u002Fstrong> Dès qu'un chantier comporte au moins un agent affecté à un rôle de validation, la cascade délègue au sous-processus de contrôle qualité plutôt que d'effectuer le bump immédiatement. Le travail ne passe à l'état \u003Cem>terminé\u003C\u002Fem> que si le verdict global est positif ; sinon, des tâches correctives sont générées et le travail reste actif. Le bump direct n'a lieu \u003Cstrong>que\u003C\u002Fstrong> lorsqu'aucune équipe de validation n'est configurée.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>Le passage final \u003Cstrong>test → terminé reste manuel\u003C\u002Fstrong> — c'est un acte de validation humaine effectué après la commande de livraison \u003Ccode>.\u002Fship\u003C\u002Fcode>, jamais déclenché automatiquement par une cascade.\u003C\u002Fp>\n\n\u003Ch2 id=\"cascade-trigger-natif\">Cascade déclenchée nativement par la base de données (messagerie entrante)\u003C\u002Fh2>\n\n\u003Cp>Indépendamment des cascades applicatives décrites ci-dessus, un \u003Cstrong>déclencheur natif de base de données\u003C\u002Fstrong> propage automatiquement l'état \u003Cem>terminé\u003C\u002Fem> côté données, de façon transparente pour la couche applicative :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>Ce déclencheur s'active \u003Cstrong>après\u003C\u002Fstrong> toute mise à jour du statut d'un chantier.\u003C\u002Fli>\n  \u003Cli>Lorsque le chantier passe à \u003Cem>terminé\u003C\u002Fem> pour la première fois, il marque automatiquement comme \u003Cem>résolu\u003C\u002Fem> tous les emails entrants rattachés à ce chantier qui ne sont pas déjà résolus ou classés comme spam. La date de traitement est enregistrée au moment de la résolution.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Conséquence pratique\u003C\u002Fstrong> : un email entrant peut apparaître comme \u003Cem>résolu\u003C\u002Fem> sans qu'aucune action applicative explicite ne l'ait traité directement. Ce comportement est normal et intentionnel — il est à garder en mémoire lors du débogage d'un email passé à l'état résolu de manière apparemment autonome.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2 id=\"garde-fou-discovery\">Garde-fou : chantiers en phase de découverte et auto-explosion\u003C\u002Fh2>\n\n\u003Cp>La cascade travail → chantier comporte un garde-fou spécifique : si tous les travaux terminés sont encore en \u003Cstrong>phase de découverte\u003C\u002Fstrong>, le chantier n'est \u003Cstrong>pas\u003C\u002Fstrong> promu à l'état \u003Cem>test\u003C\u002Fem>. Ce garde-fou évite qu'un chantier nouvellement créé — ne portant qu'un seul travail d'analyse initiale — ne soit promu en préprod dès la fin de l'analyse, avant que les travaux d'implémentation n'aient été définis.\u003C\u002Fp>\n\n\u003Cp>Dans ce cas, un mécanisme d'\u003Cstrong>auto-explosion\u003C\u002Fstrong> peut se déclencher pour transformer le travail de découverte en travaux d'implémentation concrets. Ce mécanisme est protégé par \u003Cstrong>trois coupe-circuits successifs\u003C\u002Fstrong>, évalués dans l'ordre :\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>Une variable d'environnement permettant de désactiver globalement l'auto-explosion.\u003C\u002Fli>\n  \u003Cli>Un indicateur par chantier en base de données (\u003Ccode>auto_explode = FALSE\u003C\u002Fcode>).\u003C\u002Fli>\n  \u003Cli>Un drapeau global activable via fichier de configuration YAML.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>Si l'un de ces trois coupe-circuits est actif, le chantier reste en état \u003Cem>planification\u003C\u002Fem> pour revue manuelle. Chaque invocation du mécanisme d'auto-explosion est \u003Cstrong>auditée\u003C\u002Fstrong> dans un journal dédié, avec les statuts suivants :\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cem>killswitched\u003C\u002Fem> — au moins un coupe-circuit a bloqué l'opération ;\u003C\u002Fli>\n  \u003Cli>\u003Cem>success\u003C\u002Fem> — les travaux d'implémentation ont été créés avec succès ;\u003C\u002Fli>\n  \u003Cli>\u003Cem>validation_failed\u003C\u002Fem> — le plan proposé par le modèle de langage a été rejeté.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2 id=\"resolve-paused\">Resolve-paused — le pattern travail-bis\u003C\u002Fh2>\n\n\u003Cp>Lorsqu'un travail est mis en état \u003Cem>bloqué\u003C\u002Fem> (\u003Cem>paused\u003C\u002Fem>), un \u003Cstrong>travail-bis\u003C\u002Fstrong> est créé pour porter la résolution du blocage. Ce travail-bis référence le travail bloqué. Quand le bis passe à l'état \u003Cem>terminé\u003C\u002Fem>, le mécanisme de résolution nettoie automatiquement le travail parent bloqué, \u003Cstrong>avant\u003C\u002Fstrong> que la cascade travail → chantier ne soit réévaluée (car ce nettoyage modifie le décompte des travaux terminaux du chantier).\u003C\u002Fp>\n\n\u003Ch3>Effets produits lorsque le travail-bis passe à l'état terminé\u003C\u002Fh3>\n\n\u003Col>\n  \u003Cli>Le travail bloqué parent passe à l'état \u003Cem>terminé\u003C\u002Fem> via une mise à jour directe (contournant la récursion de cascade).\u003C\u002Fli>\n  \u003Cli>Toutes les tâches encore en attente du travail bloqué parent sont \u003Cem>annulées\u003C\u002Fem> par mise à jour groupée.\u003C\u002Fli>\n  \u003Cli>Le journal de décisions est mis à jour symétriquement sur le bis et sur le parent (traçabilité complète).\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Ch3>Garde-fous du mécanisme de résolution\u003C\u002Fh3>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Condition\u003C\u002Fth>\n      \u003Cth>Comportement\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Aucun travail parent référencé\u003C\u002Ftd>\n      \u003Ctd>Opération silencieuse sans effet (chemin hérité)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Travail parent et bis sur des chantiers différents\u003C\u002Ftd>\n      \u003Ctd>Erreur levée — résolution inter-chantier interdite\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Travail parent déjà terminé\u003C\u002Ftd>\n      \u003Ctd>Opération idempotente sans effet\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Cycle de référence mutuel (A → B et B → A)\u003C\u002Ftd>\n      \u003Ctd>Erreur levée — cycle interdit\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Travail parent introuvable (référence orpheline)\u003C\u002Ftd>\n      \u003Ctd>Avertissement journalisé, opération ignorée\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Statut du parent hors de {bloqué, dev, planification}\u003C\u002Ftd>\n      \u003Ctd>Avertissement journalisé, opération ignorée\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Des contraintes de base de données renforcent ce garde-fou : un index partiel sur la colonne de référence, une contrainte empêchant un travail de se référencer lui-même, et une clé étrangère configurée pour mettre la référence à \u003Ccode>NULL\u003C\u002Fcode> en cas de suppression du parent.\u003C\u002Fp>\n\n\u003Ch2 id=\"pieges-et-invariants\">Pièges &amp; invariants\u003C\u002Fh2>\n\n\u003Cul>\n  \u003Cli>\n    \u003Cstrong>Colonnes multi-lignes et lecteurs SQL\u003C\u002Fstrong> — certains champs textuels (lettre de mission, notes, plan de test, focus courant, prochaine action, tâche courante) contiennent des retours à la ligne. Les fonctions de lecture génériques basées sur un séparateur de colonnes échouent sur ces champs : utiliser systématiquement le lecteur CSV dédié pour toute nouvelle requête portant sur ces colonnes.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Synchronisation schéma DB \u002F déclaration d'entité\u003C\u002Fstrong> — ajouter une colonne en base de données sans l'ajouter à la liste des champs de l'entité applicative correspondante rend les mises à jour silencieusement inopérantes sur cette colonne. Vérifier systématiquement la cohérence entre le schéma DB et la déclaration d'entité lors de toute migration.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Statuts distincts par niveau (depuis le découpage en deux ensembles)\u003C\u002Fstrong> — les statuts valides pour un travail ne sont pas les mêmes que ceux valides pour un chantier. Un statut \u003Cem>review\u003C\u002Fem> est admis pour un travail mais hors-énumération pour un chantier. Toujours passer le bon ensemble de statuts lors de la validation. Ne jamais réutiliser l'ensemble canonique générique pour un nouveau code niveau-spécifique.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Contraintes de statut en base de données\u003C\u002Fstrong> — des contraintes \u003Ccode>CHECK\u003C\u002Fcode> sont présentes en base pour les deux niveaux. Un insert brut avec un statut hors-énumération sera rejeté par la base, y compris dans les scripts de migration.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Périmètre du chantier : la base de données fait autorité\u003C\u002Fstrong> — en cas de divergence entre la contrainte DB et la docstring applicative, toujours se référer à la contrainte DB.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Champs visuels sur les tâches\u003C\u002Fstrong> — deux champs optionnels permettent d'associer une intention visuelle et une URL de référence à une tâche. \u003Ccode>NULL\u003C\u002Fcode> signifie que la tâche n'a pas de composante visuelle. Le pipeline de vérification visuelle post-déploiement contrôle que l'intention est bien rendue. Ne pas omettre ces champs de la déclaration d'entité sous peine d'une régression silencieuse de mise à jour (cf. le piège de synchronisation décrit ci-dessus).\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Credentials de base de données\u003C\u002Fstrong> — les identifiants d'accès à la base sont lus exclusivement depuis les variables d'environnement du fichier de configuration racine. Ils ne doivent jamais apparaître en clair dans le code source ni dans la base de données.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Deux mécanismes de dépendances coexistent\u003C\u002Fstrong> — les dépendances \u003Cem>travail → travail\u003C\u002Fem> sont gérées par une colonne de référence directe sur l'entité travail ; les dépendances \u003Cem>tâche → tâche\u003C\u002Fem> au sein d'un même travail sont gérées par un graphe orienté acyclique N:M via une entité de dépendance dédiée. Ces deux mécanismes ne sont pas interchangeables. La contrainte inter-travaux sur les dépendances de tâches est purement applicative : la corrompre directement en SQL ne sera pas rattrapée par un déclencheur.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Le journal d'auto-explosion est un log passif, pas une file de traitement\u003C\u002Fstrong> — les entrées de ce journal sont créées et mises à jour par le pipeline d'explosion lui-même. Rien ne les consomme. Ne pas les utiliser pour déclencher ou relancer une opération d'explosion.\n  \u003C\u002Fli>\n\u003C\u002Ful>","\u003Ch2 id=\"chantiers-travaux-taches\">Projects, work items &amp; tasks\u003C\u002Fh2>\n\n\u003Ch3>What it is for\u003C\u002Fh3>\n\n\u003Cp>The Synedre OS harness organises all development work into a hierarchy persisted in the database — \u003Cstrong>1 project = N work items = N tasks\u003C\u002Fstrong> — so that the orchestrator (Atlas) and the agents can create, track, parallelise and close work without any out-of-database state. This page documents the data model, the Python interface that manipulates it, the seven-step creation procedure, the multi-session lock mechanism, and the status cascades (including the so-called \"work-item-bis\" resolution of a paused work item).\u003C\u002Fp>\n\n\u003Ch2 id=\"modele-de-donnees\">1. Data model\u003C\u002Fh2>\n\n\u003Ch3>1.1 Hierarchy\u003C\u002Fh3>\n\n\u003Cp>Three nested levels structure all work:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Project\u003C\u002Fstrong> — high-level steering unit\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Work item\u003C\u002Fstrong> — subset of a project, assignable to an agent\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Task\u003C\u002Fstrong> — atomic action within a work item\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Three main Python entities encapsulate these levels. Each extends a common base class and exposes validation, creation and update methods. Two secondary entities manage respectively the \u003Cstrong>multi-session locks\u003C\u002Fstrong> and the \u003Cstrong>inter-task dependencies\u003C\u002Fstrong>.\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Entity\u003C\u002Fth>\n      \u003Cth>Role\u003C\u002Fth>\n      \u003Cth>Primary key\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Project\u003C\u002Ftd>\n      \u003Ctd>Steering unit\u003C\u002Ftd>\n      \u003Ctd>project identifier\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Work item\u003C\u002Ftd>\n      \u003Ctd>Subset of a project\u003C\u002Ftd>\n      \u003Ctd>work item identifier\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Task\u003C\u002Ftd>\n      \u003Ctd>Atomic action\u003C\u002Ftd>\n      \u003Ctd>task identifier\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Auto-decomposition log\u003C\u002Ftd>\n      \u003Ctd>Audit of automatic decomposition runs\u003C\u002Ftd>\n      \u003Ctd>run identifier\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Lock\u003C\u002Ftd>\n      \u003Ctd>Mutex per project and per owner type\u003C\u002Ftd>\n      \u003Ctd>(project identifier, owner type)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Task dependency\u003C\u002Ftd>\n      \u003Ctd>Arc in the intra-work-item DAG\u003C\u002Ftd>\n      \u003Ctd>dependency identifier\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Migration note\u003C\u002Fstrong> — Table naming is heterogeneous for historical reasons. Do not rename without a dedicated project.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch3>1.2 Statuses\u003C\u002Fh3>\n\n\u003Cp>Canonical statuses are \u003Cstrong>split by level\u003C\u002Fstrong> to prevent cross-level validation errors.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Project statuses\u003C\u002Fstrong>: \u003Ccode>planning\u003C\u002Fcode>, \u003Ccode>dev\u003C\u002Fcode>, \u003Ccode>test\u003C\u002Fcode>, \u003Ccode>paused\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>cancelled\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Work item statuses\u003C\u002Fstrong>: \u003Ccode>discovery\u003C\u002Fcode>, \u003Ccode>planning\u003C\u002Fcode>, \u003Ccode>dev\u003C\u002Fcode>, \u003Ccode>paused\u003C\u002Fcode>, \u003Ccode>review\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>cancelled\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>An alias grouping the union of both sets is retained for backward compatibility with certain internal modules. All new code must use the set specific to the relevant level.\u003C\u002Fp>\n\n\u003Cp>The shared validation method accepts a \u003Ccode>status_set\u003C\u002Fcode> parameter: the Project entity passes it the project statuses, and the Work item entity passes it the work item statuses. An SQL \u003Ccode>INSERT\u003C\u002Fcode> with an out-of-enum status is rejected directly by the database through \u003Ccode>CHECK\u003C\u002Fcode> constraints placed on the two relevant tables.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Task statuses\u003C\u002Fstrong>: \u003Ccode>todo\u003C\u002Fcode>, \u003Ccode>doing\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode>, \u003Ccode>paused\u003C\u002Fcode>, \u003Ccode>cancelled\u003C\u002Fcode> — a distinct vocabulary, with no \u003Ccode>CHECK\u003C\u002Fcode> constraint in the database at this time.\u003C\u002Fp>\n\n\u003Cp>Canonical priorities are: \u003Ccode>P0\u003C\u002Fcode>, \u003Ccode>P1\u003C\u002Fcode>, \u003Ccode>P2\u003C\u002Fcode>, \u003Ccode>P3\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Ch3>1.3 Scopes\u003C\u002Fh3>\n\n\u003Cp>The \u003Ccode>scope\u003C\u002Fcode> field discriminates the synchronisation perimeter (monolith or OSS) and conditions certain validations.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Project scope\u003C\u002Fstrong> (column \u003Ccode>scope\u003C\u002Fcode>, nullable) — values authorised by a database constraint:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>synedre | codemyshop-oss | codemyshop-enterprise | tenant | business | juridique | negociation | conseil\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Warning\u003C\u002Fstrong> — The internal documentation for the creation method only lists the first five scopes. The database authorises three additional ones (\u003Ccode>juridique\u003C\u002Fcode>, \u003Ccode>negociation\u003C\u002Fcode>, \u003Ccode>conseil\u003C\u002Fcode>), added for Atlas intents. \u003Cstrong>The database takes precedence.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>\u003Cstrong>Task scope\u003C\u002Fstrong> — validated both in code and by a database constraint:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>synedre-internal | codemyshop-oss | codemyshop-enterprise | tenant-single | tenant-multi | infra | doctrine\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>An invalid task scope raises a \u003Ccode>ValueError\u003C\u002Fcode> at task creation time.\u003C\u002Fp>\n\n\u003Ch3>1.4 Notable fields\u003C\u002Fh3>\n\n\u003Cp>\u003Cstrong>At the project level\u003C\u002Fstrong>:\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Field\u003C\u002Fth>\n      \u003Cth>Role\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>mission_letter\u003C\u002Fcode> (free text)\u003C\u002Ftd>\n      \u003Ctd>Structured mission letter (step 2 of the doctrine)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>preprod_test_plan\u003C\u002Fcode> (free text)\u003C\u002Ftd>\n      \u003Ctd>Pre-production test plan — Markdown or YAML v2 (presence of \u003Ccode>version:\u003C\u002Fcode> triggers automatic QA)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>ship_command\u003C\u002Fcode> (short string)\u003C\u002Ftd>\n      \u003Ctd>Exact closure command, e.g. \u003Ccode>.\u002Fship synedre-os\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>auto_explode\u003C\u002Fcode> (boolean, default \u003Ccode>true\u003C\u002Fcode>)\u003C\u002Ftd>\n      \u003Ctd>Per-project kill-switch for automatic discovery→implementation decomposition\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>mode_auto\u003C\u002Fcode> (boolean)\u003C\u002Ftd>\n      \u003Ctd>Project autonomy V1 — the agent executes without validating each action\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>max_cost_eur\u003C\u002Fcode> (numeric)\u003C\u002Ftd>\n      \u003Ctd>Per-project AI cost cap (\u003Ccode>NULL\u003C\u002Fcode> = unlimited)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>client_id\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Reference to a client, or \u003Ccode>NULL\u003C\u002Fcode> for an internal project\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>\u003Cstrong>At the work item level\u003C\u002Fstrong>: notable fields include the codename and prompt of the assigned agent, the zone perimeter, the exit criteria, structured JSON blocks for context \u002F decisions \u002F findings, an inter-work-item dependency identifier (with cycle detection), the resolution identifier for the work-item-bis pattern (see §6), as well as metadata covering steering, contractual model, auto mode, repository paths and associated backlogs.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>At the task level\u003C\u002Fstrong>: notable fields include the assignee codename, the token estimate, the recommended model, the display position, the result of the last test, actual metrics (tokens, cost), start and end timestamps, and two visual fields (\u003Ccode>visual_intent\u003C\u002Fcode> and \u003Ccode>visual_url\u003C\u002Fcode>) used by the post-deployment visual verification pipeline — a \u003Ccode>NULL\u003C\u002Fcode> value indicates a non-visual task.\u003C\u002Fp>\n\n\u003Ch3>1.5 Agent recruitment — persistence\u003C\u002Fh3>\n\n\u003Cp>Agent recruitment onto a project (step 3 of the doctrine) is persisted in a dedicated table, \u003Cstrong>distinct\u003C\u002Fstrong> from the assignment field of an individual task (which only carries the assignment of a single task).\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Column\u003C\u002Fth>\n      \u003Cth>Role\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Assignment identifier (PK)\u003C\u002Ftd>\n      \u003Ctd>Primary key of the assignment\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Project identifier\u003C\u002Ftd>\n      \u003Ctd>Link to the project\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Agent codename\u003C\u002Ftd>\n      \u003Ctd>Recruited agent (e.g. Mitnick, Turing, Brunel…)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Role\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>production\u003C\u002Fcode> \u002F \u003Ccode>lead_production\u003C\u002Fcode> \u002F \u003Ccode>validation\u003C\u002Fcode> \u002F \u003Ccode>lead_validation\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Position\u003C\u002Ftd>\n      \u003Ctd>Display order\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Assignment \u002F unassignment date\u003C\u002Ftd>\n      \u003Ctd>Assignment lifecycle\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Notes\u003C\u002Ftd>\n      \u003Ctd>Recruitment justification\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>The task→work-item cascade logic queries this table to detect the presence of a QA team: if an agent with a \u003Ccode>validation\u003C\u002Fcode> or \u003Ccode>lead_validation\u003C\u002Fcode> role is present, the cascade switches to a QA run rather than a simple status change (see §5).\u003C\u002Fp>\n\n\u003Ch3>1.6 Automatic decomposition log\u003C\u002Fh3>\n\n\u003Cp>A dedicated entity persists one audit record per invocation of the module that transforms the decisions of a \u003Cem>discovery\u003C\u002Fem> work item into implementation work items (phases A\u002FB\u002FC). One record equals one invocation. It is created with the status \u003Ccode>pending\u003C\u002Fcode> at the start of the pipeline, then updated at the end.\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Status\u003C\u002Fth>\n      \u003Cth>Meaning\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>pending\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Run in progress\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>success\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>Implementation work items created\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>validation_failed\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>LLM plan rejected by validation\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>llm_failed\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>LLM API error\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Ccode>killswitched\u003C\u002Fcode>\u003C\u002Ftd>\n      \u003Ctd>One of the three kill-switches interrupted execution\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Key fields include the produced JSON plan, the number of work items created, token and cost metrics, and \u003Ccode>dry_run\u003C\u002Fcode> and \u003Ccode>force\u003C\u002Fcode> flags. Record retention is configurable (default: 90 days). A global daily quota is also configurable: if the number of runs in the last 24 hours exceeds it, the decomposition is marked \u003Ccode>killswitched\u003C\u002Fcode> without an error. The run history is accessible from the dashboard via the \u003Ccode>\u002Fchantier explode list\u003C\u002Fcode> command.\u003C\u002Fp>\n\n\u003Ch3>1.7 Task→task dependencies — intra-work-item DAG\u003C\u002Fh3>\n\n\u003Cp>The task dependency engine manages an N:M directed acyclic graph (DAG). \u003Cstrong>Not to be confused\u003C\u002Fstrong> with inter-work-item dependencies (a column on the Work item entity): these two mechanisms operate at distinct levels.\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>Semantics\u003C\u002Fstrong>: a blocked task cannot start until the blocking task reaches the \u003Ccode>done\u003C\u002Fcode> status. A strong business constraint applies: both tasks \u003Cstrong>must\u003C\u002Fstrong> belong to the \u003Cstrong>same\u003C\u002Fstrong> work item — a cross-work-item dependency raises a dedicated application error (rejected in code, no SQL trigger).\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Operation\u003C\u002Fth>\n      \u003Cth>Conceptual method\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Add a dependency\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>add_dep(blocked, blocker)\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>List dependencies of a work item\u003C\u002Ftd>\n      \u003Ctd>JOIN blocker\u002Fblocked with task titles\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>List blockers of a task\u003C\u002Ftd>\n      \u003Ctd>Direct blockers of a given task\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Check whether a task is unblocked\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>is_tache_unblocked(id)\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Remove a dependency\u003C\u002Ftd>\n      \u003Ctd>Idempotent — no effect if non-existent\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>\u003Cstrong>Cycle prevention\u003C\u002Fstrong>: before any addition, a depth-first search (DFS) is performed from the blocking task through the existing graph. If the traversal reaches the blocked task, a cycle error is raised before the insert. A maximum-depth guard (100 levels) protects against corrupted DAGs.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>A blocker with a \u003Ccode>cancelled\u003C\u002Fcode> status is considered \u003Cstrong>resolved\u003C\u002Fstrong> in the unblocking check, in order to avoid deadlocks. This behaviour also applies to selective auto mode, which only starts a task once all its blockers are resolved.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"creation-atomique\">Atomic Creation of a Work Package\u003C\u002Fh2>\n\n\u003Cp>The creation method exposed by the project management engine is the \u003Cstrong>sole canonical path\u003C\u002Fstrong> for instantiating a work package: it atomically inserts the work package, its first work item, and at least one task, thereby preventing orphaned work packages from appearing.\u003C\u002Fp>\n\n\u003Ch3>Signature\u003C\u002Fh3>\n\n\u003Cp>The method accepts the following parameters:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>codename\u003C\u002Fstrong>: kebab-case identifier of the work package.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>title\u003C\u002Fstrong>: human-readable label of the work package.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>first_travail\u003C\u002Fstrong> (required): dictionary describing the first work item — codename, title, and optional fields such as priority, current phase, current task, next action, blockers.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>first_taches\u003C\u002Fstrong> (preferred, v3 doctrine): list of dictionaries describing the initial tasks. Each task carries at minimum a title and the codename of the assigned agent; optional fields enrich the description (priority, token estimate, estimated duration, description, position, recommended model, scope, visual intent, visual URL).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>first_tache\u003C\u002Fstrong>: singular form retained for backward compatibility — must not be combined with \u003Ccode>first_taches\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>client_id\u003C\u002Fstrong>: optional reference to the relevant tenant.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>priority\u003C\u002Fstrong>: priority level among \u003Ccode>P0\u003C\u002Fcode>, \u003Ccode>P1\u003C\u002Fcode>, \u003Ccode>P2\u003C\u002Fcode> (default), \u003Ccode>P3\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>scope\u003C\u002Fstrong>, \u003Cstrong>current_focus\u003C\u002Fstrong>, \u003Cstrong>notes\u003C\u002Fstrong>: optional contextual fields.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>The method returns a confirmation dictionary (see §&nbsp;Return Value below).\u003C\u002Fp>\n\n\u003Ch3>Blocking Validations\u003C\u002Fh3>\n\n\u003Cp>All validations are accumulated before the first INSERT; a single violation raises a \u003Ccode>ValidationError\u003C\u002Fcode> and cancels the entire operation:\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Kebab-case format of codename\u003C\u002Fstrong> — regular expression \u003Ccode>^[a-z0-9][a-z0-9\\-]{2,62}[a-z0-9]$\u003C\u002Fcode>: lowercase letters, digits and hyphens, 4 to 64 characters.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Codename uniqueness\u003C\u002Fstrong> — an existence check is performed; if the codename is already in use, creation is rejected.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Valid priority\u003C\u002Fstrong> — must belong to \u003Ccode>{P0, P1, P2, P3}\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Valid initial work item\u003C\u002Fstrong> — codename and title must be provided and non-empty.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>At least one task\u003C\u002Fstrong> — \u003Ccode>first_tache\u003C\u002Fcode> or \u003Ccode>first_taches\u003C\u002Fcode> is required; supplying both simultaneously is an error.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Known agent\u003C\u002Fstrong> — each assigned agent codename is verified against the active agent registry.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>At least two distinct agents if scope is of tenant type\u003C\u002Fstrong> (v3 doctrine) — if the \u003Ccode>scope\u003C\u002Fcode> field begins with \u003Ccode>tenant\u003C\u002Fcode>, the set of assignees must contain at least two distinct codenames.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>\u003Cstrong>Non-blocking warning:\u003C\u002Fstrong> the absence of a token estimate on a task generates a warning but does not prevent creation — an automatic estimator fills in the missing value (see §&nbsp;Estimation below).\u003C\u002Fp>\n\n\u003Ch3>Atomicity and Rollback\u003C\u002Fh3>\n\n\u003Cp>The insertion is performed within a single transaction (\u003Ccode>BEGIN … COMMIT\u003C\u002Fcode>) with the immediate-abort-on-error option. If a single INSERT fails, the entire transaction is rolled back: neither a work package without a work item, nor a work item without a task can exist. Work items and tasks reference their parent via a sub-select on the work package codename, guaranteeing foreign key resolution in insertion order.\u003C\u002Fp>\n\n\u003Ch3>Token Estimation and Recommended Model\u003C\u002Fh3>\n\n\u003Cp>If the recommended model is not explicitly provided for a task, it is computed automatically according to the following rule:\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Condition\u003C\u002Fth>\n      \u003Cth>Selected Model\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Priority P0 or task in recurring failure (≥&nbsp;2 iterations in \u003Ccode>fail\u003C\u002Fcode> state)\u003C\u002Ftd>\n      \u003Ctd>High-capacity model (e.g. opus)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Estimate ≥&nbsp;8,000 tokens or estimate absent\u003C\u002Ftd>\n      \u003Ctd>High-capacity model (e.g. opus)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Estimate ≥&nbsp;1,500 tokens\u003C\u002Ftd>\n      \u003Ctd>Intermediate model (e.g. sonnet)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Otherwise\u003C\u002Ftd>\n      \u003Ctd>Lightweight model (e.g. haiku)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>When creating a task outside the skeleton, if the token estimate is absent, the engine computes it via a dedicated estimator, then recomputes the recommended model on the persisted row.\u003C\u002Fp>\n\n\u003Ch3>Attached Skills and Tools\u003C\u002Fh3>\n\n\u003Cp>After insertion, the skeleton attaches the optional skills and tools declared per task. An unknown skill or tool does not raise an error: a warning is emitted and a proposal is queued for validation.\u003C\u002Fp>\n\n\u003Ch3>Return Value\u003C\u002Fh3>\n\n\u003Cp>The method returns a dictionary containing:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>The internal identifiers of the created work package, work item, and tasks.\u003C\u002Fli>\n  \u003Cli>An \u003Ccode>id_tache\u003C\u002Fcode> field (backward compatibility, equal to the first in the list) and \u003Ccode>id_taches\u003C\u002Fcode> (full list, v3 doctrine).\u003C\u002Fli>\n  \u003Cli>A \u003Ccode>codenames\u003C\u002Fcode> object grouping the codenames of the work package, work item, and tasks.\u003C\u002Fli>\n  \u003Cli>A \u003Ccode>warnings\u003C\u002Fcode> list of non-blocking warnings emitted during creation.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch3>Command-Line Interface\u003C\u002Fh3>\n\n\u003Cp>The project management engine exposes a CLI allowing atomic creation to be triggered by passing the JSON payload on standard input or via a file. The \u003Ccode>--dry-run\u003C\u002Fcode> option prints the constructed payload without performing any insertion. Additional verbs are available — list, display, work item creation, update, close, resolve — accessible via the \u003Ccode>--help\u003C\u002Fcode> option.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>A guard hook detects any attempt at direct SQL insertion on the project engine tables made outside this facade, and emits a warning.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch2 id=\"procedure-creation-chantier\">7-Step Work Package Creation Procedure\u003C\u002Fh2>\n\n\u003Cp>No work package may be created without following these steps in order. For work packages triggered by email, the dedicated facade executes steps \u003Cstrong>0a through 4\u003C\u002Fstrong> under constraint (blocking if attachment scan is not clean or fewer than 2 agents). It stops at the atomic skeleton creation (step 4) and performs \u003Cstrong>neither step 2\u003C\u002Fstrong> (drafting the mission brief: the facade generates only generic task titles of the form \u003Cem>@agent — analysis…\u003C\u002Fem>) \u003Cstrong>nor step 5\u003C\u002Fstrong> (post-skeleton update of the mission fields, scope, test plan, and delivery command). These two steps remain to be completed manually after the facade call.\u003C\u002Fp>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Step\u003C\u002Fth>\n      \u003Cth>Action\u003C\u002Fth>\n      \u003Cth>Mechanism\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>0a\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Read the email request verbatim\u003C\u002Ftd>\n      \u003Ctd>Read via the internal inbox API\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>0b\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Extract and scan attachments before any reading\u003C\u002Ftd>\n      \u003Ctd>Attachment extraction, then antivirus\u002Fantimalware analysis. Non-\u003Ccode>clean\u003C\u002Fcode> verdict → immediate block\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>1\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Audit of available agents\u003C\u002Ftd>\n      \u003Ctd>Query of the active agent registry (codename, nickname, role, group)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>2\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Draft the mission brief\u003C\u002Ftd>\n      \u003Ctd>Structure: OBJECTIVE \u002F CONTEXT \u002F SCOPE \u002F DELIVERABLES \u002F CONSTRAINTS \u002F DOCTRINE, based on content read in steps 0a\u002F0b\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>3\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Explicit recruitment\u003C\u002Ftd>\n      \u003Ctd>At least 2 distinct agents if scope is of tenant type\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>4\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Atomic skeleton creation\u003C\u002Ftd>\n      \u003Ctd>See §&nbsp;Atomic Creation\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>\u003Cstrong>5\u003C\u002Fstrong>\u003C\u002Ftd>\n      \u003Ctd>Post-skeleton update\u003C\u002Ftd>\n      \u003Ctd>Persistence of the mission brief, scope, pre-production test plan, and delivery command via a named UPDATE on the codename\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch3>Email-Based Creation Facade\u003C\u002Fh3>\n\n\u003Cp>The email-based creation facade follows this flow:\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Email retrieval\u003C\u002Fstrong> — call to the internal inbox API (never direct SQL).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Attachment extraction and scan\u003C\u002Fstrong> — if an attachment is not \u003Ccode>clean\u003C\u002Fcode>, the facade displays a blocking message and exits with code \u003Ccode>2\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Request summary\u003C\u002Fstrong> — extraction of title, client, priority, and scope.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Agent listing and recruitment\u003C\u002Fstrong> — if fewer than 2 distinct agents are selected, blocking with exit code \u003Ccode>3\u003C\u002Fcode>.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Skeleton construction and insertion\u003C\u002Fstrong> — one task per agent with generic titles, initial work item in \u003Ccode>discovery\u003C\u002Fcode> phase. Corresponds to doctrinal step 4. No mission brief update or doctrinal step 5 is performed.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>\u003Cstrong>Exit codes:\u003C\u002Fstrong> \u003Ccode>2\u003C\u002Fcode> = non-clean attachment, \u003Ccode>3\u003C\u002Fcode> = fewer than 2 agents, \u003Ccode>4\u003C\u002Fcode> = validation error.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>Two distinct control levels on agent count:\u003C\u002Fstrong> the facade \u003Cem>always\u003C\u002Fem> requires at least 2 distinct agents, regardless of the declared scope. The atomic creation engine, for its part, only enforces this constraint when the \u003Ccode>scope\u003C\u002Fcode> field begins with \u003Ccode>tenant\u003C\u002Fcode>. The default scope \u003Ccode>tenant\u003C\u002Fcode> aligns the facade's blocking message, but does not condition its own check.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cblockquote>\n  \u003Cp>The facade creates a single initial work item in \u003Ccode>discovery\u003C\u002Fcode> phase. The transition to implementation work items remains manual or goes through the automatic work item explosion mechanism (see §&nbsp;Auto-explode).\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch3>Test Plan and Delivery Command Fields (Step 5)\u003C\u002Fh3>\n\n\u003Cp>These two fields are required for any non-trivial work package. In their absence, the work package update cascade records a non-blocking warning, but the \"READY TO SHIP\" banner cannot be validated. If the test plan is written in YAML v2 format (with a \u003Ccode>version:\u003C\u002Fcode> header), the cascade automatically triggers a Playwright test session in the background. An environment switch allows this automatic triggering to be disabled.\u003C\u002Fp>\n\n\u003Ch2 id=\"lock-multi-session\">Multi-Session Locking\u003C\u002Fh2>\n\n\u003Cp>The locking mechanism allows N \u003Cstrong>different\u003C\u002Fstrong> work packages to be opened in parallel (N terminals or workers) while refusing two concurrent sessions on the \u003Cstrong>same\u003C\u002Fstrong> work package. This is not an ergonomic blocker, but a collision-prevention safety net.\u003C\u002Fp>\n\n\u003Ch3>Data Model\u003C\u002Fh3>\n\n\u003Cp>The lock relies on a dedicated table with a composite primary key \u003Cstrong>(work package identifier, owner type)\u003C\u002Fstrong>. The owner type is constrained to two values:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>user\u003C\u002Fstrong>: interactive session (for example, an SSH session from a human operator).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>worker\u003C\u002Fstrong>: agent subprocess spawned by the automated task engine.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Both types may coexist on the same work package; each type contests only its own slot. The type is detected via an environment variable, with fallback to inspection of the parent process tree. The session identifier is resolved in order: Claude session environment variable &gt; transcript path fingerprint &gt; PID\u002Fuser\u002Fhost combination.\u003C\u002Fp>\n\n\u003Ch3>TTL and Acquisition\u003C\u002Fh3>\n\n\u003Cp>The time-to-live of an inactive lock is \u003Cstrong>30 minutes\u003C\u002Fstrong>. Acquisition attempts an \u003Ccode>INSERT … ON CONFLICT … DO UPDATE\u003C\u002Fcode> that succeeds only if the session identifier matches the current holder \u003Cem>or\u003C\u002Fem> if the lock has expired (last activity more than 30 minutes ago). Otherwise, the method returns \u003Ccode>{acquired: False, owner: {…}}\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>The lock is acquired automatically when retrieving the context of a work package. On refusal, a \u003Ccode>ChantierLockedError\u003C\u002Fcode> exception is raised, intercepted by the work package management skill. An environment switch allows the locking mechanism to be disabled entirely.\u003C\u002Fp>\n\n\u003Ch3>Available Operations\u003C\u002Fh3>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Operation\u003C\u002Fth>\n      \u003Cth>Description\u003C\u002Fth>\n      \u003Cth>CLI Option\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>Active lock inventory\u003C\u002Ftd>\n      \u003Ctd>Lists live locks with their owner and last activity timestamp\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>--locks\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Release own lock\u003C\u002Ftd>\n      \u003Ctd>Idempotent deletion of the lock corresponding to the current session\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>--release &lt;codename&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Reclaim lock (kick)\u003C\u002Ftd>\n      \u003Ctd>Forces reclaim of the same-type slot, evicting the previous owner\u003C\u002Ftd>\n      \u003Ctd>\u003Ccode>--force-claim &lt;codename&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Keep lock alive\u003C\u002Ftd>\n      \u003Ctd>Updates the activity timestamp to push back TTL expiration\u003C\u002Ftd>\n      \u003Ctd>—\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch3>Three Release Safety Nets\u003C\u002Fh3>\n\n\u003Col>\n  \u003Cli>\u003Cstrong>Session-end hook\u003C\u002Fstrong> — declared in the Claude hooks configuration, releases the lock promptly at the end of an interactive session.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>TTL cron task\u003C\u002Fstrong> — runs every 5 minutes, removes locks whose last activity exceeds 30 minutes. Idempotent, always exits 0, silent if the database is unavailable or the table is absent. This is the true safety net against crashes, network outages, or abrupt process terminations.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Manual force-claim\u003C\u002Fstrong> — used as a last resort to reclaim an orphaned lock.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"cascades-de-statut\">Status Cascades\u003C\u002Fh2>\n\n\u003Cp>Entity update methods are overloaded to propagate terminal statuses from one level to the next. \u003Cstrong>The cascade is limited to one step per level\u003C\u002Fstrong> — each step autonomously re-triggers its own cascade.\u003C\u002Fp>\n\n\u003Ch3>Task → job propagation logic\u003C\u002Fh3>\n\n\u003Cp>When all tasks of a job reach a terminal status (\u003Cstrong>completed\u003C\u002Fstrong> or \u003Cstrong>cancelled\u003C\u002Fstrong>) and at least one task is completed, the system evaluates whether a validation team is present on the project:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>With a validation team\u003C\u002Fstrong> — the reflex engine delegates to a quality-control subprocess. If the overall verdict is positive, the job transitions to the \u003Cem>completed\u003C\u002Fem> state. Otherwise, corrective tasks are created and the job remains active in its development phase.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Without a validation team\u003C\u002Fstrong> — the transition to \u003Cem>completed\u003C\u002Fem> is performed directly.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>Any exception raised by the quality-control subprocess is \u003Cstrong>fail-safe\u003C\u002Fstrong>: it is logged without blocking or modifying the job's state.\u003C\u002Fp>\n\n\u003Ch3>Job → project propagation logic\u003C\u002Fh3>\n\n\u003Cp>When all jobs of a project reach a terminal status and at least one is completed, the project is promoted to the \u003Cem>test\u003C\u002Fem> state (pre-production, pending review). This transition also triggers, in a non-blocking manner, the entropy detector described in the following section.\u003C\u002Fp>\n\n\u003Cblockquote>\n  \u003Cp>\u003Cstrong>⚠️ The task → job transition is not always direct.\u003C\u002Fstrong> As soon as a project has at least one agent assigned to a validation role, the cascade delegates to the quality-control subprocess rather than performing the bump immediately. The job transitions to the \u003Cem>completed\u003C\u002Fem> state only if the overall verdict is positive; otherwise, corrective tasks are generated and the job remains active. The direct bump occurs \u003Cstrong>only\u003C\u002Fstrong> when no validation team is configured.\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>The final \u003Cstrong>test → completed transition remains manual\u003C\u002Fstrong> — it is a human validation action performed after the \u003Ccode>.\u002Fship\u003C\u002Fcode> delivery command, never triggered automatically by a cascade.\u003C\u002Fp>\n\n\u003Ch2 id=\"cascade-trigger-natif\">Cascade Natively Triggered by the Database (Incoming Messages)\u003C\u002Fh2>\n\n\u003Cp>Independently of the application-level cascades described above, a \u003Cstrong>native database trigger\u003C\u002Fstrong> automatically propagates the \u003Cem>completed\u003C\u002Fem> state at the data layer, transparently to the application layer:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>This trigger fires \u003Cstrong>after\u003C\u002Fstrong> any status update on a project.\u003C\u002Fli>\n  \u003Cli>When a project transitions to \u003Cem>completed\u003C\u002Fem> for the first time, it automatically marks as \u003Cem>resolved\u003C\u002Fem> all incoming emails attached to that project that are not already resolved or classified as spam. The processing timestamp is recorded at the time of resolution.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Practical consequence\u003C\u002Fstrong>: an incoming email may appear as \u003Cem>resolved\u003C\u002Fem> without any explicit application-level action having directly processed it. This behavior is normal and intentional — it should be kept in mind when debugging an email that has transitioned to the resolved state in an apparently autonomous manner.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2 id=\"garde-fou-discovery\">Safeguard: Projects in Discovery Phase and Auto-Explosion\u003C\u002Fh2>\n\n\u003Cp>The job → project cascade includes a specific safeguard: if all completed jobs are still in the \u003Cstrong>discovery phase\u003C\u002Fstrong>, the project is \u003Cstrong>not\u003C\u002Fstrong> promoted to the \u003Cem>test\u003C\u002Fem> state. This safeguard prevents a newly created project — carrying only a single initial analysis job — from being promoted to pre-production as soon as the analysis is complete, before implementation jobs have been defined.\u003C\u002Fp>\n\n\u003Cp>In this case, an \u003Cstrong>auto-explosion\u003C\u002Fstrong> mechanism may trigger to transform the discovery job into concrete implementation jobs. This mechanism is protected by \u003Cstrong>three successive circuit breakers\u003C\u002Fstrong>, evaluated in order:\u003C\u002Fp>\n\n\u003Col>\n  \u003Cli>An environment variable allowing auto-explosion to be globally disabled.\u003C\u002Fli>\n  \u003Cli>A per-project flag in the database (\u003Ccode>auto_explode = FALSE\u003C\u002Fcode>).\u003C\u002Fli>\n  \u003Cli>A global flag activatable via a YAML configuration file.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>If any of these three circuit breakers is active, the project remains in the \u003Cem>planning\u003C\u002Fem> state for manual review. Each invocation of the auto-explosion mechanism is \u003Cstrong>audited\u003C\u002Fstrong> in a dedicated log, with the following statuses:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cem>killswitched\u003C\u002Fem> — at least one circuit breaker blocked the operation;\u003C\u002Fli>\n  \u003Cli>\u003Cem>success\u003C\u002Fem> — implementation jobs were created successfully;\u003C\u002Fli>\n  \u003Cli>\u003Cem>validation_failed\u003C\u002Fem> — the plan proposed by the language model was rejected.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2 id=\"resolve-paused\">Resolve-Paused — The Companion Job Pattern\u003C\u002Fh2>\n\n\u003Cp>When a job is placed in the \u003Cem>paused\u003C\u002Fem> state, a \u003Cstrong>companion job\u003C\u002Fstrong> is created to handle the resolution of the blockage. This companion job references the blocked job. When the companion transitions to the \u003Cem>completed\u003C\u002Fem> state, the resolution mechanism automatically cleans up the blocked parent job \u003Cstrong>before\u003C\u002Fstrong> the job → project cascade is re-evaluated (since this cleanup modifies the count of terminal jobs in the project).\u003C\u002Fp>\n\n\u003Ch3>Effects produced when the companion job transitions to completed\u003C\u002Fh3>\n\n\u003Col>\n  \u003Cli>The blocked parent job transitions to the \u003Cem>completed\u003C\u002Fem> state via a direct update (bypassing cascade recursion).\u003C\u002Fli>\n  \u003Cli>All tasks of the blocked parent job that are still pending are \u003Cem>cancelled\u003C\u002Fem> via a bulk update.\u003C\u002Fli>\n  \u003Cli>The decision log is updated symmetrically on both the companion and the parent (full traceability).\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Ch3>Resolution mechanism safeguards\u003C\u002Fh3>\n\n\u003Ctable>\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth>Condition\u003C\u002Fth>\n      \u003Cth>Behavior\u003C\u002Fth>\n    \u003C\u002Ftr>\n  \u003C\u002Fthead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd>No parent job referenced\u003C\u002Ftd>\n      \u003Ctd>Silent no-op (legacy path)\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Parent job and companion on different projects\u003C\u002Ftd>\n      \u003Ctd>Error raised — cross-project resolution is forbidden\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Parent job already completed\u003C\u002Ftd>\n      \u003Ctd>Idempotent no-op\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Mutual reference cycle (A → B and B → A)\u003C\u002Ftd>\n      \u003Ctd>Error raised — cycles are forbidden\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Parent job not found (orphaned reference)\u003C\u002Ftd>\n      \u003Ctd>Warning logged, operation ignored\u003C\u002Ftd>\n    \u003C\u002Ftr>\n    \u003Ctr>\n      \u003Ctd>Parent status outside {paused, dev, planning}\u003C\u002Ftd>\n      \u003Ctd>Warning logged, operation ignored\u003C\u002Ftd>\n    \u003C\u002Ftr>\n  \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Cp>Database constraints reinforce this safeguard: a partial index on the reference column, a constraint preventing a job from referencing itself, and a foreign key configured to set the reference to \u003Ccode>NULL\u003C\u002Fcode> upon parent deletion.\u003C\u002Fp>\n\n\u003Ch2 id=\"pieges-et-invariants\">Pitfalls &amp; Invariants\u003C\u002Fh2>\n\n\u003Cul>\n  \u003Cli>\n    \u003Cstrong>Multi-line columns and SQL readers\u003C\u002Fstrong> — certain text fields (mission statement, notes, test plan, current focus, next action, current task) contain line breaks. Generic read functions based on a column separator fail on these fields: always use the dedicated CSV reader for any new query targeting these columns.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>DB schema \u002F entity declaration synchronization\u003C\u002Fstrong> — adding a column to the database without adding it to the corresponding application entity's field list causes updates to that column to silently have no effect. Always verify consistency between the DB schema and the entity declaration during any migration.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Distinct statuses per level (since the split into two sets)\u003C\u002Fstrong> — the valid statuses for a job are not the same as those valid for a project. A \u003Cem>review\u003C\u002Fem> status is permitted for a job but is out-of-enumeration for a project. Always pass the correct status set during validation. Never reuse the generic canonical set for new level-specific code.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Database status constraints\u003C\u002Fstrong> — \u003Ccode>CHECK\u003C\u002Fcode> constraints are present in the database for both levels. A raw insert with an out-of-enumeration status will be rejected by the database, including in migration scripts.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Project scope: the database is authoritative\u003C\u002Fstrong> — in case of discrepancy between the DB constraint and the application docstring, always defer to the DB constraint.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Visual fields on tasks\u003C\u002Fstrong> — two optional fields allow a visual intent and a reference URL to be associated with a task. \u003Ccode>NULL\u003C\u002Fcode> means the task has no visual component. The post-deployment visual verification pipeline checks that the intent is correctly rendered. Do not omit these fields from the entity declaration, as this will cause a silent update regression (see the synchronization pitfall described above).\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Database credentials\u003C\u002Fstrong> — database access credentials are read exclusively from the environment variables in the root configuration file. They must never appear in plain text in the source code or in the database.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>Two dependency mechanisms coexist\u003C\u002Fstrong> — \u003Cem>job → job\u003C\u002Fem> dependencies are managed by a direct reference column on the job entity; \u003Cem>task → task\u003C\u002Fem> dependencies within a single job are managed by an N:M directed acyclic graph via a dedicated dependency entity. These two mechanisms are not interchangeable. The inter-job constraint on task dependencies is purely at the application level: corrupting it directly via SQL will not be caught by a trigger.\n  \u003C\u002Fli>\n  \u003Cli>\n    \u003Cstrong>The auto-explosion log is a passive log, not a processing queue\u003C\u002Fstrong> — entries in this log are created and updated by the explosion pipeline itself. Nothing consumes them. Do not use them to trigger or re-run an explosion operation.\n  \u003C\u002Fli>\n\u003C\u002Ful>",{"slug":14,"chapterNum":15,"title":16,"titleEn":17},"agentic-core","03","Le cœur agentique : Atlas et les agents","The Agentic Core: Atlas and the Agents",{"slug":19,"chapterNum":20,"title":21,"titleEn":22},"automates","05","Automates, crons & runs","Automations, crons & runs",{"chapters":24},[25,32,39,42,43,46,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":14,"chapterNum":15,"title":16,"titleEn":17,"summary":40,"summaryEn":41},"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":5,"chapterNum":6,"title":7,"titleEn":8,"summary":9,"summaryEn":10},{"slug":19,"chapterNum":20,"title":21,"titleEn":22,"summary":44,"summaryEn":45},"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.",{"slug":47,"chapterNum":48,"title":49,"titleEn":50,"summary":51,"summaryEn":52},"hub","06","Le Hub (\u002Fhub\u002F*)","The Hub (\u002Fhub\u002F*)","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."]