Chapitres

DOC-05 / Référence technique · Chapitre 04

Chantiers, travaux & tâches — modèle de données et API

Décrit la hiérarchie chantier/travail/tâche de Synedre OS, le modèle de données DB, les statuts/scopes canoniques et l'API Python associée.

Chantiers, travaux & tâches

À quoi ça sert

Le harness Synedre OS organise tout travail de développement en une hiérarchie persistée en base — 1 chantier = N travaux = N tâches — 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).

1. Modèle de données

1.1 Hiérarchie

Trois niveaux imbriqués structurent tout travail :

  • Chantier — unité de pilotage de haut niveau
  • Travail — sous-ensemble d'un chantier, assignable à un agent
  • Tâche — action atomique au sein d'un travail

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 verrous multi-session et les dépendances entre tâches.

Entité Rôle Clé primaire
Chantier Unité de pilotage identifiant chantier
Travail Sous-ensemble d'un chantier identifiant travail
Tâche Action atomique identifiant tâche
Journal d'auto-décomposition Audit des runs de décomposition automatique identifiant run
Verrou Mutex par chantier et par type de propriétaire (identifiant chantier, type propriétaire)
Dépendance tâche Arc du DAG intra-travail identifiant dépendance

Note de migration — La nomenclature des tables est hétérogène pour des raisons historiques. Ne pas renommer sans chantier dédié.

1.2 Statuts

Les statuts canoniques sont scindés par niveau pour prévenir les validations croisées.

Statuts chantier : planning, dev, test, paused, done, cancelled.

Statuts travail : discovery, planning, dev, paused, review, done, cancelled.

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é.

La validation commune accepte un paramètre status_set : l'entité Chantier lui passe les statuts chantier, l'entité Travail les statuts travail. Un INSERT SQL avec un statut hors-enum est rejeté directement par la base de données grâce à des contraintes CHECK posées sur les deux tables concernées.

Statuts tâche : todo, doing, done, paused, cancelled — vocable distinct, sans contrainte CHECK en base à ce jour.

Les priorités canoniques sont : P0, P1, P2, P3.

1.3 Scopes

Le champ scope discrimine le périmètre de synchronisation (monolithe ou OSS) et conditionne certaines validations.

Scope chantier (colonne scope, nullable) — valeurs autorisées par contrainte en base :

synedre | codemyshop-oss | codemyshop-enterprise | tenant | business | juridique | negociation | conseil

Attention — La documentation interne de la méthode de création ne liste que les cinq premiers scopes. La base en autorise trois de plus (juridique, negociation, conseil), ajoutés pour les intents Atlas. La base fait foi.

Scope tâche — validé à la fois en code et par contrainte en base :

synedre-internal | codemyshop-oss | codemyshop-enterprise | tenant-single | tenant-multi | infra | doctrine

Un scope tâche invalide lève une ValueError dès la création de la tâche.

1.4 Champs notables

Au niveau chantier :

Champ Rôle
mission_letter (texte libre) Lettre de mission structurée (étape 2 de la doctrine)
preprod_test_plan (texte libre) Plan de test preprod — Markdown ou YAML v2 (présence de version: déclenche la QA automatique)
ship_command (chaîne courte) Commande exacte de clôture, ex. ./ship synedre-os
auto_explode (booléen, défaut true) Kill-switch par chantier de la décomposition automatique discovery→implémentation
mode_auto (booléen) Autonomie chantier V1 — l'agent exécute sans valider chaque action
max_cost_eur (numérique) Plafond de coût IA par chantier (NULL = illimité)
client_id Référence vers un client, ou NULL pour un chantier interne

Au niveau travail : 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 / décisions / 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.

Au niveau tâche : 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 (visual_intent et visual_url) utilisés par le pipeline de vérification visuelle post-déploiement — une valeur NULL indique une tâche non-visuelle.

1.5 Recrutement d'agents — persistance

Le recrutement d'agents sur un chantier (étape 3 de la doctrine) est persisté dans une table dédiée, distincte du champ d'assignation d'une tâche individuelle (qui ne porte que l'assignation d'une tâche isolée).

Colonne Rôle
Identifiant assignation (PK) Clé primaire de l'assignation
Identifiant chantier Lien vers le chantier
Codename agent Agent recruté (ex. Mitnick, Turing, Brunel…)
Rôle production / lead_production / validation / lead_validation
Position Ordre d'affichage
Date assignation / désassignation Cycle de vie de l'assignation
Notes Justification du recrutement

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 validation ou lead_validation est présent, la cascade bascule vers un run de QA plutôt qu'un simple changement de statut (voir §5).

1.6 Journal de décomposition automatique

Une entité dédiée persiste un enregistrement d'audit par invocation du module qui transforme les décisions d'un travail de type discovery en travaux d'implémentation (phases A/B/C). Un enregistrement équivaut à une invocation. Il est créé avec le statut pending en début de pipeline, puis mis à jour en fin.

Statut Sens
pending Run en cours
success Travaux d'implémentation créés
validation_failed Plan LLM rejeté par la validation
llm_failed Erreur d'API LLM
killswitched L'un des trois kill-switches a interrompu l'exécution

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 dry_run et force. 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 killswitched sans erreur. L'historique des runs est consultable depuis le dashboard via la commande /chantier explode list.

1.7 Dépendances tâche→tâche — DAG intra-travail

Le moteur de dépendances entre tâches gère un graphe orienté acyclique (DAG) N:M. À ne pas confondre avec les dépendances inter-travaux (colonne de l'entité Travail) : ces deux mécanismes opèrent sur des niveaux distincts.

Sémantique : une tâche bloquée ne peut démarrer avant que la tâche bloquante ne soit au statut done. Contrainte métier forte : les deux tâches doivent appartenir au même travail — une dépendance inter-travaux lève une erreur applicative dédiée (refus en code, pas de trigger SQL).

Opération Méthode conceptuelle
Ajouter une dépendance add_dep(bloquée, bloquante)
Lister les dépendances d'un travail JOIN blocker/blocked avec titres des tâches
Lister les bloqueurs d'une tâche Bloqueurs directs d'une tâche donnée
Vérifier si une tâche est débloquée is_tache_unblocked(id)
Supprimer une dépendance Idempotent — sans effet si inexistante

Anti-cycle : 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.

Un bloqueur au statut cancelled est considéré comme résolu 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.

Création atomique d'un chantier

La méthode de création de chantier exposée par le moteur de gestion de projets constitue l'unique voie canonique 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.

Signature

La méthode accepte les paramètres suivants :

  • codename : identifiant kebab-case du chantier.
  • title : libellé humain du chantier.
  • first_travail (obligatoire) : dictionnaire décrivant le premier travail — codename, titre, et champs optionnels tels que priorité, phase courante, tâche courante, prochaine action, blocages.
  • first_taches (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).
  • first_tache : forme singulière conservée pour compatibilité ascendante — à ne pas combiner avec first_taches.
  • client_id : référence optionnelle au tenant concerné.
  • priority : niveau de priorité parmi P0, P1, P2 (défaut), P3.
  • scope, current_focus, notes : champs contextuels optionnels.

La méthode retourne un dictionnaire de confirmation (voir § Retour ci-dessous).

Validations bloquantes

Toutes les validations sont accumulées avant le premier INSERT ; une seule violation lève une ValidationError et annule l'ensemble :

  1. Format kebab-case du codename — expression régulière ^[a-z0-9][a-z0-9\-]{2,62}[a-z0-9]$ : minuscules, chiffres et tirets, 4 à 64 caractères.
  2. Unicité du codename — une vérification d'existence est effectuée ; si le codename est déjà utilisé, la création est refusée.
  3. Priorité valide — doit appartenir à {P0, P1, P2, P3}.
  4. Travail initial valide — codename et titre doivent être renseignés et non vides.
  5. Au moins une tâchefirst_tache ou first_taches est obligatoire ; fournir les deux simultanément est une erreur.
  6. Agent connu — chaque codename d'agent assigné est vérifié dans le registre des agents actifs.
  7. Au moins deux agents distincts si la portée est de type tenant (doctrine v3) — si le champ scope commence par tenant, le set des assignees doit contenir au moins deux codenames distincts.

Avertissement non-bloquant : 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 § Estimation ci-dessous).

Atomicité et rollback

L'insertion s'effectue dans une transaction unique (BEGIN … COMMIT) 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.

Estimation de tokens et modèle recommandé

Si le modèle recommandé n'est pas fourni explicitement pour une tâche, il est calculé automatiquement selon la règle suivante :

Condition Modèle sélectionné
Priorité P0 ou tâche en échec récurrent (≥ 2 itérations en état fail) Modèle haute capacité (ex. : opus)
Estimation ≥ 8 000 tokens ou estimation absente Modèle haute capacité (ex. : opus)
Estimation ≥ 1 500 tokens Modèle intermédiaire (ex. : sonnet)
Sinon Modèle léger (ex. : haiku)

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.

Compétences et outils attachés

Après l'insertion, le skeleton attache les compétences (skills) et outils (tools) 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.

Retour

La méthode retourne un dictionnaire contenant :

  • Les identifiants internes du chantier, du travail et des tâches créés.
  • Un champ id_tache (compatibilité ascendante, égal au premier de la liste) et id_taches (liste complète, doctrine v3).
  • Un objet codenames regroupant les codenames du chantier, du travail et des tâches.
  • Une liste warnings des avertissements non-bloquants émis pendant la création.

Interface en ligne de commande

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 --dry-run 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 --help.

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.

Procédure de création en 7 étapes

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 0a à 4 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 ni l'étape 2 (rédaction de la lettre de mission : la façade génère uniquement des titres de tâches génériques du type @agent — analyse…) ni l'étape 5 (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.

Étape Action Mécanisme
0a Lire la demande email verbatim Lecture via l'API interne de la boîte de réception
0b Extraire et scanner les pièces jointes avant toute lecture Extraction des attachements, puis analyse antivirale/antimalware. Verdict non-clean → blocage immédiat
1 Audit des agents disponibles Interrogation du registre des agents actifs (codename, surnom, rôle, groupe)
2 Rédiger la lettre de mission Structure : OBJECTIF / CONTEXTE / PÉRIMÈTRE / LIVRABLES / CONTRAINTES / DOCTRINE, à partir du contenu lu aux étapes 0a/0b
3 Recrutement explicite Au moins 2 agents distincts si la portée est de type tenant
4 Création atomique du skeleton Voir § Création atomique
5 Mise à jour post-skeleton 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

Façade de création depuis un email

La façade de création depuis un email suit le flux suivant :

  1. Récupération de l'email — appel à l'API interne de la boîte de réception (jamais de SQL direct).
  2. Extraction et scan des pièces jointes — si un attachement n'est pas clean, la façade affiche un message de blocage et s'arrête avec le code de sortie 2.
  3. Résumé de la demande — extraction du titre, du client, de la priorité et de la portée.
  4. Liste des agents et recrutement — si moins de 2 agents distincts sont sélectionnés, blocage avec le code de sortie 3.
  5. Construction et insertion du skeleton — une tâche par agent avec des titres génériques, travail initial en phase discovery. Correspond à l'étape doctrinale 4. Aucune mise à jour de lettre de mission ni d'étape doctrinale 5 n'est effectuée.

Codes de sortie : 2 = attachement non-clean, 3 = moins de 2 agents, 4 = erreur de validation.

Deux niveaux de contrôle distincts sur le nombre d'agents : la façade exige toujours 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 scope commence par tenant. Le scope par défaut tenant aligne le message de blocage de la façade, mais ne conditionne pas son propre contrôle.

La façade crée un unique travail initial en phase discovery. La transition vers les travaux d'implémentation reste manuelle ou passe par le mécanisme d'explosion automatique de travaux (voir § Auto-explode).

Champs plan de test et commande de livraison (étape 5)

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 version:), 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.

Verrouillage multi-session

Le mécanisme de verrouillage permet d'ouvrir N chantiers différents en parallèle (N terminaux ou workers) tout en refusant deux sessions concurrentes sur le même chantier. Ce n'est pas un bloqueur ergonomique, mais un filet anti-collision.

Modèle de données

Le verrou repose sur une table dédiée avec une clé primaire composite (identifiant du chantier, nature du propriétaire). La nature du propriétaire est contrainte à deux valeurs :

  • user : session interactive (par exemple, une session SSH d'un opérateur humain).
  • worker : sous-processus agent spawné par le moteur de tâches automatisées.

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 > empreinte du chemin de transcription > combinaison PID/utilisateur/hôte.

TTL et acquisition

La durée de vie d'un verrou inactif est de 30 minutes. L'acquisition tente un INSERT … ON CONFLICT … DO UPDATE qui ne réussit que si l'identifiant de session correspond à l'acquéreur actuel ou si le verrou a expiré (dernière activité il y a plus de 30 minutes). Dans le cas contraire, la méthode retourne {acquired: False, owner: {…}}.

Le verrou est acquis automatiquement lors de la récupération du contexte d'un chantier. En cas de refus, une exception ChantierLockedError 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.

Opérations disponibles

Opération Description Option CLI
Inventaire des verrous actifs Liste les verrous vivants avec leur propriétaire et date d'activité --locks
Libérer son verrou Suppression idempotente du verrou correspondant à la session courante --release <codename>
Reprendre le verrou (kick) Force la reprise du slot de même nature, évinçant le propriétaire précédent --force-claim <codename>
Maintenir le verrou vivant Met à jour l'horodatage d'activité pour repousser l'expiration TTL

Trois filets de libération

  1. Hook de fin de session — déclaré dans la configuration des hooks Claude, libère le verrou rapidement en fin de session interactive.
  2. Tâche cron TTL — 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.
  3. Force-claim manuel — utilisé en dernier recours pour reprendre un verrou orphelin.

Cascades de statut

Les méthodes de mise à jour des entités sont surchargées pour propager les statuts terminaux d'un niveau au suivant. La cascade est limitée à un cran par niveau — chaque cran redéclenche sa propre cascade de manière autonome.

Logique de propagation tâche → travail

Lorsque toutes les tâches d'un travail atteignent un statut terminal (terminé ou annulé) 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 :

  • Avec équipe de validation — 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 terminé. Dans le cas contraire, des tâches correctives sont créées et le travail reste actif dans sa phase de développement.
  • Sans équipe de validation — le passage à terminé est effectué directement.

Toute exception levée par le sous-processus de contrôle qualité est fail-safe : elle est journalisée sans bloquer ni modifier l'état du travail.

Logique de propagation travail → chantier

Lorsque tous les travaux d'un chantier atteignent un statut terminal et qu'au moins un est terminé, le chantier est promu à l'état test (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.

⚠️ Le passage tâche → travail n'est pas systématiquement direct. 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 terminé 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 que lorsqu'aucune équipe de validation n'est configurée.

Le passage final test → terminé reste manuel — c'est un acte de validation humaine effectué après la commande de livraison ./ship, jamais déclenché automatiquement par une cascade.

Cascade déclenchée nativement par la base de données (messagerie entrante)

Indépendamment des cascades applicatives décrites ci-dessus, un déclencheur natif de base de données propage automatiquement l'état terminé côté données, de façon transparente pour la couche applicative :

  • Ce déclencheur s'active après toute mise à jour du statut d'un chantier.
  • Lorsque le chantier passe à terminé pour la première fois, il marque automatiquement comme résolu 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.
  • Conséquence pratique : un email entrant peut apparaître comme résolu 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.

Garde-fou : chantiers en phase de découverte et auto-explosion

La cascade travail → chantier comporte un garde-fou spécifique : si tous les travaux terminés sont encore en phase de découverte, le chantier n'est pas promu à l'état test. 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.

Dans ce cas, un mécanisme d'auto-explosion peut se déclencher pour transformer le travail de découverte en travaux d'implémentation concrets. Ce mécanisme est protégé par trois coupe-circuits successifs, évalués dans l'ordre :

  1. Une variable d'environnement permettant de désactiver globalement l'auto-explosion.
  2. Un indicateur par chantier en base de données (auto_explode = FALSE).
  3. Un drapeau global activable via fichier de configuration YAML.

Si l'un de ces trois coupe-circuits est actif, le chantier reste en état planification pour revue manuelle. Chaque invocation du mécanisme d'auto-explosion est auditée dans un journal dédié, avec les statuts suivants :

  • killswitched — au moins un coupe-circuit a bloqué l'opération ;
  • success — les travaux d'implémentation ont été créés avec succès ;
  • validation_failed — le plan proposé par le modèle de langage a été rejeté.

Resolve-paused — le pattern travail-bis

Lorsqu'un travail est mis en état bloqué (paused), un travail-bis est créé pour porter la résolution du blocage. Ce travail-bis référence le travail bloqué. Quand le bis passe à l'état terminé, le mécanisme de résolution nettoie automatiquement le travail parent bloqué, avant que la cascade travail → chantier ne soit réévaluée (car ce nettoyage modifie le décompte des travaux terminaux du chantier).

Effets produits lorsque le travail-bis passe à l'état terminé

  1. Le travail bloqué parent passe à l'état terminé via une mise à jour directe (contournant la récursion de cascade).
  2. Toutes les tâches encore en attente du travail bloqué parent sont annulées par mise à jour groupée.
  3. Le journal de décisions est mis à jour symétriquement sur le bis et sur le parent (traçabilité complète).

Garde-fous du mécanisme de résolution

Condition Comportement
Aucun travail parent référencé Opération silencieuse sans effet (chemin hérité)
Travail parent et bis sur des chantiers différents Erreur levée — résolution inter-chantier interdite
Travail parent déjà terminé Opération idempotente sans effet
Cycle de référence mutuel (A → B et B → A) Erreur levée — cycle interdit
Travail parent introuvable (référence orpheline) Avertissement journalisé, opération ignorée
Statut du parent hors de {bloqué, dev, planification} Avertissement journalisé, opération ignorée

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 à NULL en cas de suppression du parent.

Pièges & invariants

  • Colonnes multi-lignes et lecteurs SQL — 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.
  • Synchronisation schéma DB / déclaration d'entité — 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.
  • Statuts distincts par niveau (depuis le découpage en deux ensembles) — les statuts valides pour un travail ne sont pas les mêmes que ceux valides pour un chantier. Un statut review 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.
  • Contraintes de statut en base de données — des contraintes CHECK 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.
  • Périmètre du chantier : la base de données fait autorité — en cas de divergence entre la contrainte DB et la docstring applicative, toujours se référer à la contrainte DB.
  • Champs visuels sur les tâches — deux champs optionnels permettent d'associer une intention visuelle et une URL de référence à une tâche. NULL 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).
  • Credentials de base de données — 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.
  • Deux mécanismes de dépendances coexistent — les dépendances travail → travail sont gérées par une colonne de référence directe sur l'entité travail ; les dépendances tâche → tâche 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.
  • Le journal d'auto-explosion est un log passif, pas une file de traitement — 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.