[DTP-Worker 20260802_000501] Sprint 8 · buffer · Publiciste : gate d'ANCRAGE des DEVISES du site public sur CLAUDE.md #10
Le bloc Publiciste/branding existant ancre les 4 tokens DESIGN (#4 : couleurs + typographies). Le MÊME module porte une 2e constante constitutionnelle NON gatée : les devises CLAUDE.md #10 (« **USD + DOP** »), recopiées à 4 endroits data-derived, tous invisibles à check_artifacts (pas un out/*.json) : (a) branding.py DEVISE_PRIMAIRE/SECONDAIRE (auto-étiquetées « #10 · USD + DOP » sans gate) ; (b) 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) ; (c) footer HTML « Prix … en USD + DOP (Cardnet) » ; (d) l'oracle hardcodé assertIn("USD 150 000") du test — une copie jamais comparée à CLAUDE.md. Aucune suite tests/ (FONCTIONS de rendu, pas l'ancre à CLAUDE.md) n'attrape la dérive. Nouveau bloc : RE-PARSE les 2 devises depuis la ligne #10 de CLAUDE.md (indépendamment de exp amont · aucun skip hérité), exige (a) constantes branding == #10 ordre inclus ; (b) ensemble des codes émis par les formateurs == #10 ; (c) footer cite les 2 devises dans l'ordre + (Cardnet) ; (d) oracle test == devise primaire. Claim absent échoue AUSSI (#6). 6 morsures vérifiées (M5 : rename #10 CLAUDE.md-only cascade sur les 4 copies = preuve ancre live), 4 gates re-verts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
# 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.
|
||||||
@@ -6131,6 +6131,124 @@ if b4 is not None:
|
|||||||
+ (f" · absents={miss}" if miss else "")
|
+ (f" · absents={miss}" if miss else "")
|
||||||
+ (f" · en trop={extra}" if extra else "") + " → aligner le test")
|
+ (f" · en trop={extra}" if extra else "") + " → aligner le test")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Publiciste/generator+branding · DEVISES CANONIQUES du site public « ancrées
|
||||||
|
# sur CLAUDE.md #10 » (USD + DOP) — les libellés monétaires RÉELLEMENT ÉMIS sur
|
||||||
|
# vente.otov7.com, jamais gatés.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Le bloc branding #4 ci-dessus ancre les 4 tokens DESIGN. Mais le MÊME module
|
||||||
|
# porte une 2ᵉ constante constitutionnelle NON gatée : les DEUX devises de
|
||||||
|
# CLAUDE.md #10 (« **USD + DOP** devises »), recopiées à QUATRE endroits data-
|
||||||
|
# derived, tous invisibles à check_artifacts (pas un out/*.json) et jamais
|
||||||
|
# confrontés à #10 :
|
||||||
|
# (a) lib/branding.py DEVISE_PRIMAIRE/DEVISE_SECONDAIRE — constantes AUTO-
|
||||||
|
# étiquetées « contrainte #10 · USD + DOP », sans aucun gate ;
|
||||||
|
# (b) lib/generator.py `_fmt_usd`/`_fmt_dop` — les DEUX formateurs qui
|
||||||
|
# IMPRIMENT le code devise sur la grille de prix du site public
|
||||||
|
# (`f"USD {value:,.0f}"` / `f"DOP {value:,.0f}"`) ; c'est la SORTIE
|
||||||
|
# FACE-PUBLIC : une devise HORS #10 ici = mandat trahi en silence (#6) ;
|
||||||
|
# (c) lib/generator.py footer (émis dans le HTML) « Prix … en USD + DOP
|
||||||
|
# (Cardnet) » — la prose du pied de page du site ;
|
||||||
|
# (d) tests/test_publiciste.py — l'oracle HARDCODÉ « assertIn("USD 150 000",
|
||||||
|
# …) » : une COPIE de plus de la devise primaire, JAMAIS comparée à #10.
|
||||||
|
# Aucune suite tests/ (qui teste des FONCTIONS de rendu, pas l'ancre à
|
||||||
|
# CLAUDE.md) n'attrape la dérive : renommer la devise #10 (USD→EUR) et aligner
|
||||||
|
# le module reste vert alors que le site émettrait la mauvaise devise ; ne RIEN
|
||||||
|
# bouger reste vert alors que branding se dit « #10 » en mentant. Même patron
|
||||||
|
# EXACT que le bloc branding/#4 et faisabilite/#9-#10. On RE-PARSE les 2 devises
|
||||||
|
# depuis la ligne #10 de CLAUDE.md (indépendamment de `exp` amont · aucun skip
|
||||||
|
# hérité) et on exige : (a) les 2 constantes == #10 (ordre inclus) ; (b)
|
||||||
|
# l'ensemble des codes émis par les formateurs == #10 ; (c) le footer cite les 2
|
||||||
|
# devises dans l'ordre ; (d) l'oracle du test == devise primaire. Un claim
|
||||||
|
# absent échoue AUSSI (traçabilité #6).
|
||||||
|
# ============================================================================
|
||||||
|
try:
|
||||||
|
cm_raw10p = open("CLAUDE.md", encoding="utf-8").read()
|
||||||
|
l10p = re.search(r"^10\.\s+(.+)$", cm_raw10p, re.M)
|
||||||
|
if not l10p:
|
||||||
|
raise ValueError("contrainte #10 INTROUVABLE")
|
||||||
|
seg10p = [s for s in re.split(r"·", l10p.group(1)) if "devise" in _ai(s)]
|
||||||
|
mdev = re.search(r"\*\*(.+?)\*\*", seg10p[0]) if seg10p else None
|
||||||
|
if not mdev:
|
||||||
|
raise ValueError("segment devises #10 (« **… devises** ») illisible")
|
||||||
|
pub_cur = re.findall(r"[A-Z]{3}", mdev.group(1))
|
||||||
|
if len(pub_cur) != 2:
|
||||||
|
raise ValueError(f"attendu 2 devises en gras, lu {pub_cur}")
|
||||||
|
except (OSError, ValueError) as e:
|
||||||
|
bad(f"Publiciste devises · CLAUDE.md #10 illisible/inattendu : {e}"); pub_cur = None
|
||||||
|
if pub_cur is not None:
|
||||||
|
good(f"Publiciste devises · CLAUDE.md #10 sain : primaire={pub_cur[0]} "
|
||||||
|
f"secondaire={pub_cur[1]}")
|
||||||
|
PUB = f"{D}/publiciste"
|
||||||
|
# (a) ANCRAGE : branding.py DEVISE_PRIMAIRE/SECONDAIRE == #10 (ordre inclus) ---
|
||||||
|
BRD = f"{PUB}/lib/branding.py"
|
||||||
|
try:
|
||||||
|
brd_src = open(BRD, encoding="utf-8").read()
|
||||||
|
except OSError as e:
|
||||||
|
bad(f"Publiciste devises · {BRD} illisible : {e}"); brd_src = None
|
||||||
|
if brd_src is not None:
|
||||||
|
dev_pat = {
|
||||||
|
"primaire": (r'DEVISE_PRIMAIRE\s*=\s*"([^"]+)"', pub_cur[0]),
|
||||||
|
"secondaire": (r'DEVISE_SECONDAIRE\s*=\s*"([^"]+)"', pub_cur[1]),
|
||||||
|
}
|
||||||
|
ddrift = []
|
||||||
|
for k, (pat, want) in dev_pat.items():
|
||||||
|
m = re.search(pat, brd_src)
|
||||||
|
got = m.group(1) if m else None
|
||||||
|
if got != want:
|
||||||
|
ddrift.append(f"{k}={got!r} (attendu {want!r})")
|
||||||
|
if ddrift:
|
||||||
|
bad(f"Publiciste devises · branding.py CONSTANTES DÉRIVENT de CLAUDE.md #10 : "
|
||||||
|
f"{' · '.join(ddrift)} → aligner branding.py (ou CLAUDE.md)")
|
||||||
|
else:
|
||||||
|
good(f"Publiciste devises · branding.py DEVISE_PRIMAIRE/SECONDAIRE == "
|
||||||
|
f"CLAUDE.md #10 ({pub_cur[0]}/{pub_cur[1]})")
|
||||||
|
# (b) SORTIE : les formateurs generator.py émettent EXACTEMENT les 2 devises #10
|
||||||
|
GEN = f"{PUB}/lib/generator.py"
|
||||||
|
try:
|
||||||
|
gen_src = open(GEN, encoding="utf-8").read()
|
||||||
|
except OSError as e:
|
||||||
|
bad(f"Publiciste devises · {GEN} illisible : {e}"); gen_src = None
|
||||||
|
if gen_src is not None:
|
||||||
|
emis = re.findall(r'return f"([A-Z]{3}) \{value:[^}]+\}"', gen_src)
|
||||||
|
if set(emis) != set(pub_cur):
|
||||||
|
miss = sorted(set(pub_cur) - set(emis)); extra = sorted(set(emis) - set(pub_cur))
|
||||||
|
bad(f"Publiciste devises · formateurs generator.py émettent {sorted(set(emis))} "
|
||||||
|
f"MAIS CLAUDE.md #10 = {sorted(set(pub_cur))}"
|
||||||
|
+ (f" · absents={miss}" if miss else "")
|
||||||
|
+ (f" · HORS #10={extra}" if extra else "")
|
||||||
|
+ " → le site public émettrait une devise non conforme (#6)")
|
||||||
|
else:
|
||||||
|
good(f"Publiciste devises · formateurs generator.py émettent {sorted(set(emis))} "
|
||||||
|
f"== CLAUDE.md #10")
|
||||||
|
# (c) FOOTER émis dans le HTML : « en <D1> + <D2> (Cardnet) » ordre inclus ---
|
||||||
|
mft = re.search(r"en ([A-Z]{3}) \+ ([A-Z]{3}) \(Cardnet\)", gen_src)
|
||||||
|
if mft is None:
|
||||||
|
bad(f"Publiciste devises · {GEN} — pied de page « en <D1> + <D2> (Cardnet) » "
|
||||||
|
f"INTROUVABLE (claim absent = régression · #6)")
|
||||||
|
elif [mft.group(1), mft.group(2)] == pub_cur:
|
||||||
|
good(f"Publiciste devises · footer generator.py cite {pub_cur[0]} + {pub_cur[1]} "
|
||||||
|
f"== CLAUDE.md #10")
|
||||||
|
else:
|
||||||
|
bad(f"Publiciste devises · footer generator.py cite {mft.group(1)} + {mft.group(2)} "
|
||||||
|
f"MAIS CLAUDE.md #10 = {pub_cur[0]} + {pub_cur[1]} → aligner le footer")
|
||||||
|
# (d) ORACLE du test : « assertIn("<D1> 150 000", self.html) » == devise primaire
|
||||||
|
TP = f"{PUB}/tests/test_publiciste.py"
|
||||||
|
try:
|
||||||
|
tp_src = open(TP, encoding="utf-8").read()
|
||||||
|
except OSError as e:
|
||||||
|
bad(f"Publiciste devises · {TP} illisible : {e}"); tp_src = None
|
||||||
|
if tp_src is not None:
|
||||||
|
mto = re.search(r'assertIn\("([A-Z]{3}) [\d ]+", self\.html\)', tp_src)
|
||||||
|
if mto is None:
|
||||||
|
bad(f"Publiciste devises · {TP} — oracle « assertIn(\"<devise> …\", self.html) » "
|
||||||
|
f"INTROUVABLE (le contrôle prix du test a disparu · régression)")
|
||||||
|
elif mto.group(1) == pub_cur[0]:
|
||||||
|
good(f"Publiciste devises · {TP} oracle prix == devise primaire #10 ({pub_cur[0]})")
|
||||||
|
else:
|
||||||
|
bad(f"Publiciste devises · {TP} oracle prix cite {mto.group(1)!r} MAIS devise "
|
||||||
|
f"primaire #10 = {pub_cur[0]!r} → aligner le test")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Fiches agents · colonne « Job CI » — chaque nom de job cité DOIT exister dans
|
# Fiches agents · colonne « Job CI » — chaque nom de job cité DOIT exister dans
|
||||||
# .gitea/workflows/ci.yml (même classe de « vert trompeur », côté câblage CI).
|
# .gitea/workflows/ci.yml (même classe de « vert trompeur », côté câblage CI).
|
||||||
|
|||||||
Reference in New Issue
Block a user