[DTP-Worker] Sprint 8 · buffer · 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), 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 fichier ; (c) le README ; (d) le tuple d'oracle HARDCODÉ de tests/test_publiciste.py. Le seul contrôle existant (test_marque_luxury) assert que le RENDU contient ces marqueurs mais ils sont une copie de plus JAMAIS comparée à CLAUDE.md — même patron EXACT que les paramètres canoniques faisabilite/generator (CANONICAL↔CLAUDE.md #9/#10). Gate ajouté : les 4 tokens recomputés de la ligne #4 de CLAUDE.md (zéro duplication) ; (a) branding.py CONSTANTES == #4 (hex insensible casse, fontes exact), (b) docstring cite les 4, (c) README cite les 2 fontes, (d) oracle du test == #4 (set-diff). 6 morsures vérifiées (constante accent/display · CLAUDE.md #4 accent → mord SIMULTANÉMENT les 3 copies aval · docstring · README · oracle), restauré vert, 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -303,3 +303,58 @@ mobile∉`OTO Ventes` · exit 0. Working tree byte-restauré (`git checkout --`,
|
||||
édition **hors** `05_deliverables_mvp/*/out` ⇒ 0 dérive d'artefact ; l'attachement
|
||||
réel des rôles aux Workspaces `bench` reste 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 aligne `branding.py`, l'oracle du test reste périmé
|
||||
(rouge trompeur) ; si RIEN ne bouge, tout reste vert alors que le site public
|
||||
`vente.otov7.com` émettrait la mauvaise couleur — invention silencieuse de la
|
||||
classe même que #6 interdit, qu'aucune suite `tests/` (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 retire `Fraunces` ; restauré = green : 4 tokens sains · constantes ==
|
||||
#4 · docstring 4 tokens · README 2 fontes · oracle == #4 · 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é « Tokens design
|
||||
canoniques `publiciste/branding` ancré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 site `vente.otov7.com` reste côté VPS).
|
||||
- **Auto-score 4Big** : 96/100.
|
||||
|
||||
+27
-1
File diff suppressed because one or more lines are too long
@@ -3513,6 +3513,123 @@ if _ws_json is not None and _ws_roles is not None:
|
||||
f"MAIS workspace.json met « {rname} » dans son Has Role → "
|
||||
f"SUR-EXPOSITION console, assertion #6 VIOLÉE")
|
||||
|
||||
# ============================================================================
|
||||
# Publiciste/branding · TOKENS DESIGN CANONIQUES « ancrés sur CLAUDE.md #4 »
|
||||
# — les 4 valeurs de la marque luxury (2 couleurs + 2 typographies), jamais gatées.
|
||||
# ----------------------------------------------------------------------------
|
||||
# La contrainte NON-NÉGOCIABLE #4 de CLAUDE.md fixe l'identité visuelle : fond
|
||||
# dark `#0a0a12` + accent doré `#f0b429`, titres Fraunces + corps Cormorant
|
||||
# Garamond. C'est la SOURCE FAISANT AUTORITÉ. Ces 4 tokens sont RECOPIÉS à
|
||||
# QUATRE endroits, tous NON gatés :
|
||||
# (a) lib/branding.py CONSTANTES (COLOR_BG/COLOR_ACCENT/FONT_DISPLAY/FONT_BODY)
|
||||
# — la source unique importée par le générateur ET substituée dans le
|
||||
# gabarit ({{COLOR_BG}}…) ; ce n'est PAS un out/*.json → invisible à
|
||||
# check_artifacts ;
|
||||
# (b) le docstring de branding.py (« `#0a0a12` fond · `#f0b429` … Fraunces
|
||||
# (titres) + Cormorant Garamond ») — la prose du module ;
|
||||
# (c) README:43 (« Gabarit dark+doré (Fraunces + Cormorant Garamond) ») ;
|
||||
# (d) tests/test_publiciste.py — le tuple d'oracle HARDCODÉ
|
||||
# ("#0a0a12","#f0b429","Fraunces","Cormorant Garamond") : le SEUL contrôle
|
||||
# existant assert que le RENDU contient ces marqueurs, mais ils sont une
|
||||
# COPIE de plus, JAMAIS comparée à CLAUDE.md. Si #4 change (ex. accent
|
||||
# `#f0b429`→`#e0a420`) et qu'on aligne branding.py, l'oracle du test reste
|
||||
# périmé (rouge trompeur) ; si RIEN ne bouge, tout reste vert alors que le
|
||||
# site public émettrait la mauvaise couleur — invention silencieuse (#6) que
|
||||
# nulle suite tests/ (qui teste des FONCTIONS, pas l'ancrage à CLAUDE.md)
|
||||
# n'attrape. Même patron EXACT que les paramètres canoniques
|
||||
# faisabilite/generator (ancrage CANONICAL↔CLAUDE.md #9/#10).
|
||||
# On RECOMPUTE les 4 tokens depuis la ligne #4 de CLAUDE.md (zéro duplication) et
|
||||
# on exige : (a) branding.py CONSTANTES == #4 ; (b) docstring cite les 4 EXACTS ;
|
||||
# (c) README cite les 2 fontes ; (d) l'oracle du test == #4. Hex insensible à la
|
||||
# casse, fontes comparées à l'exact (strip). Un claim absent échoue AUSSI.
|
||||
# ============================================================================
|
||||
PUB = f"{D}/publiciste"
|
||||
try:
|
||||
cm_raw4 = open("CLAUDE.md", encoding="utf-8").read()
|
||||
l4 = re.search(r"^4\.\s+(.+)$", cm_raw4, re.M)
|
||||
if not l4:
|
||||
raise ValueError("contrainte #4 INTROUVABLE")
|
||||
hexes = [h.lower() for h in re.findall(r"`(#[0-9a-fA-F]{6})`", l4.group(1))]
|
||||
mfp = re.search(r"\(([^)]+)\)", l4.group(1))
|
||||
fonts = [f.strip() for f in mfp.group(1).split("+")] if mfp else []
|
||||
if len(hexes) != 2 or len(fonts) != 2:
|
||||
raise ValueError(f"attendu 2 couleurs + 2 fontes, lu {hexes} / {fonts}")
|
||||
b4 = {"bg": hexes[0], "accent": hexes[1], "display": fonts[0], "body": fonts[1]}
|
||||
except (OSError, ValueError) as e:
|
||||
bad(f"Branding canoniques · CLAUDE.md #4 illisible/inattendu : {e}"); b4 = None
|
||||
if b4 is not None:
|
||||
good(f"Branding canoniques · CLAUDE.md #4 sain : bg={b4['bg']} accent={b4['accent']} "
|
||||
f"display={b4['display']!r} body={b4['body']!r}")
|
||||
# (a) ANCRAGE : lib/branding.py CONSTANTES == CLAUDE.md #4 -------------------
|
||||
BR = f"{PUB}/lib/branding.py"
|
||||
try:
|
||||
br_src = open(BR, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Branding canoniques · {BR} illisible : {e}"); br_src = None
|
||||
if br_src is not None:
|
||||
const_pat = {
|
||||
"bg": r'COLOR_BG\s*=\s*"([^"]+)"',
|
||||
"accent": r'COLOR_ACCENT\s*=\s*"([^"]+)"',
|
||||
"display": r'FONT_DISPLAY\s*=\s*"([^"]+)"',
|
||||
"body": r'FONT_BODY\s*=\s*"([^"]+)"',
|
||||
}
|
||||
drift = []
|
||||
for k, pat in const_pat.items():
|
||||
m = re.search(pat, br_src)
|
||||
got = m.group(1) if m else None
|
||||
want = b4[k]
|
||||
ok = (got is not None and
|
||||
(got.lower() == want if k in ("bg", "accent") else got.strip() == want))
|
||||
if not ok:
|
||||
drift.append(f"{k}={got!r} (attendu {want!r})")
|
||||
if drift:
|
||||
bad(f"Branding canoniques · branding.py CONSTANTES DÉRIVENT de CLAUDE.md #4 : "
|
||||
f"{' · '.join(drift)} → aligner branding.py (ou CLAUDE.md)")
|
||||
else:
|
||||
good("Branding canoniques · branding.py CONSTANTES == CLAUDE.md #4 (4 tokens)")
|
||||
# (b) PROSE docstring : les 4 tokens cités AVANT les CONSTANTES -----------
|
||||
doc = br_src.split("COLOR_BG", 1)[0]
|
||||
doc_hex = {h.lower() for h in re.findall(r"`(#[0-9a-fA-F]{6})`", doc)}
|
||||
miss = [b4[k] for k in ("bg", "accent") if b4[k] not in doc_hex]
|
||||
miss += [b4[k] for k in ("display", "body") if b4[k] not in doc]
|
||||
if miss:
|
||||
bad(f"Branding canoniques · docstring branding.py — tokens ABSENTS de la "
|
||||
f"prose : {miss} (claim absent = régression · #6)")
|
||||
else:
|
||||
good("Branding canoniques · docstring branding.py cite les 4 tokens == CLAUDE.md #4")
|
||||
# (c) README : la ligne du gabarit cite les 2 fontes -----------------------
|
||||
PBR_RD = f"{PUB}/README.md"
|
||||
pbr_rd = read_norm(PBR_RD)
|
||||
if pbr_rd is not None:
|
||||
rd_miss = [b4[k] for k in ("display", "body") if b4[k] not in pbr_rd]
|
||||
if rd_miss:
|
||||
bad(f"Branding canoniques · {PBR_RD} — fontes ABSENTES de la prose : "
|
||||
f"{rd_miss} (attendu {b4['display']} + {b4['body']} == CLAUDE.md #4)")
|
||||
else:
|
||||
good(f"Branding canoniques · {PBR_RD} cite les 2 fontes == CLAUDE.md #4")
|
||||
# (d) ORACLE du test : le tuple hardcodé == les 4 tokens de CLAUDE.md #4 ----
|
||||
TBR = f"{PUB}/tests/test_publiciste.py"
|
||||
try:
|
||||
tbr_src = open(TBR, encoding="utf-8").read()
|
||||
except OSError as e:
|
||||
bad(f"Branding canoniques · {TBR} illisible : {e}"); tbr_src = None
|
||||
if tbr_src is not None:
|
||||
mt = re.search(r"for token in \(([^)]+)\)", tbr_src)
|
||||
if mt is None:
|
||||
bad(f"Branding canoniques · {TBR} — tuple d'oracle « for token in (...) » "
|
||||
f"INTROUVABLE (le contrôle #4 du test a disparu · régression)")
|
||||
else:
|
||||
toks = [t.strip().strip('"\'') for t in mt.group(1).split(",") if t.strip()]
|
||||
toks_n = {t.lower() if t.startswith("#") else t for t in toks}
|
||||
want_n = {b4["bg"], b4["accent"], b4["display"], b4["body"]}
|
||||
if toks_n == want_n:
|
||||
good(f"Branding canoniques · {TBR} oracle == CLAUDE.md #4 (4 tokens)")
|
||||
else:
|
||||
miss = sorted(want_n - toks_n); extra = sorted(toks_n - want_n)
|
||||
bad(f"Branding canoniques · {TBR} oracle DÉRIVE de CLAUDE.md #4"
|
||||
+ (f" · absents={miss}" if miss else "")
|
||||
+ (f" · en trop={extra}" if extra else "") + " → aligner le test")
|
||||
|
||||
sys.exit(1 if FAIL else 0)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
Reference in New Issue
Block a user