# Activity log · 2026-08-02 · Claude Code DTP Worker ## 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.