Files
oto-enterprise-os-dtp/05_activity_log/2026-08-02.md
T
Claude Code DTP Worker ab73cf9384 [DTP-Worker 20260802_034704] Sprint 8 · buffer · Faisabilité : ancrage du TEMPLATE CANONIQUE v1.0 (5 surfaces #9/#10) sur CLAUDE.md
Le document-modèle CANONIQUE que toute faisabilité doit reproduire recopiait à la
main les 6 valeurs #9/#10 à 5 surfaces structurées, aucune gatée. Nouveau bloc
« TEMPLATE FAISABILITÉ CANONIQUE v1.0 » (réutilise exp/exp_excl déjà recomputés de
CLAUDE.md · zéro dup) : intro#9, 3 lignes table #9, intro#10, 3 lignes table #10
(+ exclusion ), checklist 6 marqueurs. 5 verts sur arbre propre · 11 morsures
adversariales dont M10/M11 (mutation CLAUDE.md #9/#10 SEULE ⇒ cascade sur 3 régions =
anchor-is-live). 6 gates exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 03:47:04 +00:00

27 KiB
Raw Blame History

Activity log · 2026-08-02 · Claude Code DTP Worker

Sprint 8 · buffer · Faisabilité — ancrage du TEMPLATE CANONIQUE v1.0 (5 surfaces #9/#10) sur CLAUDE.md

Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le pattern « constante canonique CLAUDE.md recopiée à la main dans une surface data-derived » (re-dérive la valeur depuis la contrainte, jamais la cohérence interne) couvre déjà le générateur (model.py/README/renderer/scorer/ oracle-test), les fiches ERPNext/CRM (« Fiches #10 »), le module fiscal (moneda), la marque SEO §Entités, la persona Chat OTOIA, les tokens branding #4, etc. Un fichier restait NON gaté (grep : absent de tous les ci/*.sh) et c'est paradoxalement le plus normatif : 05_deliverables_mvp/faisabilite/ TEMPLATE_FAISABILITE_CANONIQUE_v1.0.md, le DOCUMENT-MODÈLE « CANONIQUE » (Livrable Sprint 1) que toute faisabilité en production DOIT reproduire, et que le générateur 4 volets + le Publiciste + la régénération batch S4 sont censés suivre.

Ce template recopie à la main les 6 valeurs de CLAUDE.md #9/#10 à CINQ surfaces structurées, toutes se réclamant de « contrainte #9 »/« #10 », aucune gatée :

  1. l.28 intro §0 « frais 3 % · marketing 8.5 % · point d'équilibre 52 % » (3 pourcentages #9, associés par mot-clé) ;
  2. l.144-146 §5.2 — 3 lignes de table « N % (fixe · contrainte #9) » ;
  3. l.29 intro §0 « USD + DOP · format Letter US · paiements Cardnet » ;
  4. l.149-151 §5.2 — 3 lignes « valeur (… contrainte #10) » (la ligne paiement citant l'exclusion « pas-X ») ;
  5. l.196 §7 checklist « 3 % / 8.5 % / 52 % · USD+DOP · Letter US · Cardnet présents » (énumération des 6 marqueurs).

Le « vert trompeur » — si CLAUDE.md #9 passe 52 %→55 % (ou #10 renomme le prestataire) et que les modules s'alignent, ce TEMPLATE — la RÉFÉRENCE de toutes les faisabilités — reste PÉRIMÉ en silence, imposant à chaque projet un paramètre qui CONTREDIT le mandat pendant qu'il se réclame « contrainte #9/#10 ». Ni check_artifacts (ce n'est pas un out/*.json) ni check_docs/guard_constraints ni aucune suite tests/ (FONCTIONS, jamais l'ancre à CLAUDE.md) ne mordent.

Gate ajouté (ci/check_readme_claims.sh, bloc « TEMPLATE FAISABILITÉ CANONIQUE v1.0 · PARAMÈTRES #9/#10 », juste après « Fiches #10 ») — RÉUTILISE exp (les 6 valeurs déjà recomputées des lignes #9/#10 de CLAUDE.md · zéro re-dérivation) + exp_excl (prestataire exclu du (pas X)). Exige que chacune des 5 surfaces == CLAUDE.md : (1) 3 pcts associés par mot-clé ; (2) SET des 3 lignes « (fixe · contrainte #9) » == {3 %,8.5 %,52 %} (mord aussi ligne ajoutée/retirée) ; (3)/(4) chaque valeur #10 présente en gras dans sa région ancrée + l'exclusion ; (5) énumération des 6 marqueurs == ensemble EXACT (set-diff absent ET en trop). Une surface INTROUVABLE échoue AUSSI (recensement évaporé = régression #6).

Vérif — 5 checks verts sur l'arbre propre. 11 morsures adversariales : M1 intro#9 52→55 · M2 ligne#9 8.5→9 · M3 intro#10 Cardnet→Azul · M4 ligne#10 Letter US→A4 · M5 exclusion Stripe→PayPal · M6 checklist perd Cardnet · M7 checklist 52→60 · M8 intro#9 supprimée (INTROUVABLE) · M9 checklist « présents »→ « présent » (INTROUVABLE) · M10 CLAUDE.md #9 52→55 SEUL ⇒ cascade sur 3 régions (1)(2)(5) · M11 CLAUDE.md #10 Cardnet→Azul SEUL ⇒ cascade sur 3 régions (3)(4)(5) — les deux preuves que l'AUTORITÉ est CLAUDE.md, pas la cohérence interne du template. Un piège corrigé en route : le groupe capturé est le NOMBRE nu (sans %), donc _pct() (qui exige un %) renvoyait Nonefloat() direct. Gates re-verts : check_artifacts · check_docs · guard_constraints · check_ci_integrity · check_regression · check_readme_claims tous exit 0.


Sprint 8 · buffer · Fiscal e-CF — ancrage du CONTRAT DE FORMAT e-NCF dans le COMPOSEUR ncf.py (CODE) + son ORACLE de test

Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le bloc « Fiscal · e-CF DGII · CONTRAT DE FORMAT » existant ancre le format de l'identifiant fiscal DGII (E + tipoeCF(2) + secuencia(10) = 13, Ley 32-23) sur le SEUL bloc e_ncf byte-gaté de out/ecf_plan.json (dérivé de ecf_spec.json) et couvre 4 surfaces de PROSE : README ×3 + fiche erpnext_backend. Mais le composeur d'exécution lui-mêmeecflib/ncf.py, le code Frappe qui VALIDE et COMPOSE réellement le numéro e-NCF côté VPS/Compupar — porte TROIS copies INDÉPENDANTES du format, AUCUNE dérivée de l'artefact, + un oracle de test qui les bénit, tous NON gatés :

  1. ENCF_RE = re.compile(r"^E([0-9]{2})([0-9]{10})$") (l.26) — la regex de VALIDATION (cœur de is_valid_encf/parse_encf) ;
  2. FORMULE = "E + tipoeCF(2) + secuencia(10)" (l.27) — la chaîne publiée par compose_encf (renvoyée au caller VPS) ;
  3. docstring l.8 « "E" + tipoeCF (2 chiffres) + secuencia (10 chiffres) → 13 caracteres » + commentaire l.25 « Ley 32-23 : E + tipo(2) + secuencia(10) = 13 » (deux proses-en-code) ;
  4. tests/test_ecf_dgii.py:246 — oracle hardcodé assertEqual(…["formule"], "E + tipoeCF(2) + secuencia(10)").

Le « vert trompeur » — un rango DGII à 12 chiffres (évènement ops réel, cité dans le commentaire du gate existant) régénère ecf_spec→ecf_plan, donc les 4 proses gatées SUIVENT. Mais ENCF_RE reste {2}{10} : compose_encf zfill à 10 et is_valid_encf REJETTE un e-NCF désormais valide → bug d'exécution du composeur fiscal, pendant que la suite tests/ reste VERTE (son oracle == FORMULE périmée, self-consistante). Ni check_artifacts (ncf.py n'est pas un out/*.json) ni tests/ (teste les FONCTIONS, pas leur ANCRAGE à l'artefact) ne mordent. Même patron que l'oracle/formateur du Publiciste déjà gaté.

Gate ajouté (ci/check_readme_claims.sh, extension du bloc e-NCF · surfaces 5→9) — RÉUTILISE prefix/tipo_len/seq_len/total déjà dérivés du bloc e_ncf byte-gaté + le helper ecf_check (zéro re-dérivation du format #6). Ancre les 5 surfaces CODE/test au SEUL artefact : (5) ENCF_RE, (6) FORMULE, (7) docstring, (8) commentaire, (9) oracle de test. Une surface absente échoue AUSSI (INTROUVABLE · anti-évaporation #6).

Vérif — 5 checks verts sur l'arbre propre. 7 morsures adversariales : M1 ENCF_RE {10}→{12} · M2 FORMULE secuencia(10)→(12) · M3 docstring 10→12 chiffres · M4 commentaire secuencia(10)→(12) · M5 oracle test (10)→(12) · M6 SILENT-GREEN — l'ARTEFACT passe à seq=12 pendant que ncf.py reste à 10 ⇒ les 5 surfaces mordent TOUTES (preuve que l'autorité est l'artefact byte-gaté, pas la cohérence interne du composeur) · M7 FORMULE renommée ⇒ INTROUVABLE. Les 39 tests unittest e-CF restent verts (composeur inchangé). Gates re-verts : check_artifacts · check_ci_integrity · check_docs · check_readme_claims · check_regression · guard_constraints tous exit 0.


Sprint 8 · buffer · CRM Commissions — gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (7ᵉ instance du ledger)

Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles CI depuis le registre-source # N · de _validate, jamais une liste à la main) a déjà couvert Chat OTOIA, SEO, Fiscal e-CF, Legal CONFOTUR, CRM Dossier Vente et QA Audit 5D. Le 7ᵉ générateur — dernier candidat restant recensé au ledger (crm/commissions ·10) — était NON gaté : crm/commissions/commissions_gen.py (barème de commissions vendeurs · roadmap l.51).

Le compte « 10 invariants » y est transcrit à la main à CINQ endroits, aucun gaté, tous invisibles à check_artifacts (le .py source n'est pas un out/*.json) :

  1. message de succès CLI validate « … schéma + 10 invariants verts. » (l.209) ;
  2. aide du sous-parseur validate « schéma + 10 invariants » (l.221) ;
  3. README bloc de commande « schéma + 10 invariants » (l.60) ;
  4. README titre de section « ## Les 10 invariants » (l.64) ;
  5. README prose de score « 10 invariants de cross-cohérence » (l.92).

Ajouter un contrôle # 11 · à _validate sans toucher les 5 chaînes ⇒ le README ET les self-reports du .py mentent en silence pendant que la CI applique 11 contrôles. Aucune suite tests/ (qui teste des FONCTIONS de validation, pas la prose) n'attrape ce « vert trompeur ».

Particularité du registre Commissions (≈ Dossier Vente) : le ledger est PROPRE 1..10 — le schéma est compté À PART (phrasé « schéma + N invariants »), donc pas de schéma-#1 non marqué et pas de split a/b ; on garde la détection de doublon nu comme filet. Le module porte AUSSI un SECOND nombre — « 25 tests » (la suite unittest, déjà gatée par ailleurs) présent dans le README : l'ancre sur le mot « invariants » l'exclut nativement (« 25 tests » ≠ « invariants ») ⇒ aucun faux positif. Comme Dossier Vente, ce README A un titre « ## Les N invariants » ET une énumération numérotée du ledger → on gate (a)(b)(c)(d)(e).

Gate ajouté (ci/check_readme_claims.sh, bloc « CRM · Commissions · IDENTITÉ du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source (# N · de _validate) et exige :

  • (a) ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
  • (b) toute mention « N invariants » du .py == |ledger| ;
  • (c) l'annonce du titre README « ## Les N invariants » == |ledger| ;
  • (d) toute mention « N invariants » du README == |ledger| ;
  • (e) l'ÉNUMÉRATION numérotée de la section (« 1. … 2. … N. ») contiguë 1..N. Une mention ou le registre absent échoue AUSSI (traçabilité #6 · anti-évaporation).

Vérif — 5 checks verts sur l'arbre propre (ledger 1..10 = 10 marqueurs # N ·, schéma compté à part). 7 morsures adversariales : M1 self-report .py 10→11 (b) · M2 titre README 10→11 (c+d) · M3 prose README 10→11 (d) · M4 ajout marqueur # 11 · ⇒ ledger→11 pendant que la prose reste 10 (mord .py ET README titre/mention ET l'énumération, preuve que l'autorité est le CODE) · M5 suppression # 7 · ⇒ gap non-contigu (a) · M6 doublon nu # 5 · ⇒ détection de vrai doublon (a) · M7 énumération README 7.→8. ⇒ deux 8/pas de 7 (e). Le distinct « 25 tests » reste INVISIBLE au gate (ancre « invariants »). Gates re-verts : check_artifacts · check_docs · guard_constraints · check_ci_integrity · check_regression · check_readme_claims tous exit 0. Série ledger close : les 7 générateurs portant le nombre magique d'invariants sont désormais tous gatés.


Sprint 8 · buffer · QA Audit 5D — gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (6ᵉ instance du ledger)

Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles CI depuis le registre-source # N · de _validate, jamais une liste à la main) a déjà couvert Chat OTOIA, SEO, Fiscal e-CF, Legal CONFOTUR et CRM Dossier Vente. Le 6ᵉ générateur portant le même nombre magique restait NON gaté : qa/audit_5d/audit_5d_gen.py (candidat restant recensé : crm/commissions ·10, ledger propre non gaté).

Le compte « 15 invariants » y est transcrit à la main à SIX endroits, aucun gaté, tous invisibles à check_artifacts (le .py source n'est pas un out/*.json) :

  1. docstring sous-commande module (l.19 « valide schéma + 15 invariants ») ;
  2. docstring _validate (l.80 « 15 invariants de cohérence audit/contrôles ») ;
  3. message de succès CLI validate « … 15 invariants — verdict … » (l.218) ;
  4. aide du sous-parseur validate « valide schéma + 15 invariants » (l.229) ;
  5. README bloc de commande « schéma + 15 invariants » (l.66) ;
  6. README prose d'architecture « CLI build/validate + 15 invariants » (l.81).

Ajouter un contrôle # 16 · à _validate sans toucher les 6 chaînes ⇒ le README ET les self-reports du .py mentent en silence pendant que la CI applique 16 contrôles. Aucune suite tests/ (qui teste des FONCTIONS de validation, pas la prose) n'attrape ce « vert trompeur ».

Particularité du registre Audit 5D (≈ Dossier Vente, ≠ Fiscal) : le ledger est PROPRE 1..15 — le schéma est compté À PART (phrasé « schéma + N invariants »), donc pas de schéma-#1 non marqué et pas de split a/b ; on garde la détection de doublon nu comme filet. MAIS le module porte un SECOND nombre distinct — « 17 contrôles » (le compte de fonctions-contrôle de qalib/controls.py, déjà gaté par ailleurs), présent dans le .py ET le README : un \d+ invariants ancré sur le mot « invariants » l'exclut nativement (« 17 contrôles » ≠ mot « invariants ») ⇒ aucun faux positif sur l'arbre propre. Contrairement à Dossier Vente/Fiscal, ce README n'a NI titre « ## Les N invariants » NI énumération numérotée du ledger → on ne gate que (a)(b)(d).

Gate ajouté (ci/check_readme_claims.sh, bloc « QA · Audit 5D · IDENTITÉ du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source (# N · de _validate) et exige :

  • (a) ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
  • (b) toute mention « N invariants » du .py == |ledger| ;
  • (d) toute mention « N invariants » du README == |ledger|. Une mention ou le registre absent échoue AUSSI (traçabilité #6 · anti-évaporation).

Vérif — 3 checks verts sur l'arbre propre (ledger 1..15 = 15 marqueurs # N ·, schéma compté à part). 6 morsures adversariales : M1 self-report .py 15→16 (b) · M2 mention README 15→16 (d) · M3 ajout marqueur # 16 · ⇒ ledger→16 pendant que la prose reste 15 (mord .py ET README, preuve que l'autorité est le CODE) · M4 suppression # 7 · ⇒ gap non-contigu (a) · M5 doublon nu # 5 · ⇒ détection de vrai doublon (a) · M6 no-false-positive : le distinct « 17 contrôles » reste INVISIBLE au gate (l'ancre « invariants » l'exclut, arbre propre VERT). Gates re-verts : check_artifacts · check_docs · guard_constraints · check_ci_integrity · check_regression · check_readme_claims tous exit 0.


Sprint 8 · buffer · CRM Dossier Vente — gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate (5ᵉ instance du ledger)

Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles CI depuis le registre-source # N · de _validate, jamais une liste à la main) a déjà couvert Chat OTOIA, SEO, Fiscal e-CF et Legal CONFOTUR. Le 5ᵉ générateur portant le même nombre magique restait NON gaté : crm/dossier_vente/doctype_dossier_vente_gen.py (candidats restants recensés : qa/audit_5d ·15, crm/commissions ·10, tous ledgers propres non gatés).

Le compte « 12 invariants » y est transcrit à la main à SEPT endroits, aucun gaté, tous invisibles à check_artifacts (le .py source n'est pas un out/*.json) :

  1. docstring module (l.14 « schéma + 12 invariants de ») ;
  2. docstring _validate (l.77 « 12 invariants de cross-cohérence ») ;
  3. message de succès CLI validate « … schéma + 12 invariants verts. » (l.218) ;
  4. aide du sous-parseur validate (l.230) ;
  5. README bloc de commande « schéma + 12 invariants » (l.56) ;
  6. README titre de section « ## Les 12 invariants » (l.60) ;
  7. README prose de score « 12 invariants de cross-cohérence » (l.83).

Ajouter un contrôle # 13 · à _validate sans toucher les 7 chaînes ⇒ le README ET les self-reports du .py mentent en silence pendant que la CI applique 13 contrôles. Aucune suite tests/ (qui teste des FONCTIONS de validation, pas la prose) n'attrape ce « vert trompeur ».

Particularité du registre Dossier Vente (≈ CONFOTUR, ≠ Fiscal) : le ledger est PROPRE 1..12 — le schéma est compté À PART (phrasé « schéma + N invariants »), donc pas de schéma-#1 non marqué et pas de split a/b. On garde néanmoins la détection de doublon nu comme filet. Le README porte AUSSI une énumération numérotée sous le titre de section (item 1 = conformité au schéma, puis les contrôles), gatée en contiguïté 1..N.

Gate ajouté (ci/check_readme_claims.sh, bloc « CRM · Dossier Vente · IDENTITÉ du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source (# N · de _validate) et exige :

  • (a) ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
  • (b) toute mention « N invariants » du .py == |ledger| ;
  • (c) l'annonce du titre README « ## Les N invariants » == |ledger| ;
  • (d) toute mention « N invariants » du README == |ledger| ;
  • (e) l'ÉNUMÉRATION numérotée de la section (« 1. … 2. … N. ») contiguë 1..N. Une mention ou le registre absent échoue AUSSI (traçabilité #6).

Vérif — 5 checks verts sur l'arbre propre (ledger 1..12 = 12 marqueurs # N ·, schéma compté à part). 7 morsures adversariales : M1 self-report .py 12→13 (b) · M2 titre README 12→13 (c+d) · M3 prose README 12→13 (d) · M4 ajout marqueur # 13 · ⇒ ledger→13 pendant que la prose reste 12 (mord .py ET README ET l'énumération, preuve que l'autorité est le CODE) · M5 suppression # 7 · ⇒ gap non-contigu (a) · M6 doublon nu # 5 · ⇒ détection de vrai doublon (a) · M7 énumération README 7.→8. ⇒ deux 8/pas de 7 (e). Gates re-verts : check_artifacts · check_docs · guard_constraints · check_ci_integrity · check_regression · check_readme_claims tous exit 0.


Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le pattern « ledger d'invariants » (recompute le nombre de contrôles CI depuis le registre-source # N · de _validate, jamais une liste à la main) a déjà couvert Chat OTOIA, SEO et Fiscal e-CF. Le 4ᵉ générateur portant le même nombre magique restait NON gaté : legal/confotur/confotur_application_gen.py.

Le compte « 14 invariants » y est transcrit à la main à SIX endroits, aucun gaté, tous invisibles à check_artifacts (le .py source n'est pas un out/*.json) :

  1. docstring module (l.24 « schéma + 14 invariants de ») ;
  2. docstring _validate (l.87 « 14 invariants de cross-coherence ») ;
  3. message de succès CLI validate « … schéma + 14 invariants verts. » (l.283) ;
  4. aide du sous-parseur validate (l.295) ;
  5. README bloc de commande « schéma + 14 invariants » (l.56) ;
  6. README commentaire d'arborescence « CLI build/validate · 14 invariants » (l.82).

Ajouter un contrôle # 15 · à _validate sans toucher les 6 chaînes ⇒ le README ET les self-reports du .py mentent en silence pendant que la CI applique 15 contrôles. Aucune suite tests/ (qui teste des FONCTIONS de validation, pas la prose) n'attrape ce « vert trompeur ».

Particularité du registre CONFOTUR (≠ Fiscal) : le ledger est PROPRE 1..14 (pas de schéma-#1 non marqué, pas de split a/b). MAIS le README porte AUSSI un compte de SOUS-ENSEMBLE éditorial — « 2 invariants refusent tout champ monétaire » (l.92) — à NE PAS confondre avec le total. Un \d+ invariants naïf mordrait donc l'arbre PROPRE (il verrait {2, 14}). Le gate exclut le sous-ensemble par lookahead négatif (\d+)\s+invariants(?!\s+refusent), appliqué au .py comme au README.

Gate ajouté (ci/check_readme_claims.sh, bloc « Legal · CONFOTUR · IDENTITÉ du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source (# N · de _validate) et exige :

  • (a) ledger CONTIGU 1..N (lacune OU doublon nu = vrai défaut de registre) ;
  • (b) toute mention-total « N invariants » du .py (hors « refusent ») == |ledger| ;
  • (c) toute mention-total du README (hors « refusent ») == |ledger|. Une mention ou le registre absent échoue AUSSI (disparition d'un chiffre = régression de traçabilité #6).

Vérif — 3 checks verts sur l'arbre propre (ledger 1..14 = 14 marqueurs # N ·). 6 morsures adversariales : M1 self-report .py 14→15 (b) · M2 README commande 14→15 (c) · M3 README arborescence 14→13 (c) · M4 ajout marqueur # 15 · ⇒ ledger→15 pendant que la prose reste 14 (mord .py ET README, preuve que l'autorité est le CODE) · M5 suppression # 7 · ⇒ gap non-contigu (a) · M6 doublon nu # 5 · ⇒ détection de vrai doublon (a). M7 no-false-positive : sous-ensemble « 2 → 3 invariants refusent » reste VERT (le lookahead exclut bien le sous-ensemble). Gates re-verts : check_artifacts · check_docs · guard_constraints · check_ci_integrity · check_regression · check_readme_claims tous exit 0.


Sprint 8 · buffer · Publiciste — gate d'ANCRAGE des DEVISES du site public sur CLAUDE.md #10

Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le bloc Publiciste/branding existant ancre les 4 tokens DESIGN de CLAUDE.md #4 (2 couleurs + 2 typographies) sur leurs 4 copies. Mais le MÊME module publiciste porte une 2ᵉ constante constitutionnelle — les devises CLAUDE.md #10 (« USD + DOP ») — recopiée à 4 endroits data-derived, aucun gaté, tous invisibles à check_artifacts (pas un out/*.json) :

  1. lib/branding.py DEVISE_PRIMAIRE/DEVISE_SECONDAIRE — constantes auto- étiquetées « contrainte #10 · USD + DOP », sans gate ;
  2. lib/generator.py _fmt_usd/_fmt_dop — les formateurs qui IMPRIMENT le code devise sur la grille de prix du site public vente.otov7.com (sortie face-public : une devise hors #10 = mandat trahi en silence) ;
  3. lib/generator.py footer HTML « Prix … en USD + DOP (Cardnet) » ;
  4. tests/test_publiciste.py — l'oracle hardcodé assertIn("USD 150 000", …), une copie de plus jamais comparée à CLAUDE.md.

Aucune suite tests/ (qui teste des fonctions de rendu, pas l'ancre à CLAUDE.md) n'attrape la dérive : renommer la devise #10 et aligner le module reste vert alors que le site émettrait la mauvaise devise ; ne rien bouger reste vert alors que branding.py se dit « #10 » en mentant.

Gate ajouté (ci/check_readme_claims.sh, bloc « Publiciste/generator+branding · DEVISES CANONIQUES ») — RE-PARSE les 2 devises depuis la ligne #10 de CLAUDE.md (indépendamment de exp amont · aucun skip hérité) puis exige :

  • (a) branding.py DEVISE_PRIMAIRE/SECONDAIRE == #10 (ordre inclus) ;
  • (b) l'ensemble des codes émis par les formateurs generator.py == #10 ;
  • (c) le footer HTML cite les 2 devises dans l'ordre + (Cardnet) ;
  • (d) l'oracle du test == devise primaire #10. Un claim absent échoue AUSSI (traçabilité #6 · anti-évaporation).

Vérif — 5 checks verts sur l'arbre propre. 6 morsures adversariales : M1 branding primaire USD→EUR · M2 formateur _fmt_usd→EUR (hors #10) · M3 footer USD+DOP→USD+EUR · M4 oracle test → EUR · M5 CLAUDE.md #10 USD→EUR = cascade sur les 4 copies simultanément (preuve que l'ancre est CLAUDE.md, pas la cohérence interne) · M6 footer supprimé → INTROUVABLE. Gates re-verts : check_readme_claims · check_docs · guard_constraints · check_ci_integrity tous exit 0.


Sprint 8 · buffer · Fiscal e-CF DGII : gate d'IDENTITÉ du COMPTE D'INVARIANTS de _validate

Contexte — Série anti-dérive Sprint 8 (CLAUDE.md #6 « zéro invention de chiffres »). Le pattern « ledger d'invariants » a déjà été appliqué à Chat OTOIA et SEO (recompute le nombre d'invariants CI depuis le registre-source # N · de _validate, jamais une liste à la main). Le 3ᵉ générateur portant le même nombre magique restait NON couvert : fiscal/ecf_dgii/ecf_dgii_gen.py.

Le compte « 12 invariants » y est transcrit à la main à SEPT endroits, aucun gaté, tous invisibles à check_artifacts (le .py source n'est pas un out/*.json) :

  1. ecf_dgii_gen.py docstring module (l.24) ;
  2. docstring _validate (l.110) ;
  3. message de succès CLI validate « … schéma + 12 invariants verts. » (l.277) ;
  4. aide du sous-parseur validate (l.289) ;
  5. README bloc de commande « schéma + 12 invariants » (l.65) ;
  6. README titre de section « ## Les 12 invariants » (l.69) ;
  7. README prose de score « 12 invariants de cross-cohérence » (l.105).

Ajouter un contrôle # 13 · à _validate sans toucher les 7 chaînes ⇒ le README ET les self-reports du .py mentent en silence pendant que la CI applique 13 contrôles. Aucune suite tests/ (qui teste des FONCTIONS de validation, pas la prose) n'attrape ce « vert trompeur ».

Particularité du registre fiscal (≠ SEO/Chat OTOIA au ledger propre 1..N) : l'invariant 1 est la validation de SCHÉMA (validate(bundle, schema), non marquée # 1 ·), les marqueurs couvrent 2..N, et l'invariant 12 est splitté en # 12a · (unicité évènements) / # 12b · (comptes du manifeste) — deux volets d'un même invariant. Le gate reconstruit donc le ledger = {1:schéma} marqueurs-top-level, en collapsant 12a/12b, tout en mordant un vrai doublon d'un numéro NON suffixé.

Gate ajouté (ci/check_readme_claims.sh, bloc « Fiscal · e-CF DGII · IDENTITÉ du COMPTE D'INVARIANTS ») — RECOMPUTE le ledger depuis le SEUL registre-source (# N · de _validate) et exige :

  • (a) ledger CONTIGU 1..N (schéma #1 présent + marqueurs 2..N sans lacune ; doublon nu = défaut mordu ; 12a/12b comptés 1×) ;
  • (b) toute mention « N invariants » du .py == |ledger| ;
  • (c) l'annonce du titre README == |ledger| ;
  • (d) toute mention « N invariants » du README == |ledger| ;
  • (e) l'ÉNUMÉRATION numérotée du README (« 1. … 2. … N. ») elle-même contiguë 1..N (item ni sauté ni dupliqué).

Vérif — 5 checks verts sur l'arbre propre (ledger 1..12 = schéma #1 + 11 marqueurs). 8 morsures adversariales : M1 self-report .py 12→13 (b) · M2 titre README 12→13 (c+d) · M3 mention README 12→13 (d) · M4 ajout marqueur # 13 · ⇒ ledger→13 pendant que la prose reste 12 (b+c+d, preuve que l'autorité est le CODE, pas la prose) · M5 suppression # 7 · ⇒ gap non-contigu (a) · M6 doublon nu # 5 · ⇒ détection de vrai doublon · M7 énumération README 7.→8. ⇒ deux 8/pas de 7 (e) · M8 suppression validate(bundle, schema) ⇒ invariant 1 introuvable. Gates re-verts : check_artifacts · check_docs · guard_constraints · check_ci_integrity · check_readme_claims · check_regression tous exit 0.