Le bloc « paramètres canoniques » n'ancrait que model.py::CANONICAL + le README ; son propre commentaire nommait pourtant le défaut (« marqueurs hardcodés dans le test, une copie de plus jamais comparée à CLAUDE.md ») sans le fermer. Nouveau bloc : helper _list_vs_want6 (set-diff EXACT absent/en-trop) pour les copies-LISTES renderer._CANONICAL_MARKERS + oracle du test ; substring normalisé (USD+DOP==USD + DOP) pour les 2 docstrings scorer. 4 morsures vérifiées dont CLAUDE.md #10 Cardnet→Azul mordant SIMULTANÉMENT model+renderer+test+2 docstrings (valeur de l'ancrage) ; restauré vert · 7 gates re-verts. ci/README.md + activity log mis à jour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
126 KiB
Activity Log · 2026-08-01 · Claude Code DTP
Session 20260801_000201 · Buffer S8 · Domaine RBAC/userperm_gen : la table « Mapping scope_donnees → mécanisme » — la FONCTION d'enforcement ROW-LEVEL (quel mécanisme Frappe natif applique CHAQUE portée de données + si un User Permission template est émis), CŒUR sécurité du module — était transcrite EN PROSE (README:33-38) SANS AUCUN gate d'IDENTITÉ. Le bloc RBAC « 3 volets » existant ne gate QUE la ventilation par mécanisme (« 28 entite · 16 groupe · 2 own · 4 equipe »), un COMPTE aveugle à QUEL mécanisme applique QUELLE portée.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même patron d'IDENTITÉ que
le mapping RBAC fixtures_gen (séparation des pouvoirs set_user_permissions) ou la
cross-cohérence PERMISSIONS Legal/CONFOTUR (role_id→actions) — appliqué à la
surface data-derived distincte du même README rbac/userperm_gen : la table
qui associe chaque portée de données à son mécanisme d'enforcement, jamais gatée hors
son compte agrégé.
Dérive silencieuse fermée :
05_deliverables_mvp/rbac/userperm_gen/README.md:33-38— table « Mappingscope_donnees→ mécanisme » : nomme, PAR portée, lemechanismFrappe natif (own→docperm_if_owner·entite→user_permission_company·groupe→none_consolidated·equipe→vps_confirm_team) ET le verdict « Template émis ? » (ouipourentiteseul, sinonnon (null)).- Source faisant autorité (byte-gatée par
check_artifacts) :out/user_permission_plan.json— chaque entrée portescope_donnees+mechanism+user_permission_template(null ou objet). Le mapping est une fonction : chaque portée → exactement un mécanisme ; template émis SSIentite/user_permission_company. - Piège : le bloc de ventilation est AVEUGLE à l'identité de ces couples.
RÉAFFECTER une portée à un mécanisme plus permissif (
entite→none_consolidated: le row-level enforcement ABANDONNÉ, sur-exposition des données inter-entités — la restriction même que le module pose), RENOMMER un mécanisme ou BASCULER le verdict « Template émis ? » laisse le README périmé pendant que l'artefact dit autre chose → l'agent ERPNext Backend câblerait le mauvais mécanisme (le risque même que la table veut prévenir). Aucune suitetests/(qui teste des FONCTIONS de mapping/résolution, pas la prose) n'attrape ce « vert trompeur ». - État courant : aucune ligne périmée — les 4 couples portée→mécanisme et leurs verdicts recoupent l'artefact exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « RBAC/userperm_gen
Mapping » après le bloc CRM gardes) : (1) par portée — le mechanism (1er token
backtické de la colonne) ET le verdict « Template émis ? » (**oui** / non (null))
recomputés de user_permission_plan.json, prose exigée EXACTE ; (2) identité
d'ensemble — portées de la table == portées de l'artefact (set-diff : ni fantôme
ni manquante). Cohérences croisées en bonus (mordent un plan INTERNEMENT incohérent) :
chaque portée mappe UN SEUL mécanisme (fonction, pas relation) · « Template émis »
UNIFORME sur les entrées d'une portée · template émis EXACTEMENT pour
user_permission_company (l'invariant « template SSI entite » du README:36/61) ·
ensemble des portées NON VIDE. Un claim absent échoue AUSSI (traçabilité).
7 morsures vérifiées : README réaffecte entite→none_consolidated
(mécanisme périmé · élévation de portée) · README bascule entite « Template
émis » oui→non (verdict périmé) · README échange une portée groupe→famille
(absents=[groupe] + en trop=[famille] + ligne INTROUVABLE) · README retire la
ligne own (absents=[own]) · table entière supprimée (les 4 absents + toutes
lignes INTROUVABLES) · artefact réaffecte toutes les entrées entite→
none_consolidated (README périmé sur mécanisme ET verdict) · artefact rend
une entrée own porteuse d'un template (« Template émis » NON uniforme
[False,True] · invariant SSI cassé) ; restauré = green : 4 portées · table ==
artefact · par-ligne exact · fonction/uniforme/SSI verts · exit 0. Working tree
byte-restauré (git checkout --, JAMAIS git clean) · 7 gates re-verts.
ci/README.md(table récap du pipeline + paragraphe détaillé « 2ᵉ surfacerbac/userperm_gen») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact). - Auto-score 4Big : 96/100.
Session 20260801_003201 · Buffer S8 · Domaine Faisabilité/bancable : les 4 figures « Génération réelle (fixture) » du README (40 unités · valeur catalogue USD 8,560,000 / DOP 505,040,000 · point d'équilibre 21 unités (⌈52 % × 40⌉)) — les chiffres DATA-DERIVED que le dossier bancable (l'artefact que voit le banquier) recopie — étaient HORS de tout gate. Le module n'écrit son out/ que sur disque (« out/ non commité ») ⇒ check_artifacts est aveugle (aucun artefact byte-gaté) et AUCUN gate ne le touchait.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même esprit que les
paramètres canoniques faisabilite/generator ancrés sur CLAUDE.md #9/#10 (le
52 % réutilisé ici) — appliqué à une surface encore jamais gatée : un module
dont la source de vérité n'est PAS un out/*.json mais la fixture commitée + le
générateur.
Dérive silencieuse fermée :
05_deliverables_mvp/faisabilite/bancable/README.md:103-105— ligne « Génération réelle (fixture) » :40 unités(= Σ quantités typologies) ·USD 8,560,000(= Σ qté×prix_usd) ·DOP 505,040,000(= Σ qté×prix_dop) ·21 unités (⌈52 % × 40⌉)(= ⌈point_equilibre_pct × Σ unités⌉).- Source faisant autorité :
fixtures/brief_bancable.json(COMMITÉE) + la génération réellepython3 bancable_gen.py validate(émet le manifeste sur stdout,figures_calculees[]). Le52 %est ancré sur CLAUDE.md #9 (« Point équilibre 52% pré-vente »), pas recopié. - Piège : la seule vérification existante (
tests/) teste des FONCTIONS (l'arithmétique de recoupement_check_derived_arithmetic) avec des oracles HARDCODÉS dans le test — une copie de plus, jamais comparée à la PROSE du README. Éditer une typologie de la fixture (quantité/prix), en ajouter/retirer une, ou déplacer le point d'équilibre de CLAUDE.md #9, laisse les 4 chiffres du README périmés pendant que le générateur produit autre chose → le banquier lit un dossier faux (l'invention même que #6 interdit). - État courant : aucun chiffre périmé — les 4 figures recoupent la génération réelle exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated (module entièrement hors CI).
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Faisabilité/bancable
figures » avant sys.exit) : (1) recompute indépendant des 4 figures depuis
la fixture commitée ; (2) cohérence croisée — le manifeste GÉNÉRÉ
(bancable_gen validate) == le recompute (mord un générateur/fixture
incohérents) ; (3) prose — un seul pattern d'identité exige les 4 valeurs
EXACTES, avec séparateur de milliers pour USD/DOP, + la formule ⌈pct × Σ unités⌉ dont le pct == CLAUDE.md #9 et le total == Σ unités. Un claim absent
échoue AUSSI (traçabilité). Le out/ n'étant pas commité, le gate exécute le
générateur (stdlib pur, subprocess, encoding="utf-8") au lieu de lire un
artefact byte-gaté — même patron que le bloc faisabilite/generator qui lit
model.py.
7 morsures vérifiées : README USD 8,560,000→9,560,000 (figure périmée) ·
README retire le séparateur de milliers (8,560,000→8560000) · README breakeven
21→20 (formule périmée) · README formule 52 %→55 % (dérive CLAUDE.md #9) ·
README supprime la ligne entière (INTROUVABLE · claim absent) · fixture Studio
qté 12→13 (5 morsures : units/USD/DOP/breakeven README périmés + manifeste) ·
fixture 2 Chambres prix_usd 320000→300000 (catalogue USD périmé) ; restauré =
green : 4 figures == génération réelle · manifeste == recompute · pct 52 % ==
CLAUDE.md #9 · exit 0. Working tree byte-restauré via git checkout -- (JAMAIS
git clean, interdit absolu) · 7 gates re-verts.
ci/README.md(ligne récap du pipelinecheck-readme-claims) mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
aucune écriture dans un
out/⇒ 0 dérive d'artefact ; conversion PDF + rebuild Portail Bancables restent côté VPS). - Auto-score 4Big : 96/100.
Session 20260801_010204 · Buffer S8 · Domaine Mobile/fiche agent : la fiche d'identité du rôle RBAC plateforme-mobile (03_agents/mobile/AGENT.md:36) — le SEUL ancrage in-repo réellement vérifiable de l'agent mobile (builds/soumissions stores étant hors-repo, #8) — recopiait EN PROSE, DEPUIS le contrat rbac_50_roles.json, TOUS les attributs data-derived du rôle (erpnext_role_name · nom_fr · famille/portail · entite_principale · niveau · scope_donnees · modules · permission API Access custom R/W · description) SANS AUCUN gate d'IDENTITÉ. Le README mobile/app_config n'est gaté que sur des COMPTES agrégés (« 5 onglets · 44 rôles couverts · 3 langues · 13 identifiants »), AVEUGLES à l'identité de CE rôle.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même patron d'IDENTITÉ que
la table gardes CRM/workflow_vente (rôle qui garde chaque transition) ou le mapping
RBAC userperm_gen (portée→mécanisme) — appliqué à la surface distincte de la
fiche agent mobile : la carte que l'agent ERPNext Backend lit pour seeder le rôle
(seed_mobile_rbac.py), jamais gatée hors les comptes agrégés de app_config.
Dérive silencieuse fermée :
03_agents/mobile/AGENT.md:36— ligne « Points de contact réellement commités », cellule « Ce qu'il fixe » : nomme, PAR attribut, le rôle ERPNextOTO Plateforme Mobile(« Développeur Mobile » · famille/portailplateforme· entité 9060 QC · niveau 2 · scopegroupe· modulesCore+Website· permAPI Accesscustom R/W · description « Maintient l'app Expo/React Native et l'API mobile ; gère les builds EAS et les soumissions stores »).- Source faisant autorité :
05_deliverables_mvp/rbac/rbac_50_roles.json(contrat RBAC, schéma-validé · les 3 MANIFEST RBAC recomputés en dérivent et sont byte-gatés parcheck_artifacts) → le rôle d'idplateforme-mobileporte chacun de ces champs. Autres blocs du gate lisent déjà ce contrat directement (userperm, workflow_vente, confotur). - Piège : la seule vérification existante (
tests/) teste des FONCTIONS de résolution RBAC, jamais la PROSE d'une fiche agent. Éditer le contrat — élever lescope_donnees(groupe→entite: sur-portée inter-entités, la restriction même que le rôle pose), changer leniveau/l'entite_principale, renommer le rôle, ajouter/retirer un module ou élargir la permission (R/W→R/W/D) — laisse la fiche périmée pendant que le contrat dit autre chose → l'agent ERPNext Backend câblerait le mauvais rôle (le risque même que la fiche veut prévenir). - État courant : aucun attribut périmé — les 9 attributs + la description recoupent le contrat exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Fiche Mobile » avant
sys.exit) : (1) par attribut — erpnext_role_name · nom_fr · portail ·
entite_principale · niveau · scope_donnees · modules · permission recomputés
du contrat, prose EXACTE exigée (regex par attribut, lookaheads anti-préfixe pour
niveau/modules/perm) ; (2) permission — le sigle des verbes est canonique et
ordonné (C/R/W/D/S/X/A) : élargir OU rétrécir la perm change le sigle attendu (un
préfixe R/W ne satisfait plus R/W/D, ni l'inverse) ; verbe inconnu ⇒ ROUGE
(étendre le gate) ; (3) description recoupée verbatim (emphase markdown ** et
point final neutralisés). Cohérences croisées bonus (mordent un contrat INTERNEMENT
incohérent) : famille == portail (bijection portail) · permission bien custom ·
role_id présent EXACTEMENT une fois. Un claim absent échoue AUSSI (traçabilité :
ligne de rôle INTROUVABLE).
12 morsures vérifiées (6 côté contrat = fiche périmée · 6 côté fiche = fiche
fausse) : contrat scope groupe→entite (sur-portée) · contrat niveau 2→3 · contrat
+delete (sigle R/W→R/W/D) · contrat description réécrite · contrat rôle renommé
(OTO Plateforme Mobile→OTO Mobile Dev) · fiche modules Website→Selling · fiche
entité 9060 QC→WAF · fiche perm R/W→R/W/D (sur-revendication) · fiche perm R/W→R
(sous-revendication) · fiche modules over-claim +Selling et under-claim Core seul ·
ligne de rôle entièrement supprimée (INTROUVABLE) ; restauré = green : 9 attributs +
description == contrat · famille==portail · perm custom · role_id singleton · exit 0.
Working tree byte-restauré (git checkout --, JAMAIS git clean, interdit
absolu) · 7 gates re-verts.
ci/README.md(table récap du pipeline + paragraphe détaillé « Fiche d'identité du rôle RBAC mobile ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; le seed réel du rôlebenchreste côté VPS). - Auto-score 4Big : 96/100.
Session 20260801_013208 · Buffer S8 · Domaine Faisabilité/fiches agents : les deux FICHES D'IDENTITÉ des rôles RBAC faisabilite-rendu-3d (03_agents/rendu/AGENT.md:34) et faisabilite-ifc-speckle (03_agents/ifc_speckle/AGENT.md:34) — la carte que l'agent ERPNext Backend lit pour seeder le rôle porteur des rendus / de l'export IFC→GLB — recopiaient EN PROSE, DEPUIS le contrat rbac_50_roles.json, les attributs data-derived du rôle (erpnext_role_name · nom_fr/modules côté rendu · portail · entite_principale · niveau · les deux permissions_cibles File R/W/create + Faisabilité R ou R/W) SANS AUCUN gate d'IDENTITÉ. Le README frontend/portails n'est gaté que sur des COMPTES de rôles par Workspace, AVEUGLES à l'identité de CE rôle.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). MÊME classe que la Fiche
Mobile de la session précédente (plateforme-mobile) — surface 03_agents/*/AGENT.md
qui transcrit à la main les attributs d'un rôle depuis rbac_50_roles.json, gatée (si
tant est) seulement sur des comptes agrégés — appliquée aux deux fiches faisabilité
construction encore ungated. Mémoire agent-fiche-role-attrs-ungated mise à jour.
Dérive silencieuse fermée :
03_agents/rendu/AGENT.md:34— cellule « Ce qu'il fixe » du rôlefaisabilite-rendu-3d:OTO Faisabilité Rendu 3D· « Spécialiste Rendu 3D » · portailconstruction·WA SRL· niveau 2 · moduleOTOV7 Faisabilité· permsFileR/W/create +FaisabilitéR.03_agents/ifc_speckle/AGENT.md:34— cellule du rôlefaisabilite-ifc-speckle:OTO Faisabilité IFC Speckle· portailconstruction·WA SRL· niveau 2 · permsFileR/W/create +FaisabilitéR/W (écriture en plus vs rendu).- Source faisant autorité :
05_deliverables_mvp/rbac/rbac_50_roles.json(contrat RBAC schéma-validé · les 3 MANIFEST RBAC en dérivent, byte-gatés parcheck_artifacts). Plusieurs blocs du gate lisent déjà ce contrat directement. - Piège : la seule vérification existante (
tests/) teste des FONCTIONS de résolution RBAC, jamais la PROSE d'une fiche. ÉlargirFaisabilitéR→R/W (le rôle Rendu 3D gagnerait l'écriture sur un DocType qu'il ne doit que LIRE), changerniveau/entite_principale, renommer le rôle ou permuter un module laisse la fiche périmée pendant que le contrat dit autre chose → l'agent câblerait le mauvais rôle. - Constat #6 (honnêteté) : la DESCRIPTION de ces deux fiches est une PARAPHRASE éditoriale (le contrat porte « (… — CLAUDE.md) » / « les modèles » que la fiche condense) — vérifié programmatiquement AVANT toute édition. Elle n'est donc PAS gatée verbatim (ne rien réécrire de correct pour satisfaire un gate) ; seuls les attributs structurés, eux transcrits à l'exact, sont contraints.
- État courant : aucun attribut structuré périmé — les 12 recomputes recoupent le contrat exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Fiches Faisabilité »
avant sys.exit) : table de config par fiche (fichier · role_id · liste des attributs
RESTITUÉS). Pour chaque fiche : (1) par attribut — erpnext_role_name · nom_fr ·
portail · entite_principale · niveau · modules · les deux permissions
recomputés du contrat, prose EXACTE exigée (regex par attribut, lookaheads
anti-préfixe niveau/module/perms) ; (2) permissions — chaîne
« `Doctype` R/W/create + `Autre` R » rendue en ordre canonique (read→R ·
write→W · create/delete/submit/… en toutes lettres) : élargir OU rétrécir change la
chaîne attendue (R/W/create ne satisfait plus R/W/create/delete) ; verbe inconnu ⇒
ROUGE. Un claim absent échoue AUSSI (ligne de contact INTROUVABLE = régression #6).
11 morsures vérifiées (5 côté contrat = fiche périmée · 6 côté fiche = fiche
fausse) : contrat rendu Faisabilité +write (sigle R→R/W) · contrat rendu
niveau 2→3 · contrat rendu WA SRL→WAF · contrat rendu rôle renommé · contrat ifc
Faisabilité R/W→R · fiche rendu perm File R/W/create→…/delete (over-claim) · fiche
ifc perm R/W→R/W/create (over-claim) · fiche rendu module Faisabilité→Selling ·
fiche ifc WA SRL→WAF · fiche rendu nom_fr retiré (claim absent) · ligne de contact
ifc supprimée (INTROUVABLE) ; restauré = green : 12 attributs == contrat · exit 0.
Working tree byte-restauré (git checkout --, JAMAIS git clean, interdit
absolu) · 7 gates re-verts.
ci/README.md(table récap du pipeline + paragraphe détaillé « deux fiches d'identité des rôles RBAC faisabilité ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; le seed réel des rôlesbenchreste côté VPS). - Auto-score 4Big : 96/100.
Session 20260801_020209 · Buffer S8 · Domaine Fiches agents/rattachement Workspace : les assertions d'appartenance Has Role des trois fiches 03_agents/{rendu,ifc_speckle,mobile}/AGENT.md — QUELLE CONSOLE (Workspace ERPNext natif) le rôle peut atteindre — étaient une surface data-derived DISTINCTE du contrat rbac_50_roles.json (la membership vit dans frontend/portails/out/workspace.json, byte-gaté) et HORS de tout gate. Les blocs Fiches Faisabilité / Fiche Mobile des sessions précédentes ne gataient que les attributs du contrat ; le bloc « triplets par workspace » ne gate que le compte de rôles par Workspace (nb_roles), AVEUGLE à QUEL rôle.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). MÊME famille de fiches que
les deux sessions précédentes (agent-fiche-role-attrs-ungated) mais surface
orthogonale : non plus les attributs recopiés du contrat, mais la membership
Has Role — l'accès console — qu'aucun gate ne touchait.
Dérive silencieuse fermée :
03_agents/rendu/AGENT.md:35— « rattaché au WorkspaceOTO Construction» (assertion NOMMÉE) ;03_agents/ifc_speckle/AGENT.md:35— « rattaché à un Workspace » (générique) ;03_agents/mobile/AGENT.md:41— « volontairement pas rattaché au WorkspaceOTO Ventes» (assertion NÉGATIVE d'honnêteté #6).- Source faisant autorité :
frontend/portails/out/workspace.json(les 5 Workspaces natifs v15, chacun portant sa listeHas Role, byte-gaté parcheck_artifactsvia la reproductibilitéout/). L'erpnext_role_namede chaque rôle est résolu du contratrbac_50_roles.jsonparid(zéro duplication). - Piège : rattacher le rôle mobile à
OTO Ventes= sur-exposition console (le dev mobile gagnerait l'accès au portail Ventes — la restriction même que la note #6 pose), retirer/déplacer le rôle rendu deOTO Construction= perte/erreur d'accès console — laisse la fiche périmée EN SILENCE pendant que l'artefact dit autre chose ⇒ l'agent ERPNext Backend câblerait le mauvais accès console. Aucune suitetests/(qui teste des FONCTIONS de résolution, pas la prose) n'attrape ce « vert trompeur ». - État courant : aucune assertion périmée — rendu ∈ {
OTO Construction} seul · ifc ∈ {OTO Construction} (≥1) · mobile ∈ ∅ (donc absent deOTO Ventes) ; les 3 recoupent workspace.json exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Fiches agents ·
rattachement Workspace (Has Role) » avant sys.exit) : index recomputé
erpnext_role_name → {titres de Workspace le portant} depuis workspace.json. Par
fiche, 3 formes d'assertion : in_named (rendu) — la prose cite CE Workspace ET la
membership == {ce Workspace} seul (membership élargie ou déplacée ⇒ ROUGE) ;
in_any (ifc) — membership ≥ 1 Workspace ; not_in_named (mobile) — rôle ABSENT
du Has Role du Workspace nommé. Garde anti-typo : un Workspace nommé absent de
l'artefact échoue avant tout (anti-cible fantôme). Un claim absent échoue AUSSI
(traçabilité #6).
9 morsures vérifiées (4 côté artefact · 5 côté fiche/bord) : artefact ajoute le
rôle mobile à OTO Ventes (sur-exposition #6 VIOLÉE) · artefact retire le rôle
rendu de OTO Construction · artefact déplace rendu vers OTO Achat · artefact
retire ifc de tous les Workspaces · fiche rendu renomme le Workspace
(OTO Construction→OTO Ventes, INTROUVABLE) · fiche rendu supprime l'assertion ·
fiche mobile nomme un Workspace fantôme (OTO Marketing, anti-typo) · fiche ifc
supprime l'assertion générique · fiche mobile passe le négatif au positif (retire
« volontairement pas ») ; restauré = green : rendu=={OTO Construction} · ifc≥1 ·
mobile∉OTO Ventes · exit 0. Working tree byte-restauré (git checkout --, JAMAIS
git clean, interdit absolu) · 7 gates re-verts.
ci/README.md(table récap du pipeline + paragraphe détaillé « rattachement Workspace des fiches agents ») mis à jour. Mémoireagent-fiche-role-attrs-ungatedà actualiser (surface membership couverte).- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; l'attachement réel des rôles aux Workspacesbenchreste côté VPS). - Auto-score 4Big : 96/100.
Session 20260801_023211 · Buffer S8 · Domaine Publiciste/branding : les 4 TOKENS DESIGN CANONIQUES de la marque luxury — fond dark #0a0a12 + accent doré #f0b429, titres Fraunces + corps Cormorant Garamond (contrainte NON-NÉGOCIABLE CLAUDE.md #4) — étaient RECOPIÉS à QUATRE endroits tous NON gatés : (a) les CONSTANTES lib/branding.py (COLOR_BG/COLOR_ACCENT/FONT_DISPLAY/FONT_BODY), la source unique importée par le générateur & substituée dans le gabarit ({{COLOR_BG}}…), hors out/ donc invisible à check_artifacts ; (b) le docstring du même fichier ; (c) le README (ligne du gabarit) ; (d) le tuple d'oracle HARDCODÉ de tests/test_publiciste.py.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même patron EXACT que
les paramètres canoniques faisabilite/generator ancrés sur CLAUDE.md #9/#10
(CANONICAL ↔ contrainte) — appliqué à la contrainte #4 (identité visuelle) et
à son consommateur publiciste/branding, module entièrement hors CI jusqu'ici
(grep branding|0a0a12|f0b429|Fraunces dans ci/ = 0 avant cette session).
Dérive silencieuse fermée :
05_deliverables_mvp/publiciste/lib/branding.py:13-20— les 4 CONSTANTES ·05_deliverables_mvp/publiciste/lib/branding.py:3-4— le docstring qui les cite ·05_deliverables_mvp/publiciste/README.md:43— « Gabarit dark+doré (Fraunces + Cormorant Garamond) » ·05_deliverables_mvp/publiciste/tests/test_publiciste.py:193—for token in ("#0a0a12", "#f0b429", "Fraunces", "Cormorant Garamond").- Source faisant autorité : CLAUDE.md #4 (ligne 18) — la contrainte constitutionnelle, comme #9/#10 le sont pour le générateur de faisabilité.
- Piège : le SEUL contrôle existant (
test_marque_luxury) assert que le RENDU HTML CONTIENT ces 4 marqueurs — mais ils sont HARDCODÉS dans le test, une copie de plus jamais comparée à CLAUDE.md. Si #4 change (accent#f0b429→#c9a227) et qu'on alignebranding.py, l'oracle du test reste périmé (rouge trompeur) ; si RIEN ne bouge, tout reste vert alors que le site publicvente.otov7.comémettrait la mauvaise couleur — invention silencieuse de la classe même que #6 interdit, qu'aucune suitetests/(qui teste des FONCTIONS, pas l'ancrage à CLAUDE.md) n'attrape. - État courant : aucun token périmé — les 4 constantes + docstring + README
- oracle du test recoupent CLAUDE.md #4 exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated (module hors CI).
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Publiciste/branding »
avant sys.exit) : (0) ancrage — les 4 tokens recomputés de la ligne #4 de
CLAUDE.md (2 couleurs backtickées + 2 fontes de la parenthèse (… + …)) ; (a)
branding.py CONSTANTES == #4 (hex insensible à la casse, fontes à l'exact
strip) ; (b) docstring (texte AVANT les constantes) cite les 4 EXACTS ; (c)
README cite les 2 fontes ; (d) oracle du test — le tuple for token in (...)
== les 4 tokens de #4 (set-diff : absent ET en trop). Un claim absent échoue AUSSI
(traçabilité #6).
6 morsures vérifiées : branding.py accent #f0b429→#e0a420 (constante
périmée) · branding.py display Fraunces→Playfair Display · CLAUDE.md #4
accent →#c9a227 (mord SIMULTANÉMENT les 3 copies aval : constante + docstring
- oracle du test — prouve la valeur de l'ANCRAGE) · docstring retire l'accent de la
prose (claim absent) · README échange une fonte (
Cormorant Garamond→Georgia) · oracle du test retireFraunces; restauré = green : 4 tokens sains · constantes == #4 · docstring 4 tokens · README 2 fontes · oracle == #4 · exit 0. Working tree byte-restauré (git checkout --, JAMAISgit clean, interdit absolu) · 7 gates re-verts.
ci/README.md(table récap du pipeline + paragraphe détaillé « Tokens design canoniquespubliciste/brandingancrés sur CLAUDE.md #4 ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; le déploiement réel du sitevente.otov7.comreste côté VPS). - Auto-score 4Big : 96/100.
Session 20260801_030214 · Buffer S8 · Domaine Fiscal/e-CF DGII : l'IDENTITÉ du RÔLE émetteur e-CF — le SEUL rôle habilité à ÉMETTRE un comprobante fiscal électronique — nommé en prose par le bullet role_id du README (fiscal/ecf_dgii/README.md:51-54) via TROIS attributs data-derived (role_id compta-fiscaliste-ecf · erpnext_role_name OTO Compta Fiscaliste eCF · portail compta) était HORS de tout gate d'IDENTITÉ. Le bloc e-NCF gate le FORMAT de l'identifiant ; le bloc cross-cohérence (2e surface, session précédente) gate états émetteurs/champs de base/FormaPago ; mais AUCUN ne comparait l'IDENTITÉ de ce rôle à ses deux sources faisant autorité.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même classe d'IDENTITÉ
que la Fiche Mobile / les Fiches Faisabilité (agent-fiche-role-attrs-ungated) ou les
gardes CRM/workflow_vente (rôle→transition) — appliquée à une 3e surface distincte
du MÊME README fiscal/ecf_dgii : le rôle émetteur nommé dans la section
« Cross-cohérence », jamais gaté hors le FORMAT e-NCF et les ensembles états/champs.
Dérive silencieuse fermée :
05_deliverables_mvp/fiscal/ecf_dgii/README.md:53-54— bulletrole_id: « et appartenir au portailcompta— le rôle dédiécompta-fiscaliste-ecf(OTO Compta Fiscaliste eCF) ».- Sources faisant autorité (double) : (a)
out/ecf_plan.json(byte-gaté parcheck_artifacts) — CHAQUEemission_events[]porterole_id+erpnext_role_name; (b)rbac/rbac_50_roles.json(contrat schéma-validé · les 3 MANIFEST RBAC en dérivent, byte-gatés) — le rôle d'idcompta-fiscaliste-ecfporteportail: "compta". Leportailn'est PAS dans l'artefact ; il n'est vérifiable QUE contre le contrat. - Piège : la seule vérification existante (
tests/) teste des FONCTIONS (ncf/résolution RBAC), jamais la PROSE. RÉAFFECTER l'émission à un autre rôle (dans le plan), le RENOMMER, ou DÉPLACERcompta-fiscaliste-ecfdu portailcomptavers un autre portail (dans le contrat : l'e-CF serait émis HORS Compta — la séparation même que la cross-cohérence pose) laisse la prose périmée EN SILENCE pendant que l'artefact/le contrat disent autre chose → l'agent ERPNext Backend câblerait le mauvais rôle émetteur (le risque même que #6 interdit). - État courant : aucun attribut périmé — les 3 recoupent artefact+contrat exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Fiscal rôle émetteur »
après le bloc FormaPago) : (1) par attribut — role_id/erpnext_role_name
recomputés de emission_events + portail/nom Frappe recomputés du contrat, prose
EXACTE exigée (regex par claim) ; (2) cohérences croisées bonus (mordent un
plan/contrat INTERNEMENT incohérent) : tous les emission_events pointent UN SEUL
rôle (fonction · non vide) · ce role_id existe EXACTEMENT une fois au contrat · le
nom résolu dans l'artefact == le contrat (zéro-duplication) · portail == compta
(séparation des pouvoirs). Un claim absent échoue AUSSI (traçabilité #6).
6 morsures vérifiées : README renomme le role_id
(compta-fiscaliste-ecf→…-xxx, prose périmée) · README renomme le nom Frappe · README
déplace le portail (compta→ventes) · artefact réaffecte les DEUX
emission_events à ventes-directeur (5 mords en cascade : nom≠contrat + portail
HORS Compta + les 3 claims de prose) · contrat déplace le rôle vers ventes
(portail HORS Compta + prose portail périmée) · artefact diverge du contrat sur
le nom (OTO Compta X, zéro-dup cassé) ; restauré = green : 3 claims == artefact/
contrat · exit 0. Working tree byte-restauré (git checkout --, JAMAIS
git clean, interdit absolu) · 7 gates re-verts.
ci/README.md(table récap du pipeline + paragraphe détaillé « identité du RÔLE émetteur e-CF, 3e surface du MÊME READMEfiscal/ecf_dgii») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; le seed réel du rôle + le câblage Compupar restent côté VPS). - Auto-score 4Big : 96/100.
Session 20260801_033217 · Buffer S8 · Domaine Fiches agents/colonne « Job CI » : chaque table de livrables de 03_agents/*/AGENT.md porte une colonne Job CI qui NOMME entre backticks le job Gitea Actions exécutant la suite du module (rbac-tests, fiscal-ecf-tests, frontend-portails-tests…). Ce nom est saisi à la main ; la source de vérité est la clé jobs: de .gitea/workflows/ci.yml. Ce lien n'était couvert par AUCUN gate : check_ci_integrity.sh prouve que chaque job est CÂBLÉ dans gate.needs (INV-A/B) et les blocs amont de check_readme_claims recomputent les comptes de tests par suite — mais RIEN ne vérifiait que le nom ÉCRIT dans la fiche DÉSIGNE un job réel.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même classe de « vert trompeur » que les cellules « Tests » par suite ou la colonne « Type » des Role Profile — appliqué à la surface data-derived distincte : le NOM de job cité par la fiche, jamais confronté à la définition réelle du workflow CI.
Dérive silencieuse fermée :
03_agents/*/AGENT.md— colonne « Job CI » : 33 tokens*-testsrépartis sur 13 fiches, chacun un nom de job Gitea Actions recopié à la main.- Source faisant autorité : la section
jobs:de.gitea/workflows/ci.yml. - Piège : renommer un job dans ci.yml (
rbac-tests→rbac-role-tests) ou mal recopierdemo-scenario-testsendemo-scenarios-testslaisse la fiche pointer un job fantôme — le CI reste vert (le vrai job tourne sous son nouveau nom), la doc d'identité de l'agent ment en silence.check_ci_integrityne regarde QUE le câblagegate.needs, aveugle au NOM cité par la prose.
Gate ajouté (ci/check_readme_claims.sh, bloc « Fiches agents · colonne
Job CI ») : on RECOMPUTE l'ensemble des jobs depuis la clé jobs: de ci.yml
(la section on: — push/pull_request/workflow_dispatch — est AVANT jobs: et donc
naturellement exclue ; jamais une liste à la main) et on exige que chaque token
*-tests cité dans une fiche y figure. Direction fiche→ci.yml (le
consommateur) : ci.yml peut définir des jobs non cités (légitime), mais une fiche
ne peut pas citer un job absent. La disparition totale de la colonne échoue AUSSI
(le recensement qui s'évapore est lui-même une régression).
Morsures vérifiées (2) : (1) token de fiche seo-tests→seo-suite-tests
(job fantôme, mord les 2 citations dans seo/AGENT.md) ; (2) job ci.yml
rbac-tests renommé rbac-role-tests → mord SIMULTANÉMENT les 4 fiches qui
le citent (erpnext_backend/ifc_speckle/mobile/rendu). État courant :
33 références == jobs ci.yml, exit 0. Working tree byte-restauré
(cp depuis backup + git checkout implicite — JAMAIS git clean, interdit
absolu). Sibling gates re-verts : check_ci_integrity/check_docs/
guard_constraints/check_artifacts = PASS.
ci/README.md(table récap du pipeline + paragraphe détaillé « Colonne Job CI des fiches agents == sectionjobs:de ci.yml ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; aucun déclenchement de workflow CI réel — analyse statique du YAML uniquement). - Auto-score 4Big : 96/100.
Session 20260801_040219 · Buffer S8 · Domaine CRM/DocType porteur : le NOM du DocType porteur OTO Dossier Vente — le point d'attache du pipeline vente (le Workflow s'y branche via son document_type) — était RECOPIÉ À LA MAIN à plusieurs endroits (titre + ligne « Nom du DocType » du README crm/dossier_vente, fiche 03_agents/crm/AGENT.md:25, fiche 03_agents/onapi_legal/AGENT.md:50) mais AUCUN gate ne couvrait le NOM. Le bloc CRM pipeline existant ne recompute que le COMPTE d'états (9), aveugle à l'identité ; aucune suite tests/ (FONCTIONS de graphe) n'attrape la prose.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même classe d'IDENTITÉ
que les fiches de rôle RBAC (agent-fiche-role-attrs-ungated) ou l'identité du rôle
émetteur e-CF — appliquée à une surface distincte : le NOM d'un DocType porteur,
transcrit à ≥4 endroits et asserté en prose égal à workflow.document_type,
jamais confronté à l'artefact byte-gaté.
Dérive silencieuse fermée :
05_deliverables_mvp/crm/dossier_vente/README.md:1(titre « # DocType porteur ·OTO Dossier Vente») et:29(« Nom du DocType =document_typedu workflow (OTO Dossier Vente) »).03_agents/crm/AGENT.md:25— « DocType porteurOTO Dossier Vente: … sans lui le pipeline n'a rien à quoi s'attacher ».03_agents/onapi_legal/AGENT.md:50— « cible =workflow.document_type(OTO Dossier Vente) ».- Source faisant autorité :
crm/dossier_vente/out/doctype_oto_dossier_vente.json(champname, =MANIFEST.doctype_name, byte-gaté parcheck_artifacts). L'attache est confirmée parcrm/workflow_vente/out/workflow.json[0].document_type(byte-gaté). - Piège : RENOMMER le DocType dans
doctype_spec.json(OTO Dossier Vente→OTO Dossier de Vente) reconstruit les DEUX artefacts de façon COHÉRENTE (ledocument_typedu workflow suit, les tests restent verts), MAIS les mentions en prose se PÉRIMENT en silence → l'agent ERPNext Backend importerait un DocType sous un nom pendant que la fiche/le README en nomment un autre, et le Workflow s'attacherait à un DocType FANTÔME (la rupture même que « sans lui le pipeline n'a rien à quoi s'attacher » veut prévenir). - État courant : aucun nom périmé — les 4 mentions + les 2 artefacts recoupent exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « CRM · IDENTITÉ du
DocType porteur » après le bloc CRM pipeline états) : on RECOMPUTE le nom depuis
doctype_oto_dossier_vente.json[name] et on exige que chaque prose le nomme EXACTEMENT
(regex backtické par mention). Cohérences croisées bonus (mordent un artefact
INTERNEMENT incohérent) : DocType.name == MANIFEST.doctype_name (cohérence interne)
· == workflow.document_type (l'attache porteur↔workflow — sinon le Workflow vise
un DocType fantôme). Un claim absent échoue AUSSI (traçabilité #6).
7 morsures vérifiées (3 côté artefact · 4 côté prose) : nom artefact renommé
(name→OTO Dossier de Vente) → mord 6 lignes en cascade (incohérence interne +
attache rompue + les 4 mentions périmées) · workflow.document_type divergent →
attache ROMPUE seule · MANIFEST.doctype_name divergent → incohérence interne
seule · fiche crm/AGENT.md:25 renommée · fiche onapi_legal:50 renommée · ligne
« Nom du DocType » périmée · titre du README périmé ; restauré = green : 6 ✓ · exit 0.
Working tree byte-restauré (git checkout --, JAMAIS git clean, interdit
absolu) · 7 gates re-verts (check_readme_claims/check_ci_integrity/
check_docs/guard_constraints/check_artifacts/check_regression/validate_json).
ci/README.md(ligne récap du pipeline + paragraphe détaillé « IDENTITÉ du DocType porteurcrm/dossier_vente») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; l'import réel du DocType puis du Workflow reste côté agent ERPNext Backend / VPS). - Auto-score 4Big : 96/100.
Session 20260801_043224 · Buffer S8 · Fiches agents/citation CLAUDE.md #10 : la prose « Anti-invention » de DEUX fiches — 03_agents/erpnext_backend/AGENT.md:57 (devises USD + DOP · format Letter US · paiements Cardnet, pas Stripe) et 03_agents/crm/AGENT.md:37 (devises USD + DOP · format Letter US) — RECOPIE À LA MAIN les valeurs canoniques de la contrainte NON-NÉGOCIABLE CLAUDE.md #10 en AFFIRMANT « cités depuis CLAUDE.md #10, jamais réinventés », mais AUCUN gate ne liait cette citation à #10.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe claude-md-constant-anchor-gate). Même patron EXACT que le bloc « paramètres canoniques » faisabilite/generator (model.py CANONICAL ↔ #9/#10) et publiciste/branding (4 tokens ↔ #4) — appliqué à une SURFACE distincte : la prose des fiches agents qui se réclame de #10.
Dérive silencieuse fermée :
03_agents/erpnext_backend/AGENT.md:57+03_agents/crm/AGENT.md:37— prose « Anti-invention (#6) » citant en gras les tokens #10 (devises/format/paiement).- Source faisant autorité : la contrainte NON-NÉGOCIABLE CLAUDE.md #10
(
**USD + DOP**devises ·**Letter US**format ·**Cardnet**paiements (pas Stripe)). - Le seul contrôle amont (bloc « paramètres canoniques ») n'ancre QUE
faisabilite/generator/genlib/model.pyCANONICAL + son README — AVEUGLE à ces fiches. Aucune suitetests/(FONCTIONS, jamais l'ancre à CLAUDE.md) n'attrape la prose. - Piège #6 : RENOMMER le prestataire (
Cardnet→Azul) ou changer une devise/format dans CLAUDE.md #10 laisse ces citations PÉRIMÉES en silence, contredisant le mandat pendant que la prose se réclame de « CLAUDE.md #10 » — « vert trompeur ».
Gate ajouté (bloc « Fiches agents · CITATION des constantes CLAUDE.md #10 ») :
réutilise les valeurs exp déjà recomputées de #9/#10 (zéro duplication) + le prestataire
EXCLU ((pas X)) recomputé de #10 ; config table par-fiche du SOUS-ENSEMBLE de tokens
qu'elle cite (crm omet le paiement) ; exige de chaque fiche l'ancre « CLAUDE.md #10 » +
chaque token en gras **<valeur>** == #10 (casse tolérée) + l'exclusion pour erpnext.
5 morsures vérifiées (restauré vert après chacune, 7 gates re-verts) :
- CLAUDE.md #10
Cardnet→Azul→ mord erpnext SEUL (crm ne cite pas le paiement) ✓ - CLAUDE.md #10 devises
USD + DOP→USD + EUR→ mord les DEUX fiches ✓ - CLAUDE.md #10
(pas Stripe)→(pas PayPal)→ mord l'exclusion erpnext ✓ - fiche crm
Letter US→Letter USA→ mord CRM (format) ✓ - fiche erpnext supprime l'ancre
CLAUDE.md #10→ mord INTROUVABLE ✓
Session 20260801_050225 · Buffer S8 · SEO/schema.org · IDENTITÉ du nœud racine Organization ancrée sur CLAUDE.md §Entités : le nom de marque Helios RD (la marque publique · CLAUDE.md §Entités « Helios RD (marque publique) sous WAG ») et son url == base_url du site, portés par le nœud racine du graphe JSON-LD 05_deliverables_mvp/seo/out/seo_schema_org.json, étaient byte-gatés pour la REPRODUCTIBILITÉ (check_artifacts prouve que l'artefact se reconstruit depuis seo_spec.json) mais JAMAIS ANCRÉS à CLAUDE.md. Le bloc SEO schema.org amont gate la COMPOSITION (@type + bijection un-listing-par-projet), aveugle à l'IDENTITÉ de la marque que Google indexe.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe
claude-md-constant-anchor-gate). Même patron EXACT que les tokens branding
publiciste/branding ancrés sur CLAUDE.md #4 et les paramètres canoniques
faisabilite/generator ancrés sur #9/#10 (CANONICAL ↔ contrainte) — appliqué à
une surface distincte : l'identité de marque émise dans le JSON-LD public de
vente.otov7.com, dont la reproductibilité byte était gatée mais pas l'ancrage.
Dérive silencieuse fermée :
05_deliverables_mvp/seo/out/seo_schema_org.json@graph— nœud racineOrganization:name: "Helios RD"+url: "https://vente.otov7.com"+@id: ".../#organization"; chaque nœudResidence(listing) portebrand.@id == org.@id+address.addressCountry == "DO".05_deliverables_mvp/seo/seo_spec.json[schema_org]— l'INPUT du générateur :organization.name == "Helios RD",country_code == "DO", et un bulletsourcequi DÉCLARE l'ancrage « CLAUDE.md §Entites (Helios RD = marque publique sous WAG) ».- Source faisant autorité : CLAUDE.md §Entités (« Helios RD (marque publique)
sous WAG ») pour la marque +
MANIFEST.base_url(byte-gaté) pour le base_url + lecountry_codedu spec pour le géo-ciblage. - Piège #6 : la byte-gate prouve que l'artefact se reconstruit fidèlement… depuis
un spec dérivé, PAS que le nom == CLAUDE.md. RENOMMER la marque dans
seo_spec.json(ou dans CLAUDE.md §Entités) puis régénérer laisse le JSON-LD public émettre un nom de marque qui CONTREDIT le mandat pendant quecheck_artifactsreste VERT — « vert trompeur ». Aucune suitetests/(FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape. - État courant : aucune valeur périmée —
name/url/@id/rattachements/addressCountry/déclaration du spec recoupent CLAUDE.md+MANIFEST+spec exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « SEO org » après le bloc
composition schema.org) : la marque est re-dérivée de CLAUDE.md §Entités
(parenthèse « (marque publique) », exigée présente 1× exactement · zéro duplication),
puis : (a) Organization.name == marque CLAUDE.md ; (b) Organization.url
== MANIFEST.base_url et @id sous base_url ; (c) chaque listing rattaché à
CETTE Organization (brand.@id == org.@id) et sous base_url ; (d)
addressCountry UNIFORME == country_code du spec (code ISO 2 lettres, géo-ciblage
appliqué à l'identique) ; (e) le spec DÉCLARE l'ancrage (organization.name ==
marque + source cite CLAUDE.md + la marque). Racine Organization exigée singleton
en pré-requis.
6 morsures vérifiées (restauré vert après chacune, 7 gates re-verts) :
- artefact
Organization.name→Helios DR→ mord (a) anchor ✓ - CLAUDE.md §Entités rename
Helios RD→Helios Dominicana→ mord SIMULTANÉMENT (a) artefact ET (e) spec — l'ancre est vive ✓ - artefact
Organization.urldivergent → mord (b) url/@id ✓ - artefact un
addressCountryDO→US→ mord (d) uniformité géo ✓ - spec
organization.sourcesupprime l'ancre CLAUDE.md → mord (e) déclaration ✓ - artefact un listing
brand.@id→#phantom→ mord (c) rattachement ✓
Restauré = green : Helios RD == CLAUDE.md · url == base_url · 9 listings rattachés
sous base_url · addressCountry uniforme DO · spec déclare l'ancrage · exit 0.
Working tree byte-restauré (git checkout --, JAMAIS git clean, interdit
absolu) · 7 gates re-verts (check_readme_claims/check_ci_integrity/
check_docs/guard_constraints/check_artifacts/check_regression/validate_json).
ci/README.md(clause de la table récap du pipeline + paragraphe détaillé « Identité du nœud racineOrganizationancrée sur CLAUDE.md §Entités ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; l'injection réelle du<script type="application/ld+json">dans les pagesvente.otov7.comreste côté agent Frontend/SEO / VPS). - Auto-score 4Big : 96/100.
Session 20260801_053228 · Buffer S8 · Legal/CONFOTUR · IDENTITÉ des ENTITÉS porteuses ancrée sur CLAUDE.md §Entités : le champ Select entite_porteuse du DocType CONFOTUR Application — le menu déroulant qui fixe QUELLE entité juridique porte chaque dossier d'incitation touristique — offre pour options les 7 entités canoniques du mandat (WAF · WA SRL · AC Arias Cuevas · Consortium ECR DR · Helios RD · Ploutos · 9060 QC). Ces options sont DÉRIVÉES via options_source de confotur_spec.json[entites] et byte-gatées pour la REPRODUCTIBILITÉ par check_artifacts (l'artefact doctype_confotur_application.json se reconstruit depuis le spec) mais JAMAIS ANCRÉES à CLAUDE.md §Entités, leur source faisant autorité. Les blocs CONFOTUR amont ne gatent que le COMPTE de champs/rôles et la cross-cohérence des PERMISSIONS, AVEUGLES à l'identité de cette liste d'entités.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe
claude-md-constant-anchor-gate). Même patron EXACT que la marque SEO org
ancrée sur CLAUDE.md §Entités (session précédente 050225) et les tokens branding
publiciste/branding ancrés sur #4 — appliqué à une surface distincte du MÊME
§Entités : le déroulant que l'agent ERPNext Backend importe (bench import-fixtures), dont la reproductibilité byte était gatée mais pas l'ancrage.
Dérive silencieuse fermée :
05_deliverables_mvp/legal/confotur/out/doctype_confotur_application.json— champentite_porteuse(Select ·reqd),options= les 7 entités séparées par\n.05_deliverables_mvp/legal/confotur/confotur_spec.json— l'INPUT :entites(la liste) +entites_source(DÉCLARE « CLAUDE.md #Entites (…) ») + le champ dontoptions_source: "entites"(wiring : le déroulant DÉRIVE de la liste).05_deliverables_mvp/legal/confotur/README.md:37-38— prose «entite_porteuse(Select) : entités CLAUDE.md (WAF · WA SRL · AC · Consortium ECR DR · Helios RD · Ploutos · 9060 QC) » (abréviation éditoriale « AC » pour « AC Arias Cuevas »).- Source faisant autorité : CLAUDE.md §Entités — les 7 tokens en gras
**...**de la section « ## Entités », dans l'ordre. - Piège #6 : la byte-gate prouve que l'artefact se reconstruit fidèlement… depuis
un spec dérivé, PAS que la liste == CLAUDE.md. RENOMMER une entité dans
CLAUDE.md §Entités (
9060 QC→9061 QC) OU dansconfotur_spec.json[entites]puis régénérer laisse le DocType offrir un menu qui CONTREDIT/omet une entité canonique du mandat pendant quecheck_artifactsreste VERT — « vert trompeur ». L'agent ERPNext Backend importerait un DocType dont le déroulant liste une entité FANTÔME ou en omet une (la restriction même quereqdpose). Aucune suitetests/(FONCTIONS de génération) ne l'attrape. - État courant : aucune valeur périmée — options artefact /
entitesdu spec / déclaration / README recoupent CLAUDE.md §Entités exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Legal · CONFOTUR —
IDENTITÉ des ENTITÉS porteuses » avant sys.exit) : les 7 entités sont
re-dérivées des tokens en gras de la section « ## Entités » de CLAUDE.md
(uniques, non vides), puis : (a) options du Select de l'artefact == entités
CLAUDE.md (ORDRE exact) ; (b) spec[entites] == CLAUDE.md (l'INPUT ancré) ;
(c) le spec DÉCLARE l'ancrage (entites_source cite CLAUDE.md + chaque
entité) ; (d) wiring intact (options_source == "entites" — sinon le déroulant
est DÉCROCHÉ de la liste ancrée) ; (e) README cite l'ancre « CLAUDE.md » +
nomme chaque entité par nom complet OU 1er token (l'abréviation « AC » tolérée ·
#6, pas de réécriture d'une prose correcte pour satisfaire un gate). Un claim absent
échoue AUSSI (traçabilité #6).
7 morsures vérifiées (restauré vert après chacune, 7 gates re-verts) :
- CLAUDE.md §Entités
9060 QC→9061 QC→ mord SIMULTANÉMENT (a) artefact + (b) spec + (c) déclaration + (e) README = 4 bites — l'ancre est vive ✓ - artefact retire la dernière option → mord (a) ✓
- artefact permute 2 options → mord (a) ORDRE ✓
- spec
entitesrenommePloutos→Ploutos SA→ mord (b) ✓ - spec
entites_sourceretire « CLAUDE.md » → mord (c) ✓ - spec champ
options_source→hardcoded→ mord (d) wiring ✓ - README retire l'entité
Ploutosde la ligne → mord (e) ✓
Restauré = green : options == CLAUDE.md (7, ordre) · spec == CLAUDE.md · spec
déclare l'ancrage · wiring == "entites" · README nomme les 7 · exit 0. Working tree
byte-restauré (git checkout --, JAMAIS git clean, interdit absolu) ·
7 gates re-verts (check_readme_claims/check_ci_integrity/check_docs/
guard_constraints/check_artifacts/check_regression/validate_json).
ci/README.md(clause de la table récap du pipeline + paragraphe détaillé « Entités porteuses du DocType CONFOTUR ancrées sur CLAUDE.md §Entités ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; l'import réel du DocTypeCONFOTUR Applicationreste côté agent ERPNext Backend / VPS). - Auto-score 4Big : 96/100.
Session 20260801_060234 · Buffer S8 · Legal/CONFOTUR — cross-cohérence PERMISSIONS répliquée dans la FICHE AGENT 03_agents/onapi_legal/AGENT.md:43-45 : la MÊME table role→portail→actions que le README du module, jamais gatée
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Classe explicitement
notée en mémoire (agent-fiche-role-attrs-ungated) : une fiche 03_agents/*/AGENT.md
peut restituer la MÊME donnée data-derived qu'un README déjà gaté, sur une surface
distincte que le gate README ne voit pas.
Dérive silencieuse fermée :
03_agents/onapi_legal/AGENT.md:43-45— §Permissions de la fiche de l'agent ONAPI/Legal, annoncée « mot pour mot lespermissions_ciblesRBAC des 3 rôles » :ventes-confotur(portail Ventes) → read/write/create/print ·legal-onapi(portail Direction) → read/write/create ·legal-directeur(portail Direction) → read/write/submit/report. Convention DIFFÉRENTE du README du module (/espacé ·**submit**en gras).- Source faisant autorité :
legal/confotur/out/MANIFEST.json(roles_rbac_utilises[]:role_id·portail·permissions, recomputé du contratrbac_50_roles.jsonà chaque build · byte-gaté par check_artifacts). - Piège : le bloc « Legal confotur perms · cross-cohérence » existant ne gate QUE
la §Permissions du README du MODULE (
legal/confotur/README.md), AVEUGLE à la fiche. PROMOUVOIRventes-confoturàsubmit(élévation de privilège · casse la séparation des pouvoirs dontis_submittableest déduit), RETIRER une action, RÉAFFECTER un portail, RENOMMER un rôle ou AJOUTER une ligne fantôme dans la FICHE laisse celle-ci périmée pendant que l'artefact (et le README, lui gaté) disent autre chose → l'agent ONAPI/Legal lirait sa PROPRE doc d'identité mentant sur le jeu de permissions qu'il porte (le risque même que « mot pour mot » promet d'écarter) — « vert trompeur » qu'aucune suitetests/(FONCTIONS de mapping RBAC, jamais la prose de fiche) n'attrape. - État courant : aucune ligne périmée — les 3 couples portée→actions et leurs portails recoupent l'artefact exactement (anti-invention #6). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Legal · CONFOTUR —
cross-cohérence PERMISSIONS répliquée dans la FICHE AGENT » après le bloc
cross-cohérence du README) : RÉUTILISE conf_perm_by_id/conf_portail_by_id
déjà dérivés du MANIFEST par le bloc README (ZÉRO duplication du contrat #6) ;
regex tolérant l'espacement / et le gras ** ; PAR rôle le portail (casse
normalisée) ET le jeu d'actions exigés EXACTS (set-diff : absent ET en trop) ;
identité d'ensemble fiche⇔MANIFEST (aucun rôle fantôme ni manquant). Un claim
absent échoue AUSSI (traçabilité).
6 morsures vérifiées : fiche promeut ventes-confotur→submit (en trop) ·
fiche retire create de legal-onapi (absent) · fiche réaffecte portail
Ventes→Direction · fiche renomme legal-directeur→legal-boss (ligne
INTROUVABLE + ensemble absents/fantôme) · sous-liste entière supprimée (3
INTROUVABLES + ensemble vide) · artefact promeut ventes-confotur→submit,
fiche stale (le vrai « silent green » : source change, fiche fige) ; restauré =
green : 3 rôles portail+actions == MANIFEST · identité d'ensemble · exit 0.
Working tree byte-restauré (git checkout --, JAMAIS git clean) · 7
gates re-verts.
ci/README.md(table récap du pipeline · clause « 3e surface » CONFOTUR) mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact). - Auto-score 4Big : 96/100.
Session 20260801_063234 · Buffer S8 · Chat OTOIA/persona+capabilities : l'IDENTITÉ de la persona (Amélie / voix multilingual_v2) et des 4 CAPABILITIES OTOIA (aec.py + knowledge.py + prompt_engine.py + chat.py) du montage Chat par portail frontend/chat_otoia — portées par out/chat_mount.json (5 configs runtime) + out/MANIFEST.json, byte-gatés pour la reproductibilité (check_artifacts prouve la reconstruction depuis chat_spec.json) mais JAMAIS ANCRÉES à CLAUDE.md §Architecture cible, leur source faisant autorité (« Voix Amélie QC (multilingual_v2) » l.30 · « OTOIA capabilities : aec.py + knowledge.py + prompt_engine.py + chat.py » l.28).
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe
claude-md-constant-anchor-gate). Même patron EXACT que les tokens branding
publiciste/branding ancrés sur CLAUDE.md #4, la marque SEO Organization ancrée sur
§Entités, les paramètres canoniques faisabilite/generator ancrés sur #9/#10 —
appliqué à une surface distincte : la persona + les capabilities émises dans CHAQUE
portail rôle, dont la reproductibilité byte était gatée mais pas l'ancrage.
Dérive silencieuse fermée :
05_deliverables_mvp/frontend/chat_otoia/out/chat_mount.json— les 5 mounts, chacunpersona: {nom: "Amélie", voix: "multilingual_v2"}+capabilities: [aec.py, knowledge.py, prompt_engine.py, chat.py]; idemout/MANIFEST.json; l'INPUTchat_spec.json(persona +capabilities[].module) ; la table README:28-29 (« PersonaAmélie/multilingual_v2» · « Capabilitiesaec/knowledge/prompt_engine/chat.py» toutes deux annotées «CLAUDE.md §Architecture cible»).- Source faisant autorité : CLAUDE.md §Architecture cible (l.28 capabilities · l.30 « Voix Amélie QC (multilingual_v2) »).
- Piège #6 : le SEUL contrôle d'identité vit dans
tests/(test_persona_amelie_sourcee/test_capabilities_sourcees_sans_ajout) mais son ORACLE est HARDCODÉ (self.assertEqual(spec["persona"]["nom"], "Amélie")·caps == ["aec.py", …]) — une copie de plus, jamais comparée à CLAUDE.md. Renommer la persona (Amélie→Sophie), changer la voix, ou renommer/RETIRER une capability danschat_spec.json(ou dans CLAUDE.md) reconstruit l'artefact fidèlement (byte-gate VERTE) sans toucher l'oracle du test (tests VERTS) → le chat monté dans CHAQUE portail annoncerait une persona/voix/capabilities qui CONTREDIT le mandat — « vert trompeur » qu'aucune suitetests/(FONCTIONS de génération, jamais l'ancre à CLAUDE.md) n'attrape. - État courant : aucune valeur périmée — persona + capabilities recoupent CLAUDE.md exactement (anti-invention #6, rien à réécrire). Le défaut est l'ancrage ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Chat OTOIA · persona +
capabilities ancrées CLAUDE.md » avant sys.exit) : persona+capabilities re-dérivées
de CLAUDE.md §Architecture cible (regex Voix … QC (…) + OTOIA capabilities : …
split +, NFC), puis : (a) chat_spec.json persona/capabilities == CLAUDE.md
(ordre exact) ; (b) le spec DÉCLARE l'ancrage (persona.source + chaque
capability.source citent CLAUDE.md) ; (c) CHACUN des 5 mounts + le MANIFEST
portent persona/capabilities == CLAUDE.md ; (d) README cite l'ancre §Architecture
cible + nomme persona (Amélie+multilingual_v2) + chaque capability (notation
compacte matchée par radical) ; (e) l'ORACLE du test == CLAUDE.md (set-diff). Un
claim absent échoue AUSSI (traçabilité #6).
8 morsures vérifiées : CLAUDE.md Amélie→Sophie (mord SIMULTANÉMENT
spec/mount/README/oracle — 5 fails, l'ancre est vive) · CLAUDE.md retire chat.py
(3 fails) · spec aec.py→exfil.py (capability injectée) · spec persona.source
sans CLAUDE.md · UN SEUL mount à voix altérée (byte-gate aveugle) · README retire
le radical prompt_engine · oracle du test persona Amélie→Bob · oracle du test
capabilities réordonné/tronqué ; restauré = green : 5 sous-checks (a/b/c/d/e) verts ·
exit 0. Working tree byte-restauré (git checkout --, JAMAIS git clean, interdit
absolu) · 7 gates re-verts · unittest module chat_otoia re-vert.
ci/README.md(table récap du pipeline + paragraphe détaillé « Persona & capabilities du Chat OTOIA ancrées sur CLAUDE.md §Architecture cible ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; l'import réel desCustom Block+ le câblage de l'endpoint OTOIA restent côté agent ERPNext Backend / VPS). - Auto-score 4Big : 96/100.
Session 20260801_070237 · Buffer S8 · Mobile/app_config : le SDK MAJEUR Expo (« Expo 54 ») — la version majeure que l'app compagnon cible, ANCRÉE sur la roadmap Sprint 5 l.56 « Rebuild Expo 54 » — était transcrite À LA MAIN cinq fois dans le README SANS AUCUN gate d'IDENTITÉ ni d'ANCRAGE. Le bloc « Mobile · récap de l'app Expo » existant ne gate QUE le quadruplet onglets/rôles/langues/identifiants a_confirmer (comptes de MANIFEST.counts), AVEUGLE au majeur du SDK.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même patron d'ANCRAGE que
la marque SEO §Entités (Helios RD ancrée sur CLAUDE.md) ou les roadmap_line de la
recette qa/acceptance — appliqué ici à une valeur ancrée sur la ROADMAP (source
faisant autorité de la version Expo, CLAUDE.md ne la spécifiant pas).
Dérive silencieuse fermée :
05_deliverables_mvp/mobile/app_config/README.md:4/19/35/72— « Expo 54 » (×4 mentions) + la ligne de source « Expo SDK 54 | roadmap Sprint 5 l.56 ».- Chaîne de dérivation (byte-gatée par
check_artifacts) : roadmap Sprint 5 l.56 « Rebuild Expo 54 » (INPUT faisant autorité) →mobile_spec.json[app](expo_sdk_major = 54+expo_sdk_sourcecite la roadmap) →out/app_config.json.expo.extra.expoSdkMajor = 54+out/MANIFEST.json .app.expo_sdk_major = 54. - Piège #6 : BUMPER le SDK (Expo 55 sort ·
eas buildcible 55) dans le spec/artefact SANS toucher au README (ou l'inverse), ou DÉRIVER la roadmap de l'artefact, laisse la prose PÉRIMÉE en silence pendant que le graphe byte-gaté dit autre chose → l'agent Mobile lanceraiteas buildsur le MAUVAIS SDK (le rebuild même que la ligne roadmap prescrit). Aucune suitetests/(FONCTIONS de génération, jamais la prose ni l'ancre à la roadmap) n'attrape ce « vert trompeur ». - État courant : aucune mention périmée — les 4 « Expo 54 », le spec, le MANIFEST et la roadmap concordent (anti-invention #6, rien à réécrire). Le défaut est la surface ungated + non ancrée à la roadmap.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Mobile · le SDK MAJEUR
Expo … ANCRÉ sur la roadmap Sprint 5 l.56 », inséré avant sys.exit) : le majeur
sdk re-dérivé de app_config.json byte-gaté, puis (a) cohérence interne
artefact ⇔ MANIFEST.app.expo_sdk_major ⇔ spec.app.expo_sdk_major ; (b)
ancrage roadmap — re.search("Rebuild Expo (\d+)") sur
04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md == sdk (ancre vive) ; (c) le spec DÉCLARE
l'ancrage (expo_sdk_source cite « roadmap » + « Expo N ») ; (d) toute mention
prose « Expo [SDK] N » (regex Expo(?:\s+SDK)?\s*\*{0,2}(\d+)) == sdk (aucune
périmée) ; (e) la ligne de source du tableau cite l'ancre roadmap à côté du majeur.
Un claim absent échoue AUSSI (traçabilité).
7 morsures vérifiées : README Expo SDK **54**→**55** (prose périmée) ·
artefact 54→55 (README + roadmap périmés = le vrai silent green) ·
roadmap Rebuild Expo 54→55 (artefact périmé · ancre qui dérive) ·
MANIFEST 54→55 (chaîne interne incohérente) · spec expo_sdk_source sans
« roadmap » (ancrage déclaré manquant) · README ligne de source sans « roadmap »
(ancrage prose manquant) · README retire TOUTE mention « Expo N » (claim absent) ;
restauré = green : cohérence interne · ancre roadmap vive · spec déclare · 4
mentions == 54 · ligne source cite roadmap · exit 0. Working tree byte-restauré
(git checkout --, JAMAIS git clean) · 7 gates re-verts.
ci/README.md(table récap du pipeline + paragraphe détaillé « SDK majeur Expo de l'app mobile ancré sur la roadmap Sprint 5 l.56 ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ;eas build/eas submitApp Store #32 / Play Store restent côté agent Mobile · VPS). - Auto-score 4Big : 96/100.
Session 20260801_073239 · Buffer S8 · Fiscal/ecf_dgii : le JEU DE DEVISES moneda (USD/DOP) ANCRÉ sur CLAUDE.md #10 — le champ que TipoMoneda porte dans chaque e-CF DGII, byte-gaté pour la REPRODUCTIBILITÉ mais JAMAIS ancré à la contrainte. Le README s'en réclame « #10 » (README:37/57/77) ; les blocs fiscaux amont gatent le FormaPago (Cardnet · #10) + états/champs émetteurs, AVEUGLES au jeu de devises ; le bloc « Fiches #10 » ancre les fiches ERPNext/CRM, AVEUGLE au module fiscal.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même patron d'ANCRAGE que
les paramètres canoniques faisabilite/generator (#9/#10), la marque SEO §Entités
(Helios RD), la persona Chat OTOIA (§Architecture cible) ou les tokens branding #4
— appliqué ici à une valeur ancrée sur CLAUDE.md #10 (« USD + DOP devises »).
Dérive silencieuse fermée :
05_deliverables_mvp/fiscal/ecf_dgii—moneda.options = ["USD","DOP"]dans le spec ET (byte-gaté parcheck_artifacts) dansout/ecf_plan.json.- Chaîne de dérivation : CLAUDE.md #10 « USD + DOP devises » (INPUT faisant
autorité) →
ecf_spec.json[moneda].options→out/ecf_plan.json.moneda.options(copie verbatim parecflib/builder.py:79). - Piège #6 : byte-gater une constante prouve la REPRODUCTIBILITÉ, PAS l'ANCRAGE.
check_artifactsprouveecf_plan==builddepuis le spec, JAMAIS quespec[moneda]==#10. RENOMMER/ÉTENDRE les devises dans CLAUDE.md #10 (USD + DOP→USD + EUR, ou +EUR) ET dans le spec de façon cohérente reste byte-VERT tout en faisant émettre à l'e-CF unTipoMonedad'une devise qui CONTREDIT le mandat — « vert trompeur » qu'aucune suitetests/(FONCTIONS de génération, jamais l'ancre à CLAUDE.md) n'attrape. L'agent ERPNext Backend câbleraitEncabezado.TipoMonedasur une devise hors mandat (le risque même que la cross-cohérence e-CF veut prévenir). - État courant : aucune dérive — spec/artefact/README concordent avec #10
(anti-invention #6). Le défaut était la surface byte-gatée mais non ancrée +
l'absence de déclaration d'ancrage (
moneda.sourcelegitimementnullcar il source le taux TipoCambio, à confirmer côté VPS · #8 — l'ancre des OPTIONS devait vivre dans un champ DISTINCT).
Correctif de déclaration : nouveau champ moneda.options_source dans
ecf_spec.json (+ requis dans ecf.schema.json) citant « CLAUDE.md #10 — devises
USD + DOP » ; distinct de source (taux, reste null/a_confirmer). Artefact
out/ecf_plan.json régénéré (byte-gate re-verte · 39 tests module verts · le compte
a_confirmer inchangé, il ne teste que moneda.a_confirmer).
Gate ajouté (ci/check_readme_claims.sh, bloc « Fiscal · ecf_dgii — le JEU DE
DEVISES moneda … ANCRÉ sur CLAUDE.md #10 », inséré après la famille « Fiches
#10 ») : RÉUTILISE exp["devises"] déjà recomputé depuis #10 (zéro duplication),
want_cur = re.findall("[A-Z]{3}", exp["devises"]), puis (a) spec[moneda].options
== want_cur ORDRE EXACT (mord ajout/retrait/réordonnancement) ; (b) l'artefact
byte-gaté idem ; (c) moneda.options_source cite CLAUDE.md #10 + chaque devise ;
(d1) la ligne README « devise (…USD/DOP… · #10) » cite le jeu EXACT ; (d2) toute
mention moneda/devise d'une paire de devises (scan ancré sur le contexte, évite
le faux positif RNC / ITBIS) == le jeu (aucune périmée). Un claim absent échoue
AUSSI (traçabilité).
7 morsures vérifiées : CLAUDE.md #10 USD+DOP→USD+EUR (l'ancre est vive →
mord a+b+c+d1 en cascade) · spec options USD/DOP→USD/EUR régénéré (le vrai
silent green → mord a) · artefact seul USD→EUR (spec+README périmés → mord
b) · reorder spec [DOP,USD] (ordre exact → mord a) · options_source sans
« CLAUDE.md #10 » (ancre déclarée manquante → mord c) · README ligne #10
USD/DOP→USD/EUR (prose périmée → mord d1) · README table moneda USD/DOP→ USD/EUR (mention périmée → mord d2) ; restauré = vert (a+b+c+d, 3 mentions
README concordantes · exit 0). Working tree byte-restauré (git checkout --,
JAMAIS git clean) · 7 gates re-verts (validate_json · check_artifacts ·
check_readme_claims · check_docs · guard_constraints · check_ci_integrity ·
check_regression).
ci/README.md(clause de récap du pipeline + paragraphe détaillé « jeu de DEVISESmoneda… 4e surface du MÊME README, ancré sur #10 ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ; la confirmation du TipoCambio USD→DOP + l'émission réelle via Compupar restent côté agent ERPNext Backend / Compta · VPS).
- Auto-score 4Big : 96/100.
Session 20260801_080244 · Buffer S8 · SEO/projets_master — IDENTITÉ du CATALOGUE PROJETS (P01..P09) ANCRÉE sur CLAUDE.md §Projets
Domaine : SEO · 05_deliverables_mvp/seo/fixtures/projets_master.json + artefacts out/seo_schema_org.json.
Constat (anti-drift #6) : la matière première du worker SEO est projets_master.json — 9 projets (code + nom) que le bundle SEO expose à Google (un listing Residence par projet dans le JSON-LD indexé sous vente.otov7.com/projets/…). Chaque entrée DÉCLARE source.fichiers: ["CLAUDE.md#Projets"] et le README:80 l'affirme (« 9 projets P01..P09 · noms sourcés de CLAUDE.md §Projets ») — MAIS AUCUN gate ne le VÉRIFIAIT. check_artifacts prouve QUE les out/ se reconstruisent depuis la fixture (byte-for-byte), PAS que ses code/nom == CLAUDE.md §Projets ; les blocs SEO amont gatent les COMPTES de mots-clés, la COMPOSITION du graphe schema.org (bijection un-listing-par-projet) et l'IDENTITÉ de la marque racine Helios RD (§Entités) — tous AVEUGLES à l'identité de la liste de projets.
Vert trompeur : RENOMMER un projet dans CLAUDE.md §Projets (« Aqua Terra Las Terrenas » → « … Bay »), le RENOMMER/AJOUTER/RETIRER dans la fixture, puis régénérer, laisse le JSON-LD PUBLIC lister un projet FANTÔME ou en OMETTRE un canonique pendant que la byte-gate reste VERTE (l'artefact se reconstruit fidèlement… depuis une fixture DÉRIVÉE) et que le compte reste 9. Même classe EXACTE que la marque org (§Entités · session 050225), les entités CONFOTUR (§Entités · 053228) et les libellés projets du run_sheet démo (§Projets · déjà gaté). Aucune suite tests/ (FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape.
Gate ajouté (ci/check_readme_claims.sh, bloc « SEO · l'IDENTITÉ du CATALOGUE PROJETS ») — RE-DÉRIVE le catalogue de CLAUDE.md §Projets (zéro duplication · source unique), puis exige :
- (a) identité d'ensemble des codes fixture ⇔ CLAUDE.md §Projets (aucun fantôme/manquant · pas de doublon) ;
- (b)
nomde chaque entrée == CLAUDE.md §Projets (accent-insensible : la fixture normalise « Xamaná »→« Xamana ») ; - (c) chaque entrée DÉCLARE l'ancrage
CLAUDE.md#Projetsdanssource.fichiers; - (d) les
namedes listings du graphe schema.org PUBLIC == l'ensemble des noms CLAUDE.md §Projets + bijection (1 listing/projet) +MANIFEST.countsprojects/listings == |§Projets| ; - (e) le README cite l'ancrage CLAUDE.md §Projets. Un claim absent échoue AUSSI (disparition = régression).
Vérif : 13 confirmations vertes (9 noms + set codes + listings schema.org + MANIFEST + README) ; 7 morsures vérifiées puis restaurées — CLAUDE.md rename P07 (bite b+d simultanés, l'ancre est vive) · fixture nom drift P03 · fixture drop P09 (set manquant) · fixture add fantôme P10 (set fantôme) · fixture strip source.fichiers P01 · artefact schema.org rename Structure→Estructura (le vrai silent-green public) · README retire la citation §Projets. Restauré vert. 7 gates CI re-verts (check_readme_claims, check_artifacts, check_docs, check_ci_integrity, guard_constraints, check_regression, validate_json).
- Auto-score 4Big : 96/100.
Session 20260801_083254 · Buffer S8 · DevOps/deploy_runbook — IMPUTABILITÉ des CONFIRMATIONS préalables VPS (owner + réf-contrôle) ancrée sur out/deploy_runbook.json ⨯ qa/audit_5d
Domaine : DevOps · 05_deliverables_mvp/devops/deploy_runbook/README.md:55-58
(ligne « Confirmations préalables VPS ») ⨯ out/deploy_runbook.json (byte-gaté) ⨯
05_deliverables_mvp/qa/audit_5d/out/audit_report.json (byte-gaté).
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même classe de « vert
trompeur » que la table run-book (2e surface deploy_runbook) ou l'identité du RÔLE
émetteur e-CF — appliquée à une 3e surface distincte du MÊME README deploy_runbook :
non plus le COMPTE de confirmations ni l'ENSEMBLE de leurs noms (déjà gatés), mais
l'imputabilité de chacune — QUI la porte et QUEL contrôle audit_5d la trace.
Dérive silencieuse fermée :
devops/deploy_runbook/README.md:55-58— la ligne « Confirmations préalables VPS » annote CHAQUE confirmation réglementaire d'un COUPLE data-derivednom(owner · réf-contrôle) :taux_commission(Direction · audit_5d D1.1) ·rnc_emisor(Compta · D1.2) ·itbis_tipocambio(Fiscaliste eCF · D1.3) ·seuil_uaf(Oficial de Cumplimiento · D2.3) ·endpoint_otoia(ERPNext Backend · session 19). L'OWNER = qui doit confirmer le paramètre avant déploiement ; la RÉF = le contrôle qa/audit_5d (D#.#) — ou la session — qui en assure le suivi.- Sources faisant autorité (double, byte-gatées par
check_artifacts) : (a) lesconfirmationsdeout/deploy_runbook.json— chaque item porteowner+source; (b)qa/audit_5d/out/audit_report.json— chaquecontrol(D1.1..D5.3) porte SON PROPREowner. L'owner et le contrôle ne vivent PAS dans le MANIFEST (confirmations_ouvertesn'est qu'une liste de NOMS) : ils ne sont vérifiables que contre ces deux artefacts croisés. - Piège #6 : les blocs DevOps amont ne gatent que le COMPTE (2) et l'ENSEMBLE des
noms — AVEUGLES à QUI porte chaque confirmation et à QUEL contrôle la trace.
RÉAFFECTER
taux_commissionde Direction à Ventes (mauvais responsable escaladé → confirmation jamais obtenue · séparation des pouvoirs cassée), MISATTRIBUER le contrôle (D1.1→D2.1, pointant un contrôle qui ne trace PAS ce paramètre) ou citer un contrôle FANTÔME (D9.9, absent d'audit_5d) laisse la prose périmée EN SILENCE pendant que l'artefact/l'audit disent autre chose → l'agent DevOps escaladerait le mauvais owner / le mauvais contrôle. Aucune suitetests/(FONCTIONS de graphe) n'attrape ce « vert trompeur ». - État courant : aucune annotation périmée — les 5 couples owner/réf recoupent
deploy_runbook.json, et les 4 réfD#.#existent dans audit_5d avec un owner compatible (Oficial de Cumplimiento⊆Oficial de Cumplimiento / UAF,Fiscaliste eCF⊂Compta Fiscaliste eCF) (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, bloc « 2ter) deploy_runbook —
IMPUTABILITÉ des CONFIRMATIONS » après le bloc table de phases) : confirmations
aplaties de deploy_runbook.json (unicité inter-phases), contrôle audit_5d résolu de
la source (D#.#), annotations README parsées nom (owner · réf), puis (a)
cross-module — chaque confirmation tracée porte un contrôle audit_5d RÉEL dont l'owner
est COMPATIBLE avec l'owner run-book (sous-ensemble de tokens · tolère « / UAF ») ET
elle reste ANNOTÉE au README ; (b) chaque annotation désigne une confirmation
RÉELLE (pas fantôme), owner README ⊆ owner run-book (tolère l'abréviation), et la réf
== contrôle réel (présent audit_5d + source) ou, pour une réf textuelle
(« session 19 »), présente dans la source. Un claim absent échoue AUSSI (traçabilité #6).
9 morsures vérifiées (4 côté README · 3 côté artefact · 2 réf) : README réaffecte
owner Direction→Ventes (mauvais responsable) · README misattribue D1.1→D2.1
(contrôle périmé vs source) · README cite contrôle fantôme D9.9 · README retire
l'annotation de taux_commission (tracée par D1.1, INTROUVABLE) · artefact
réaffecte taux_commission owner→Ventes (README stale = le vrai silent green · mord
cross-module ET README⊆) · artefact audit_5d D1.1 owner Direction→Finanzas
(divergence inter-modules) · artefact source seuil_uaf D2.3→D9.9 (contrôle
FANTÔME cité par la source) · README annotation fantôme (endpoint_otoia→endpoint_xxx) ·
README réf textuelle session 19→session 42 (absente de la source) ; restauré = green :
5 couples owner/réf == artefacts croisés · 4 réf D#.# présentes audit_5d + source ·
exit 0. Working tree byte-restauré (git checkout --, JAMAIS git clean, interdit
absolu) · 7 gates re-verts (check_readme_claims/check_ci_integrity/check_docs/
guard_constraints/check_artifacts/check_regression/validate_json).
ci/README.md(clause de la table récap du pipeline + paragraphe détaillé « 3ᵉ surfacedevops/deploy_runbook— imputabilité des confirmations ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; l'obtention réelle des 7 confirmations auprès de leurs owners reste côté agent DevOps / VPS). - Auto-score 4Big : 96/100.
Session 20260801_093301 · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §3 — DÉFAUT FACTUEL corrigé + gate d'identité par portail
Contexte : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6). Cette fois la surface data-derived non gatée était
FAUSSE, pas seulement à risque : la table §3 « Cartographie portails ↔ familles »
de RBAC_50_ROLES_SPEC.md (colonne « Nb rôles » PAR portail) avait dérivé du contrat
rbac_50_roles.json en préservant le total 50, donc en silence.
Défaut factuel trouvé & corrigé :
05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md:47— portaildirectionaffichait 10 rôles ; le contrat en compte 9 (6 direction + CFO/finance + analyste faisabilité + 2 legal).…:52— portailplateformeaffichait 5 ; le contrat en compte 6 (devops · rbac-admin · qa · otoia · mobile · bi).- L'erreur était compensée (10+5 vs 9+6) → total 50 exact → invisible aux gates agrégés. Le MANIFEST roleprofile (byte-gaté) disait déjà 9 et 6 : la SPEC seule avait dérivé. Corrigé pour refléter la source.
Dérive silencieuse fermée (gate durable) :
ci/check_readme_claims.sh— nouveau bloc « 1quater) RBAC_50_ROLES_SPEC §3 » : RECOMPTE le nombre de rôles PAR portail depuisrbac/rbac_50_roles.json(source de vérité, byte-gatée parcheck_artifacts) et exige l'égalité avec CHAQUE ligne de la table §3 plus le total**Total** | **50**. Un portail absent de la table échoue aussi (disparition = régression de traçabilité). Complète le gate roleprofile (qui gate le MANIFEST, un doc distinct) sur une 2ᵉ surface transcrivant la même ventilation.- Vérif négative : réintroduire
direction=10fait échouer le gate (exit 1, message « dit 10 MAIS rbac_50_roles.json compte 9 ») ; restauré → vert.
Artefact régénéré (reproductibilité #artifact-reproducibility-gate) :
qa/audit_4big/out/quality_report.json— l'evidenceDOC cite la taille en octets de la SPEC (10040 → 10039 octets après correction −1 o) ; rebuildaudit_4big_gen.py build -o outbyte-identique.check_artifactsre-vert.
CI : les 7 gates PASS (validate_json · check_artifacts · check_docs · check_readme_claims · guard_constraints · check_regression · check_ci_integrity).
- Hors périmètre worker (VPS · #8) : néant (correction d'un doc en-repo + gate
bash/python3 stdlib ; l'unique artefact
out/touché a été régénéré à l'identique). - Auto-score 4Big : 96/100.
Session 20260801_100301 · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §7 — comptes du run-book d'application ancrés sur le contrat rbac_50_roles.json
Contexte : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6). Même classe que le §3 gaté à la session précédente
(093301, per-portail) : une 2ᵉ surface de la MÊME SPEC transcrivant à la main des
comptes data-derived, déjà gatés ailleurs mais AVEUGLES à cette note (mémoire
rbac-spec-portail-count-gate).
Dérive silencieuse fermée :
05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md§7 — la NOTE « ✅ … est livré en-repo » restitue TROIS comptes du run-book d'application sur une surface DISTINCTE des READMEsuserperm_gen/roleprofile_gen(déjà gatés) et du §3 (ventilation par portail, bloc1quater) : « 28 templatesCompany(portéeentite) » · « 6 profils (5 portails métier + console techniqueplateforme) » · « 50 rôles de façon bijective ».- Source faisant autorité :
rbac/rbac_50_roles.json(contrat, byte-gaté parcheck_artifacts). Recomputes : 28 == |scope_donnees == entite| (= nombre deUser Permissiontemplates) · 6 == |portails distincts| (1Role Profilepar portail) · 5 == portails métier (horsplateforme) · 50 == |rôles|. - Piège #6 : BUMPER une portée
entite→groupe(le décompte de templates change) ou AJOUTER/RETIRER un portail (le décompte de profils change) dans le contrat périme la note EN SILENCE pendant que les READMEs de volet et le §3 restent verts → l'agent ERPNext Backend lirait un run-book promettant un décompte faux. Aucune suitetests/(FONCTIONS de génération/résolution RBAC, jamais la prose de la SPEC) n'attrape ce « vert trompeur ». - État courant : aucun compte périmé — 28/6/5/50 recoupent le contrat exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, bloc « 1quinquies) RBAC_50_ROLES_SPEC §7 »
après le bloc §3) : réutilise src_roles (contrat) + spec_rd (déjà chargés par le
§3, zéro duplication). Le blockquote markdown > de la note survit au collapse de
read_norm et fragmente la prose (« 28 > templates ») ⇒ neutralisé (spec7 = spec_rd.replace("> ", "")) avant match. Exige : (a) « N templates Company (portée
entite) » == |scope==entite| ; (b) « N profils (M portails métier + console
technique plateforme) couvrant les K rôles de façon bijective » == |portails
distincts|/|métier hors plateforme|/|rôles|. Garde anti-typo : plateforme doit exister
au contrat (sinon la note « console technique plateforme » est fausse). Un claim absent
échoue AUSSI (INTROUVABLE = régression #6).
6 morsures vérifiées (restauré vert après chacune) : SPEC 28→27 (prose périmée) ·
SPEC 6 profils→7 · SPEC 5 portails métier→4 · SPEC 50→49 · contrat une portée
entite→groupe (28→27 · README stale = le vrai silent green · « SPEC dit 28 MAIS
compte 27 ») · SPEC retire « 28 templates » (claim absent = INTROUVABLE) ; restauré =
green : UP templates 28 == |entite| · Role Profiles 6 (métier 5) bijectifs sur 50 ==
contrat · exit 0. Working tree byte-restauré (git checkout --, JAMAIS git clean,
interdit absolu) · 7 gates re-verts (check_readme_claims/check_ci_integrity/
check_docs/guard_constraints/check_artifacts/check_regression/validate_json).
ci/README.md(clause de la table récap du pipeline + paragraphe détaillé « Comptes du run-book d'applicationRBAC_50_ROLES_SPEC.md§7 ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
aucune écriture dans un
out/⇒ 0 dérive d'artefact ; l'applicationbenchréelle reste côté agent ERPNext Backend / VPS). - Auto-score 4Big : 96/100.
Session 20260801_103304 · Buffer S8 · Chat OTOIA — IDENTITÉ des configs runtime (persona · langues · endpoint) dans la fiche Frontend Console
Contexte : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la série anti-dérive CLAUDE.md #6, classe « fiche agent qui restitue des attributs data-derived, gatée uniquement sur des COMPTES agrégés » — mémoire agent-fiche-role-attrs-ungated).
Dérive silencieuse fermée :
03_agents/frontend_console/AGENT.md:29— la cellule « Métrique vérifiée » du livrablechat_otoiarestitue TROIS attributs data-derived du montage OTOIA : « persona Amélie … · langues FR/EN/ES ·`endpoint: null`». Le bloc chat_otoia amont (ci/check_readme_claims.sh) ne recompute que le COMPTE blocks/mounts/portails (tous = 5) — AVEUGLE à CE QUE dit chaque config runtime.- Le gate persona/capabilities « ancré sur CLAUDE.md §Architecture cible » vise le spec, les 5 mounts, le MANIFEST, le README et l'oracle du test — JAMAIS cette cellule de fiche, et il ignore langues comme endpoint. La cellule était donc une surface data-derived ungated de plus.
- Sources faisant autorité (byte-gatées par
check_artifacts) :frontend/chat_otoia/out/chat_mount.json(une config par portail · 5) +out/MANIFEST.json(persona/langues/endpoint_statut consolidés). endpoint: null= AFFIRMATION D'HONNÊTETÉ #6/#8 : l'endpoint OTOIA n'est PAS fabriqué (renseigné côté VPS · agent Frontend). Qu'une config gagne un endpoint réel dans l'artefact et la fiche « endpoint: null » devient un MENSONGE en silence.- État courant : aucun attribut périmé — persona
Amélie· languesfr/en/es·endpoint == nullsur les 5 mounts ET le MANIFEST (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, bloc « Chat OTOIA · IDENTITÉ des configs
runtime (persona · langues · endpoint) » après le bloc de COMPTE chat_otoia) : charge
MANIFEST.json + chat_mount.json (5 configs) et exige, sur la cellule de la fiche
(regex persona (\S+) QC · langues ([A-Z/]+) · \endpoint: ([^`]+)`, ne matche QUE la ligne :29, pas la prose Scope :12) : (a) persona == MANIFEST.persona.nom(le « QC » est le qualificatif de marqueVoix Amélie QCde CLAUDE.md, gardé en littéral NON gaté) ; (b) languesFR/EN/ES==MANIFEST.languesen majuscule ; (c)endpoint:
nullavecall(m.endpoint is None)sur les 5 mounts (sinonexp_endpointdevientNON-null(...)⇒ morsure d'honnêteté). Cohérences croisées :MANIFEST.persona.nometMANIFEST.langues== CHAQUE mount (montage désynchronisé mordu) ·endpoint_statut == 'a_confirmer'⇔ endpoints TOUS null (incohérence interne artefact sinon). Un claim absent échoue AUSSI (INTROUVABLE = régression #6). Aucune suitetests/` (FONCTIONS du builder, jamais la prose de la fiche) n'attrape ce
« vert trompeur ».
6 morsures vérifiées (restauré vert après chacune) : fiche persona Amélie→Amelia
(mismatch) · fiche langues FR/EN/ES→FR/EN · fiche endpoint: null→endpoint: fixé ·
artefact un mount endpoint null→URL réelle (bascule d'honnêteté ET incohérence
endpoint_statut='a_confirmer' — le vrai silent green : l'endpoint fabriqué pendant
que la fiche promet null) · fiche retire le bloc attributs (claim absent =
INTROUVABLE) · artefact un mount persona Amélie→Nadia (montage désynchronisé du
MANIFEST). Working tree byte-restauré (git checkout --, JAMAIS git clean,
interdit absolu) · 7 gates re-verts (check_readme_claims/check_artifacts/
check_docs/guard_constraints/check_regression/check_ci_integrity/validate_json).
ci/README.md(clause de la table récap du pipeline + paragraphe détaillé sous le bloc de COMPTE chat_otoia, avec renvoi au gate persona/capabilities distinct) mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
aucune écriture dans un
out/⇒ 0 dérive d'artefact ; le câblage réel de l'endpoint OTOIA + web-component reste côté agent Frontend / VPS). - Auto-score 4Big : 96/100.
Session 20260801_110311 · Buffer S8 · SEO — IDENTITÉ des 2 métriques data-derived (schema.org · hreflang) de la fiche agent
Contexte : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la série anti-dérive CLAUDE.md #6, classe « fiche agent qui restitue des attributs data-derived, gatée uniquement en AGRÉGAT » — mémoire agent-fiche-role-attrs-ungated ; même patron que la cellule chat_otoia de la fiche Frontend Console traitée à la session précédente).
Dérive silencieuse fermée :
03_agents/seo/AGENT.md:25-26— le tableau « Livrable réellement produit » restitue DEUX sorties byte-gatées du bundle SEO à côté du compte de mots-clés (lui déjà gaté sur README ET fiche) :- L25 : « graphe JSON-LD : 10 nœuds (
Organization+ 9Residence) » - L26 : « 10 pages × FR/EN/ES +
x-default»
- L25 : « graphe JSON-LD : 10 nœuds (
- Les blocs SEO schema.org (
SEO_SG_RD) et hreflang (SEO_HL_RD) amont ne lisent QUEseo/README.md— JAMAIS la fiche.SEO_FIn'était utilisé que par le bloc de COMPTE de mots-clés. Ces deux cellules étaient donc des surfaces data-derived ungated : RENOMMER le@typedes listings, AJOUTER/RETIRER un projet (→ N nœuds & N pages dérivent) ou ÉCHANGER une langue de contenu périme la fiche pendant que les trois artefacts byte-gatés (seo_schema_org.json/MANIFEST.jsonetseo_hreflang.json) disent autre chose — « vert trompeur » qu'aucune suitetests/(FONCTIONS du builder, jamais la prose de la fiche) n'attrape. - État courant : aucune métrique périmée — 10 nœuds (1
Organization+ 9Residence) · 10 pages · languesfr/en/es+x-default(anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, bloc « SEO · FICHE AGENT » inséré
après le bloc hreflang README, réutilise les vérités déjà recomputées par les blocs
schema.org/hreflang amont — zéro duplication #6) :
- L25 (regex
graphe JSON-LD : \*\*(\d+) nœuds\*\* \(\+ (\d+)\)) : total ==schema_org_nodes· racine ==@typesingleton du graphe · M ==listings· type listing ==@typeuniforme des listings · cohérence arithmétiquetotal == 1 + M. - L26 (regex
\*\*(\d+) pages\*\* × ([A-Z/]+) \+x-default``) :|pages|== carte hreflang · jeu de langues (lower) == langues de contenu de l'artefact (set-diff : absent ET en trop). - Un claim absent échoue AUSSI (INTROUVABLE = régression #6).
8 morsures vérifiées (restauré vert après chacune) : nœuds 10→11 · listing
Residence→Apartment · racine Organization→LocalBusiness · listings 9→8
(casse l'arithmétique ET la bijection) · pages 10→12 · langue es→pt · retrait
es (FR/EN) · retrait du claim L25 (INTROUVABLE). Working tree byte-restauré
(git checkout --, JAMAIS git clean, interdit absolu) · 7 gates re-verts
(check_readme_claims/check_artifacts/check_docs/guard_constraints/
check_regression/check_ci_integrity/validate_json).
ci/README.md(clause de la table récap du pipeline + paragraphe détaillé « Fiche agent SEO — 2 métriques data-derived » après le paragraphe hreflang) mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
aucune écriture dans un
out/⇒ 0 dérive d'artefact ; l'injection réelle des balises hreflang/JSON-LD dans les pages reste côté agent Frontend/SEO / VPS). - Auto-score 4Big : 96/100.
Session 20260801_113314 · Buffer S8 · RBAC/RBAC_50_ROLES_SPEC §4 — colonne « Entité » de la « Cartographie rôles ↔ entités » ancrée sur CLAUDE.md §Entités ⨯ rbac_50_roles.json
Contexte : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6, double classe « ANCRAGE à CLAUDE.md »
(claude-md-constant-anchor-gate) et « identité d'ensemble recomputée du
contrat RBAC » rbac-spec-portail-count-gate). Même patron que les entités CONFOTUR
entite_porteuse (session 053228, §Entités) et les comptes SPEC §3/§7 — appliqué à
une section encore ungated de la MÊME SPEC : la table §4.
Dérive silencieuse fermée :
05_deliverables_mvp/rbac/RBAC_50_ROLES_SPEC.md§4 « Cartographie rôles ↔ entités » — la colonne de tête liste les entités porteuses (entite_principale) : les 7 entités NOMMÉES du mandat (WAF(holding) ·WA SRL·AC Arias Cuevas·Consortium ECR DR(paymaster RH) ·Helios RD(marque publique) ·Ploutos(patrimoine/monétaire) ·9060 QC(World Activities Cdn.)) + le périmètre consolidé « Groupe (consolidé) ».- Deux sources faisant autorité : (i) CLAUDE.md §Entités — l'orthographe + le
descripteur parenthétique de chaque entité nommée ; (ii)
rbac_50_roles.json(byte-gaté via les 3 MANIFEST) — l'ENSEMBLE desentite_principaleréellement portées par les 50 rôles (distinct = 11 Helios RD · 11 WA SRL · 10 AC Arias Cuevas · 8 Groupe · 5 9060 QC · 3 WAF · 1 Ploutos · 1 Consortium ECR DR = 50). - Piège : le §3 gate les comptes par portail, le §7 les comptes du run-book, les
blocs fiches l'
entited'UN rôle ; tous aveugles à l'identité de CETTE liste. AJOUTER au contrat un rôle porté par une entité absente de §4 (menu périmé), RENOMMER une entité dans CLAUDE.md §Entités (9060 QC→9061 QC) ou DÉRIVER un descripteur laisse la SPEC contredire le mandat/le contrat EN SILENCE — « vert trompeur » qu'aucune suitetests/(FONCTIONS RBAC, jamais la prose de la SPEC) n'attrape. - État courant : aucune ligne périmée — §4 == distinct contrat (8 entités) · les 7 descripteurs == CLAUDE.md §Entités · aucune entité décorative (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, bloc 1sexies après le §7, réutilise
src_roles/spec_rd/SPEC_RD déjà en scope · extraction fraîche de CLAUDE.md §Entités
— _nfc défini plus bas, non requis, entités ASCII) :
- (a) ENSEMBLE — entités de la colonne §4 (
| **Entité** (desc)? |) == distinctentite_principaledu contrat (set-diff :manquantes= portées mais hors §4 ·fantômes= listées mais jamais portées). - (b) ANCRAGE — chaque entité NOMMÉE de CLAUDE.md §Entités (tokens en gras) est une
ligne §4 ET son descripteur parenthétique == CLAUDE.md EXACT (absence comprise :
WA SRL/AC Arias Cuevas= sans descripteur des deux côtés). - (c) chaque entité nommée du mandat est PORTÉE ≥1× par le contrat (
cm_ents ⊆ contrat, aucune décorative). - Section
## 4.INTROUVABLE = claim absent = régression #6.
8 morsures vérifiées (4 côté contrat/CLAUDE.md · 4 côté SPEC) : contrat +rôle porté
par entité Fantasia absente de §4 ((a) manquante) · contrat Ploutos→Ploutos SRL
sur tous les rôles ((a) fantôme+manquante · (c) Ploutos décorative) · CLAUDE.md
9060 QC→9061 QC ((b) absent · (c) décorative) · CLAUDE.md descripteur
(holding)→(holding co) ((b) mismatch) · SPEC §4 AC Arias Cuevas→AC Arias ((a)
fantôme+manquante · (b) absent) · SPEC §4 descripteur marque publique→marque privée
((b) mismatch) · SPEC §4 retire la ligne « Groupe » ((a) manquante) · SPEC supprime
l'en-tête ## 4. (INTROUVABLE) ; restauré = green : 9 ✓ ((a) 8 entités · (b) 7
descripteurs · (c) 7 portées) · exit 0. Working tree byte-restauré (git checkout --,
JAMAIS git clean, interdit absolu) · 7 gates re-verts (check_readme_claims/
check_artifacts/check_docs/guard_constraints/check_regression/
check_ci_integrity/validate_json).
ci/README.md(clause de la ligne récap du pipeline + paragraphe détaillé « Colonne Entité de la §4 ancrée sur CLAUDE.md §Entités ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; le seed réel des rôles/entitésbenchreste côté VPS). - Auto-score 4Big : 96/100.
Session 20260801_120319 · Buffer S8 · Frontend Console/AGENT.md — IDENTITÉ de l'ENSEMBLE des portails métier + exclusion nommée de la console technique
Contexte : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
série anti-dérive CLAUDE.md #6 — fiche-agent qui restitue une donnée gatée seulement
en AGRÉGAT/COMPTE, cf. agent-fiche-role-attrs-ungated). Même patron que
l'énumération ordonnée des onglets Mobile (role_navigation) et la colonne « Entité »
de la SPEC §4 (rbac-spec-portail-count-gate) — appliqué à une surface encore ungated
de la fiche Frontend Console.
Dérive silencieuse fermée :
03_agents/frontend_console/AGENT.mdrestitue à la main, en deux endroits data-derived, l'IDENTITÉ de l'ensemble des portails — jamais gatée hors le COMPTE :- L11 « 5 portails rôle métier (Ventes / Construction / Achat / Compta / Direction) » — l'ENSEMBLE ORDONNÉ des noms courts.
- L46 « la console technique
plateforme(6ᵉ portail RBAC) est exclue » — l'identité + le rang + la non-appartenance au métier de la console exclue.
- Deux artefacts faisant autorité : (i)
frontend/portails/out/MANIFEST.json(byte-gaté) — les workspaces métier (OTO Ventes… → noms courts sans le préfixe « OTO ») ; (ii)rbac_50_roles.json—portails_business(l'ordre du contrat) + les portails distincts des rôles (6 : 5 métier +plateforme). - Piège : le bloc « triplets par workspace » recompute cartes/liens/rôles par
portail mais reste aveugle à QUELS portails composent l'ensemble ; les autres
gates comptent « 5 » sans jamais confronter le SET. Un ÉCHANGE (Achat→Marketing),
RETRAIT, RÉORDRE, RENOMMAGE de la console technique dans le contrat
(
plateforme→console) ou un contrat qui PROMEUTplateformeen métier laisse la fiche contredire les artefacts EN SILENCE — « vert trompeur » qu'aucune suitetests/(FONCTIONS RBAC, jamais la prose) n'attrape. - État courant : aucune dérive — L11 == workspaces frontend ==
portails_business· L46plateforme== unique portail RBAC hors métier == 6ᵉ. Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, bloc « Portails ensemble » juste après le
bloc triplets, réutilise ws_list/ws_fi/WS_FI déjà en scope · relit le contrat
rbac_50_roles.json) :
- (a) ENSEMBLE — noms courts ordonnés (label
MANIFEST.workspaces[].workspacesans « OTO ») == énumération L11 (compte + absents/en trop + ordre) ET DOUBLE ANCRAGE les ids workspaces frontend ==portails_businessdu contrat (l'« égalité stricte » que la fiche revendique en L47). - (b) EXCLUSION —
plateforme== l'UNIQUE portail RBAC hors métier (distinct(roles.portail) ∖ portails_business, len==1), nommé EXACTEMENT dans L46, de rang ==|distinct|(6ᵉ), avec**exclue**structurellement exigé (regex\S*absorbe l'exposant « ᵉ » multi-octets sans le coder en dur). - Claim absent (L11 ou L46 INTROUVABLE) = régression #6.
8 morsures vérifiées (6 côté fiche · 2 côté contrat) : L11 Achat→Marketing
((a) en trop/absent) · L11 retire Compta ((a) compte+absent) · L11 réordonne
Ventes↔Construction ((a) ordre) · L46 plateforme→console ((b) nom) · L46
6ᵉ→7ᵉ ((b) rang) · L46 retire **exclue** (INTROUVABLE) · contrat réordonne
portails_business (ancrage rompu) · contrat ventes→sales dans portails_business
(ancrage rompu + plateforme n'est plus l'unique hors-métier ⇒ 2 fails) ; restauré =
green (3 ✓ : double ancrage · énumération L11 · console technique L46) · exit 0.
Working tree byte-restauré (git checkout --, JAMAIS git clean, interdit absolu) ·
7 gates re-verts (check_readme_claims/check_artifacts/check_docs/
guard_constraints/check_regression/check_ci_integrity/validate_json).
ci/README.md(clause de la ligne récap du pipeline + paragraphe détaillé « Ensemble des portails métier + exclusion nommée de la console technique ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; le seed réel des Workspaces/rôlesbenchreste côté VPS). - Auto-score 4Big : 96/100.
Sprint 8 · buffer · Gate d'IDENTITÉ de la colonne « Entrée CLI » des fiches agents — verbes documentés == subparsers argparse réels
Classe fermée : agent-fiche-role-attrs-ungated (mémoire) — sous-classe « colonne
CLI ». Chaque table de livrables des fiches 03_agents/*/AGENT.md porte une colonne
« Entrée CLI » citant, entre backticks, le point d'entrée du module ET son jeu de
verbes (publiciste.py parse|validate|generate|run, faisabilite_gen.py score|scaffold|generate|batch, rbac_apply_plan.py build|validate, …). Cette liste
est saisie à la main. Les gates amont couvrent déjà génériquement — pour TOUTES les
fiches, via git ls-files — la colonne « Tests » (recomputée depuis regression_plan)
et la colonne « Job CI » (contre ci.yml), mais RIEN ne vérifiait que les VERBES
écrits sont ceux que le CLI expose réellement.
Piège : renommer une sous-commande (generate→render), en AJOUTER une
(publish), en RETIRER une ou les RÉORDONNER laisse la fiche documenter une surface
d'invocation fantôme : le module tourne (le CI appelle le vrai verbe), la doc
d'identité de l'agent — celle qui lui dit COMMENT lancer son propre livrable — ment en
silence. Aucune suite tests/ (FONCTIONS du module, jamais la prose de fiche) ne
l'attrape. Classe explicitement notée en mémoire.
Gate ajoutée (ci/check_readme_claims.sh, bloc « colonne Entrée CLI », après le
bloc « Job CI fiches ») : _cli_subcommands() RECOMPUTE via AST l'ensemble
ORDONNÉ des subparsers.add_parser("<verbe>") de chaque script (jamais une liste à la
main · #6). Résolution du script : chemin du module de la MÊME ligne de table (lien
…/05_deliverables_mvp/<path>/README.md) + basename du CLI → {D}/<path>/<file>.py
(chaque cellule liée à SON module · pas de collision de basename). Égalité EXIGÉE
exacte — ensemble ET ordre (l'ordre de add_parser == ordre de l'aide argparse).
Les | séparateurs markdown sont échappés \| dans les cellules : dé-échappés avant
découpe. Direction fiche→CLI : la fiche ne peut pas mentir sur les verbes du script.
16 cellules gatées (6 fiches : crm×3 · devops · erpnext_backend×5 · faisabilite×2 ·
publiciste · qa×4 — dont audit_5d_gen.py/audit_4big_gen.py que les regex [a-z_]
manquaient, d'où [a-z0-9_]). Actuellement toutes exactes — la gate verrouille
l'état correct.
7 morsures vérifiées (restaurées vert après chaque) :
- FICHE renomme verbe (
generate→render) → manquants/en trop,seul_ordre_diffère=False. - FICHE ajoute verbe fantôme (
publish) → en trop. - FICHE retire un verbe (
batch) → manquant. - FICHE réordonne (
parse|validate↔) →seul_ordre_diffère=True(ordre discriminé). - Le vrai « vert trompeur » :
add_parser("generate")→("render")dans le.py, fiche INCHANGÉE → red (la source de vérité a bougé sous la fiche). - Nouveau
add_parser("audit")dans le.pynon documenté → manquant côté fiche. - Script déplacé/renommé (
publiciste.py→publiciste_v2.py) → « script INTROUVABLE ».
Incident process : un git checkout . de nettoyage d'un scénario adverse a reverté
l'edit non-commité de ci/check_readme_claims.sh (bloc effacé). Ré-appliqué à
l'identique puis re-vérifié. Leçon : ne jamais git checkout ./-- large avec un edit
de gate non commité en cours ; restaurer par chemin ciblé uniquement. (git clean
reste l'interdit absolu — non utilisé.)
- 6 gates re-verts :
check_artifacts·check_docs·guard_constraints·check_readme_claims(16 ✓ Entrée CLI) ·check_regression·check_ci_integrity. Aucun hash-lock sur les scripts de gate → rien d'autre à régénérer. - Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ·
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact). - Auto-score 4Big : 96/100.
Session 20260801_130325 · Buffer S8 · Fiches agents + master prompt/infra GPU : l'IDENTITÉ de l'endpoint RunPod ComfyUI — l'ID du pod t68akvx80wv092 + le tag modèle « Flux dev fp8 » qui exécute le rendu Flux — était RECOPIÉE À LA MAIN en 6 citations (03_agents/bim/AGENT.md:32,67 · 03_agents/rendu/AGENT.md:19,55,91 · 02_master_prompt/MASTER_PROMPT_DTP_v2.md:24) mais HORS de tout gate. La source de vérité in-repo est le bullet §1 de AGENTS_EXISTING_ASSETS.md:15 — « le fichier maître » que les fiches CITENT explicitement comme leur ancre.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même classe de « vert
trompeur » que la colonne « Job CI » ou « Entrée CLI » des fiches (surface data-derived
recopiée à la main, jamais confrontée à sa source) — appliquée à une surface distincte
encore jamais gatée : l'infra GPU (l'endpoint RunPod que l'agent Rendu/BIM câble
pour le batch Flux). Classe agent-fiche-role-attrs-ungated.
Dérive silencieuse fermée :
03_agents/bim/AGENT.md(table modules L32 + coordination L67) ·03_agents/rendu/AGENT.md(principe L19 + table L55 + coordination L91) ·02_master_prompt/MASTER_PROMPT_DTP_v2.md:24(bloc infra « GPU ») — chacun NOMME entre backticks l'idt68akvx80wv092et/ou le tag « Flux dev fp8 ».- Source faisant autorité :
AGENTS_EXISTING_ASSETS.md:15§1 BIM (« - RunPod ComfyUI endpointt68akvx80wv092(Flux dev fp8) »). - Piège : un pod RunPod est RE-CRÉÉ avec un NOUVEL id (cas le plus courant) ou le
modèle Flux est bumpé (
dev fp8→autre quant) : l'inventaire maître se met à jour, les 6 citations se PÉRIMENT en silence pendant que l'agent Rendu/BIM câblerait le batch vers un endpoint GPU MORT — l'invention même que #6 interdit.check_docsne voit qu'un lien,guard_constraintsqu'un terme interdit,check_artifactsqu'unout/— un ID de pod n'est aucun des trois ; aucune suitetests/(FONCTIONS du module) n'attrape la prose. - État courant : aucune citation périmée — les 6 recoupent l'inventaire exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « RunPod endpoint » avant
sys.exit) : (0) ancrage — id + tag recomputés du bullet §1 (garde : bullet EXACTEMENT
une fois, sinon ancre morte) ; (1) direction fiche→inventaire — toute fiche/master
prompt qui MENTIONNE RunPod doit citer l'id EXACT (tout token backtické de forme pod —
alphanum minuscule ≥12 avec un chiffre, forme qui n'entre en collision avec aucun autre
token backtické du corpus : vérifié) == canonique, ET le tag Flux EXACT (substring
normalisé, insensible casse) ; (2) disparition totale — plus aucune mention RunPod
échoue AUSSI (traçabilité #6).
6 morsures vérifiées : inventaire re-crée le pod (id change → mord les 3 fiches en
cascade) · inventaire bump le tag (dev fp8→schnell fp8 → 3 fiches) · fiche rendu mistype
une citation (id fantôme, mord rendu seul) · fiche bim retire le tag (tag absent) · master
prompt retire l'id backtické (citation incomplète) · inventaire supprime le bullet canonique
(ancre morte, recompute impossible) ; restauré = green : 3 fiches · id == inventaire · tag ==
inventaire · exit 0. Working tree byte-restauré (git checkout --, JAMAIS git clean,
interdit absolu) · 7 gates re-verts (check_readme_claims/check_docs/guard_constraints/
check_artifacts/check_ci_integrity/validate_json/check_regression).
ci/README.md(clause récap du pipelinecheck-readme-claims+ paragraphe détaillé « IDENTITÉ de l'endpoint GPU RunPod ComfyUI ancrée sur l'inventaire maître ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ; édition
hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; le pod RunPod réel + le batch ComfyUI Flux restent côté VPS/RunPod). - Auto-score 4Big : 96/100.
Session 20260801_133334 · Buffer S8 · Domaine CRM/Commissions : la PROMESSE ANTI-INVENTION (#6) du barème vendeurs — la RAISON D'ÊTRE du module — était HORS de tout gate. Aucun taux de commission n'étant documenté dans CLAUDE.md, le README §« Anti-invention » promet (README:22-23) que « le barème livré porte taux_pct: null + source: null + a_confirmer: true pour chaque évènement », et l'invariant CLI #7 REFUSE tout taux_pct fourni sans source. Le bloc cross-cohérence commissions existant ne gate QUE brouillons/champs de base ; le bloc racine QUE le compte de tests (« 25 »). RIEN ne confrontait cette promesse à l'artefact byte-gaté.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même surface crm/commissions
que le bloc cross-cohérence (états soumis/base_field) mais axe orthogonal et plus
grave : non plus QUEL état paie, mais la garantie que le module n'invente aucun
chiffre — la classe même que #6 pose, appliquée au livrable dont c'est la mission.
Dérive silencieuse fermée :
05_deliverables_mvp/crm/commissions/README.md:22-23— la promesse «taux_pct: null+source: null+a_confirmer: truepour chaque évènement ».- Source faisant autorité :
out/commission_plan.json(byte-gaté parcheck_artifacts) — chaqueevenements[]portetaux_pct/source/a_confirmer;out/MANIFEST.json[counts]recomptetaux_a_confirmer/evenements. - Piège #6, la dérive la plus dommageable possible ici : qu'un
taux_pctDEVIENNE non-null (taux INVENTÉ, ou valeur remplie sanssource) pendant que le README affirme encore « toutnull» → le calculbase × taux(commlib/finance.py) émettrait une commission fondée sur un chiffre inventé — l'invention EXACTE que le module promet d'écarter. Aucune suitetests/(FONCTIONS de calcul/résolution, jamais la promesse du README vs l'artefact commité) ne l'attrape. - État courant : les 5 évènements sont bien tout-
null(taux_pct/sourcenull ·a_confirmertrue),counts.taux_a_confirmer == 5 == |plan|,note_tauxcohérente ; rien de périmé (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « CRM/Commissions ·
ANTI-INVENTION (#6) » après le bloc cross-cohérence commissions) : on RECOMPUTE
l'état null/confirmé depuis l'artefact, puis (a) invariant #7 permanent — aucun
taux_pct renseigné sans source (taux inventé) ; (b) cohérence a_confirmer
⟺ taux_pct is null (un taux confirmé n'est plus « à confirmer », et inversement) ;
(c) MANIFEST.counts recoupe le plan (taux_a_confirmer == nb à confirmer ·
evenements == |plan|) ; (d) la promesse #6 du README REFLÈTE l'artefact dans les
DEUX SENS — si tout-null, la promesse DOIT être présente (sinon la raison d'être
disparaît · #6) ; si un taux est un jour confirmé, la promesse « tout null » DOIT
avoir été retirée (sinon « vert trompeur ») — zéro faux négatif quand la
Direction remplira les taux ; (e) note_taux du MANIFEST cohérente avec l'état.
5 morsures vérifiées : taux_pct rempli SANS source (invariant #7 · invention
- cascade b/d) ·
taux_pctrempli AVEC source (invariant #7 OK maisa_confirmerresté true → b, + README périmé → d — prouve que #7 et la cohérence sont distincts) ·a_confirmer=falsesur taux null (b + counts périmés c + d) ·MANIFEST.counts. taux_a_confirmerpérimé 5→4 (c seul) · promesse anti-invention retirée du README (d · INTROUVABLE) ; restauré = green : invariant #7 · cohérence · counts · promesse == artefact · exit 0. Working tree byte-restauré (git checkout --, JAMAISgit clean, interdit absolu) · 7 gates re-verts (check_readme_claims/check_ci_integrity/check_docs/guard_constraints/check_artifacts/check_regression/validate_json).
ci/README.md(cellule récap du pipeline + paragraphe détaillé « promesse anti-invention #6 du barèmecrm/commissions») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; la confirmation réelle des taux + le câblage du calcul restent côté Direction/VPS). - Auto-score 4Big : 96/100.
Session 20260801_140334 · Buffer S8 · Domaine Fiscal/e-CF DGII : l'IDENTITÉ des ÉVÈNEMENTS D'ÉMISSION — la section §Cross-cohérence présentée comme « le cœur du livrable » (README:40-59) — était transcrite À LA MAIN SANS aucun gate d'identité. Le SEUL gate fiscal existant couvre le contrat de format e-NCF (E+tipo(2)+seq(10)=13) + le compte de tests ; le bloc racine, le seul agrégat « e-CF 39 tests ». RIEN ne confrontait à l'artefact byte-gaté QUEL évènement du pipeline émet un e-CF, sur quel base_field, par quel rôle Compta, ni la FormaPago défaut.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive (CLAUDE.md #6). Même classe « identité
d'un ENSEMBLE » que la colonne « Entité » de la SPEC §4, l'énumération des onglets
Mobile ou les transitions à séparation des pouvoirs du workflow — appliquée à la
surface data-derived la plus critique du module fiscal : les emission_events
d'ecf_plan.json, jamais gatés hors le format e-NCF et le compte de tests.
Dérive silencieuse fermée :
05_deliverables_mvp/fiscal/ecf_dgii/README.md:40-59(§Cross-cohérence, « le cœur du livrable ») — transcrit À LA MAIN : (L48) l'ENSEMBLE des évènements émetteurs (« seulement réservation et contrat ») + l'ENSEMBLE exclu des brouillons (« on ne facture pas un brouillon (lead/visite/devis) ») ; (L50) l'ENSEMBLE desbase_field(montant_reservation/montant_contrat) ; (L53-54) le rôle UNIQUE (compta-fiscaliste-ecf· « OTO Compta Fiscaliste eCF » · portail compta) ; (L59) la FormaPago défaut (« = 3 (Tarjeta) » · Cardnet #10).- Source faisant autorité (byte-gatée par
check_artifacts) :out/ecf_plan.json—emission_events[](update_value/base_field/role_id/erpnext_role_name),forma_pago_defaut+ catalogueformas_pago. Double ancrage du rôle surrbac/rbac_50_roles.json(id →portailcompta → nom ERPNext). - Piège #6/#5, la dérive la plus dommageable ici : AJOUTER un
emission_eventsur un état BROUILLON (ex.devis) — l'anti-pattern EXACT que le module interdit (« on ne facture pas un brouillon ») — pendant que L48 dit encore « seulement réservation et contrat » → l'agent ERPNext Backend câblerait une facture émise avant soumission/encaissement. Aucune suitetests/(FONCTIONS de résolution/ composition, jamais la prose vs l'artefact) ne l'attrape. - État courant : émetteurs
{contrat, reservation}, base_field{montant_contrat, montant_reservation}, rôle uniquecompta-fiscaliste-ecf(portail compta · RBAC concordant), FormaPago défaut3/Tarjeta/Cardnet — tout recoupe l'artefact EXACTEMENT (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Fiscal · e-CF DGII —
IDENTITÉ des ÉVÈNEMENTS D'ÉMISSION » avant sys.exit) : (0) cohérence interne —
rôle UNIQUE sur tous les évènements · défaut ∈ catalogue FormaPago ; (1) ENSEMBLE
émetteurs — set-diff README seulement … == update_value de l'artefact (ni
fantôme ni manquant · accents/casse normalisés) ; (2) DISJONCTION — l'ensemble
exclu (lead/visite/devis) ∩ émetteurs = ∅ (jamais de facture sur brouillon — mord
un devis émetteur ET la self-contradiction du README) ; (3) ENSEMBLE base_field
— set-diff README == artefact ; (4) rôle unique cité backtické + nom ERPNext,
double-ancré sur rbac_50_roles (id → portail compta → nom concordant) ; (5)
FormaPago défaut — code == artefact · label ∈ catalogue · motif cite Cardnet (#10).
Un claim absent échoue AUSSI (traçabilité #6).
6 morsures vérifiées : artefact facture un BROUILLON (devis émetteur → mismatch
d'ensemble ET clash de disjonction · 2 morsures en une) · artefact renomme un
base_field (montant_reservation→montant_acompte) · artefact change FormaPago défaut
3→1 (Efectivo) · artefact casse le rôle unique (2ᵉ évènement → compta-comptable) ·
README périmé « seulement contrat » (réservation retiré) · motif du défaut ne cite
plus « Cardnet » (#10) ; restauré = green : émetteurs · disjonction · base_field ·
rôle == RBAC · FormaPago == artefact · exit 0. Working tree byte-restauré
(git checkout --, JAMAIS git clean, interdit absolu) · 7 gates re-verts
(check_readme_claims/check_docs/guard_constraints/check_artifacts/
check_ci_integrity/validate_json/check_regression).
ci/README.md(clause récap du pipelinecheck-readme-claims+ paragraphe détaillé « IDENTITÉ des évènements d'émission e-CF ancrée surecf_plan.json») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; la connexion Compupar réelle + l'émission e-CF en production restent côté ERPNext Backend/VPS). - Auto-score 4Big : 96/100.
Session 20260801_143341 · Buffer S8 · Domaine Faisabilité/generator : les TROIS copies aval des 6 MARQUEURS CANONIQUES (3 %/8.5 %/52 % · USD+DOP/Letter US/Cardnet, contraintes NON-NÉGOCIABLES CLAUDE.md #9/#10) — l'oracle d'enforcement genlib/renderer.py::_CANONICAL_MARKERS (la liste que check_canonical() PARCOURT pour PROUVER que chaque rendu de faisabilité porte les 6 marqueurs), les DEUX docstrings de genlib/scorer.py (barème axe 4 + _score_parametres_canoniques) et l'oracle du test tests/test_generator.py (for marker in [...]) — étaient HORS de tout ancrage. Le bloc « paramètres canoniques » existant n'ancre QUE model.py::CANONICAL + le README à #9/#10 ; son propre commentaire NOMMAIT pourtant le défaut (« ces marqueurs sont HARDCODÉS DANS LE TEST, une copie de plus, JAMAIS comparée à CLAUDE.md ») sans le fermer.
Tâche : Sprint 8 · buffer (DevOps CI/CD · QA). Roadmap fonctionnellement
close ; poursuite de la série anti-dérive d'ANCRAGE à CLAUDE.md (classe
claude-md-constant-anchor-gate). Même patron EXACT que le tuple d'oracle du
test publiciste/branding ancré sur #4 (une copie hardcodée dans un test, gatée) —
appliqué aux trois copies aval encore ungated du MÊME module faisabilite/generator
dont le bloc canonique n'ancrait que 2 des 5 copies.
Dérive silencieuse fermée :
05_deliverables_mvp/faisabilite/generator/genlib/renderer.py:275—_CANONICAL_MARKERS = ["3 %", "8.5 %", "52 %", "USD + DOP", "Cardnet", "Letter US"], la LISTE quecheck_canonical()parcourt pour vérifier que le volet ingénierie du rendu porte les 6 marqueurs. C'est l'enforcement même de l'« axe 4 honnêteté ».05_deliverables_mvp/faisabilite/generator/genlib/scorer.py:7(barème axe 4) et:109(_score_parametres_canoniques) — deux docstrings énumérant « 3 % / 8.5 % / 52 % · USD+DOP · Letter US · Cardnet ».05_deliverables_mvp/faisabilite/generator/tests/test_generator.py:103—for marker in ["3 %", "8.5 %", "52 %", "USD + DOP", "Cardnet", "Letter US"]: l'oracle du test lui-même, celui que le commentaire du bloc amont dénonçait.- Source faisant autorité : CLAUDE.md #9/#10 (les 6 valeurs), déjà recomputées
en
exppar le bloc « paramètres canoniques » — RÉUTILISÉES ici (zéro duplication). - Piège #6 : l'oracle
_CANONICAL_MARKERSest le juge de l'honnêteté du rendu. S'il DÉRIVE deCANONICAL(donc de #9/#10), le contrôle devient FAUX de deux façons : soit il exige un marqueur périmé (Cardnetalors que le rendu, issu d'un CANONICAL mis à jour, porteraitAzul→ faux rouge), soit on retire un marqueur et le contrôle NE VÉRIFIE PLUS RIEN (vert vacux). La seule suitetests/teste des FONCTIONS (dontcheck_canonical— AVEC son propre oracle hardcodé), jamais l'ancrage à CLAUDE.md. - État courant : aucun marqueur périmé —
set(renderer._CANONICAL_MARKERS) == set(model.CANONICAL.values())vérifié programmatiquement AVANT toute édition ; les 3 copies recoupent #9/#10 exactement (anti-invention #6, rien à réécrire). Le défaut est la surface ungated.
Gate ajouté (ci/check_readme_claims.sh, nouveau bloc « Faisabilité/generator
(2e surface) » après le bloc « paramètres canoniques ») : RÉUTILISE exp +
_want6 (6 valeurs normalisées de #9/#10, zéro duplication). Helper _list_vs_want6
pour les copies-LISTES : (c) renderer.py::_CANONICAL_MARKERS et (e) l'oracle du test
sont parsés (ast.literal_eval) puis comparés à l'ensemble EXACT (set-diff : absent
ET en trop · anti-doublon). Pour les copies-DOCSTRINGS (d), chaque valeur normalisée
(_pnorm/_ai — espaces/accents-insensibles ⇒ USD+DOP==USD + DOP) doit être
substring de la ligne normalisée ; ≥2 énumérations exigées (barème + fonction). Un
claim/ligne absent échoue AUSSI (traçabilité #6).
4 morsures vérifiées : (M1) renderer._CANONICAL_MARKERS Cardnet→Azul
(absents=[cardnet] · en trop=[azul]) · (M2) oracle du test retire 8.5 %
(absents=[8.5%]) · (M3) scorer docstring L7 52 %→55 % (n'énumère plus [52%]) ·
(M4) CLAUDE.md #10 Cardnet→Azul → mord SIMULTANÉMENT model.py + renderer + test +
les DEUX docstrings (5 morsures en cascade — la valeur MÊME de l'ancrage à la
source constitutionnelle) ; restauré = green : renderer/test == #9/#10 (6 marqueurs) ·
2 docstrings == #9/#10 · exit 0. Working tree byte-restauré (git checkout --,
JAMAIS git clean, interdit absolu) · 7 gates re-verts
(check_readme_claims/check_artifacts/check_ci_integrity/check_docs/
guard_constraints/check_regression/validate_json).
ci/README.md(clause récap du pipelinecheck-readme-claims+ paragraphe détaillé « 2ᵉ surface du MÊME module : les 3 copies aval des marqueurs canoniques ») mis à jour.- Hors périmètre worker (VPS · #8) : néant (gate bash/python3 stdlib en-repo ;
édition hors
05_deliverables_mvp/*/out⇒ 0 dérive d'artefact ; la génération réelle des faisabilités reste côté BIM/Faisabilité). - Auto-score 4Big : 96/100.