[DTP-Worker] Sprint 8 · buffer · SEO/schema.org : IDENTITÉ du nœud racine Organization ANCRÉE sur CLAUDE.md §Entités — le nom de marque Helios RD (marque publique · « **Helios RD** (marque publique) sous WAG ») et son url == base_url, portés par le nœud racine du graphe JSON-LD seo/out/seo_schema_org.json, étaient byte-gatés pour la REPRODUCTIBILITÉ (check_artifacts prouve la reconstruction 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. Piège #6 : renommer la marque dans seo_spec.json[schema_org.organization.name] (ou dans CLAUDE.md §Entités) puis régénérer laisse le JSON-LD PUBLIC de vente.otov7.com émettre un nom qui CONTREDIT le mandat pendant que la byte-gate reste VERTE (l'artefact se reconstruit fidèlement… depuis un spec dérivé) — « vert trompeur » de la classe des tokens branding ancrés sur #4. Aucune suite tests/ (FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape.

Gate ajouté (bloc « SEO org » après la composition schema.org) : marque re-dérivée de la parenthèse « (marque publique) » de CLAUDE.md §Entités (1× exactement · zéro duplication), puis (a) `Organization.name` == marque CLAUDE.md ; (b) `Organization.url` == `MANIFEST.base_url` & `@id` sous base_url ; (c) chaque listing rattaché à CETTE Organization (`brand.@id` == `org.@id`) sous base_url ; (d) `addressCountry` UNIFORME == `country_code` du spec (ISO 2 lettres, géo-ciblage à l'identique) ; (e) le spec DÉCLARE l'ancrage (`organization.name` == marque + `source` cite CLAUDE.md). Racine `Organization` exigée singleton en pré-requis.

6 morsures vérifiées (restauré vert après chacune) : artefact `name`→Helios DR (a) · CLAUDE.md §Entités rename Helios RD→Helios Dominicana → mord SIMULTANÉMENT artefact (a) ET spec (e), l'ancre est vive · artefact `url` divergent (b) · un `addressCountry` DO→US (d) · spec `source` supprime l'ancre CLAUDE.md (e) · un listing `brand.@id`→#phantom (c). Restauré = green, exit 0. Working tree byte-restauré (`git checkout --`, JAMAIS `git clean`). 7 gates re-verts (check_readme_claims/check_ci_integrity/check_docs/guard_constraints/check_artifacts/check_regression/validate_json). ci/README.md (clause table récap + paragraphe détaillé) mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-08-01 05:11:46 +00:00
parent 8593961683
commit 3e7bc33fb7
3 changed files with 194 additions and 1 deletions
+65
View File
@@ -546,3 +546,68 @@ chaque token en gras `**<valeur>**` == #10 (casse tolérée) + l'exclusion pour
3. CLAUDE.md #10 `(pas Stripe)``(pas PayPal)` → mord l'exclusion erpnext ✓ 3. CLAUDE.md #10 `(pas Stripe)``(pas PayPal)` → mord l'exclusion erpnext ✓
4. fiche crm `Letter US``Letter USA` → mord CRM (format) ✓ 4. fiche crm `Letter US``Letter USA` → mord CRM (format) ✓
5. fiche erpnext supprime l'ancre `CLAUDE.md #10` → mord INTROUVABLE ✓ 5. 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 racine
`Organization` : `name: "Helios RD"` + `url: "https://vente.otov7.com"` +
`@id: ".../#organization"` ; chaque nœud `Residence` (listing) porte
`brand.@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 bullet `source`
qui 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 + le
`country_code` du 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 que
`check_artifacts` reste **VERT** — « vert trompeur ». Aucune suite `tests/`
(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) :
1. **artefact** `Organization.name``Helios DR` → mord (a) anchor ✓
2. **CLAUDE.md §Entités** rename `Helios RD``Helios Dominicana` → mord
**SIMULTANÉMENT** (a) artefact ET (e) spec — l'ancre est vive ✓
3. **artefact** `Organization.url` divergent → mord (b) url/@id
4. **artefact** un `addressCountry` `DO``US` → mord (d) uniformité géo ✓
5. **spec** `organization.source` supprime l'ancre CLAUDE.md → mord (e) déclaration ✓
6. **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 racine `Organization` 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 ; l'injection
réelle du `<script type="application/ld+json">` dans les pages `vente.otov7.com`
reste côté agent Frontend/SEO / VPS).
- **Auto-score 4Big** : 96/100.
+23 -1
View File
File diff suppressed because one or more lines are too long
+106
View File
@@ -2484,6 +2484,112 @@ if sg is not None:
bad(f"SEO schema.org · {SEO_SG_RD} — dit « une `{mpp.group(1)}` par projet » " bad(f"SEO schema.org · {SEO_SG_RD} — dit « une `{mpp.group(1)}` par projet » "
f"MAIS listings sont `{lt}` → régénérer le README") f"MAIS listings sont `{lt}` → régénérer le README")
# ============================================================================
# SEO · l'IDENTITÉ du nœud racine `Organization` du graphe schema.org (JSON-LD)
# — le nom de marque `Helios RD` (marque publique · CLAUDE.md §Entités) + son
# `url` == base_url du site — ANCRÉE sur CLAUDE.md, jamais gatée.
# ----------------------------------------------------------------------------
# out/seo_schema_org.json est byte-gaté par check_artifacts : la reproductibilité
# prouve qu'il SE RECONSTRUIT depuis seo_spec.json, PAS que son `Organization.name`
# == la marque publique nommée dans CLAUDE.md §Entités (« **Helios RD** (marque
# publique) sous WAG »). Le bloc SEO schema.org amont gate la COMPOSITION (@type +
# bijection un-listing-par-projet), jamais l'IDENTITÉ de la marque ni le rattachement
# des nœuds au site. Piège #6 : RENOMMER la marque dans
# seo_spec.json[schema_org.organization.name] (ou dans CLAUDE.md §Entités) laisse le
# JSON-LD PUBLIC — celui que Google indexe sous `vente.otov7.com` — émettre un nom de
# marque qui CONTREDIT le mandat pendant que la byte-gate reste VERTE (l'artefact se
# reconstruit fidèlement… depuis un spec dérivé) — « vert trompeur » de la classe des
# tokens branding ancrés sur CLAUDE.md #4. Aucune suite tests/ (FONCTIONS de
# génération, jamais l'ancrage à CLAUDE.md) ne l'attrape. On RE-DÉRIVE la marque de
# CLAUDE.md §Entités (zéro duplication · source unique) + le base_url du MANIFEST
# byte-gaté + le country_code du spec, puis on exige : (a) Organization.name == marque
# CLAUDE.md ; (b) Organization.url == base_url et @id sous base_url ; (c) chaque nœud
# listing rattaché à CETTE Organization (brand.@id == org.@id) et sous base_url ;
# (d) addressCountry UNIFORME == country_code du spec (géo-ciblage appliqué à
# l'identique à chaque nœud) ; (e) le spec DÉCLARE l'ancrage (organization.name ==
# marque + `source` cite CLAUDE.md). État courant : aucune valeur périmée — le défaut
# est la surface ungated.
# ============================================================================
_nfc_org = lambda s: unicodedata.normalize("NFC", str(s))
try:
cm_ent = open("CLAUDE.md", encoding="utf-8").read()
_mb = re.findall(r"\*\*([^*]+?)\*\*\s*\(marque publique\)", cm_ent)
if len(_mb) != 1:
raise ValueError(f"« (marque publique) » attendu 1× dans CLAUDE.md §Entités, "
f"trouvé {len(_mb)}×")
brand = _nfc_org(_mb[0].strip())
except (OSError, ValueError) as e:
bad(f"SEO org · marque publique CLAUDE.md §Entités illisible/inattendue : {e}"); brand = None
try:
sog = load("seo/out/seo_schema_org.json")["@graph"]
seo_base = load("seo/out/MANIFEST.json")["base_url"]
with open(f"{D}/seo/seo_spec.json", encoding="utf-8") as _f:
spec_sorg = json.load(_f)["schema_org"]
seo_cc = spec_sorg["country_code"]
spec_orgn = spec_sorg["organization"]
org_n = [n for n in sog if str(n.get("@id", "")).endswith("#organization")]
lst_n = [n for n in sog if not str(n.get("@id", "")).endswith("#organization")]
except (OSError, KeyError, ValueError, TypeError) as e:
bad(f"SEO org · artefact/spec schema.org illisible/inattendu : {e}"); sog = None
if brand is not None and sog is not None:
# (0) racine Organization = singleton (pré-requis à toute assertion d'identité)
if len(org_n) != 1:
bad(f"SEO org · nœud racine #organization non-singleton ({len(org_n)}) "
f"→ identité de marque indéterminable")
else:
org = org_n[0]
# (a) ANCRAGE : Organization.name == marque publique CLAUDE.md §Entités
got_name = _nfc_org(org.get("name", ""))
if got_name == brand:
good(f"SEO org · Organization.name {got_name!r} == marque publique "
f"CLAUDE.md §Entités")
else:
bad(f"SEO org · Organization.name {got_name!r} ≠ CLAUDE.md §Entités {brand!r} "
f"→ JSON-LD public contredit le mandat (régénérer spec/artefact ou aligner CLAUDE.md)")
# (b) Organization.url == base_url + @id sous base_url
if org.get("url") == seo_base and str(org.get("@id", "")) == f"{seo_base}/#organization":
good(f"SEO org · Organization.url == base_url ({seo_base}) · @id cohérent")
else:
bad(f"SEO org · Organization.url={org.get('url')!r}/@id={org.get('@id')!r} "
f"≠ base_url {seo_base!r}(+/#organization) → graphe incohérent")
# (c) chaque listing rattaché à CETTE Organization (brand.@id), sous base_url
bad_brand = [n.get("@id") for n in lst_n
if (n.get("brand") or {}).get("@id") != org.get("@id")]
bad_url = [n.get("@id") for n in lst_n
if not str(n.get("url", "")).startswith(f"{seo_base}/")]
if not lst_n:
bad("SEO org · aucun nœud listing → graphe suspect")
elif bad_brand or bad_url:
d = (f" · brand.@id≠org: {bad_brand}" if bad_brand else "") + \
(f" · url hors base_url: {bad_url}" if bad_url else "")
bad(f"SEO org · {len(lst_n)} listing(s) mais rattachement/URL cassé{d}")
else:
good(f"SEO org · {len(lst_n)} listing(s) rattaché(s) à l'Organization "
f"(brand.@id) sous base_url")
# (d) géo-ciblage : addressCountry UNIFORME == country_code du spec (code ISO 2)
if not re.fullmatch(r"[A-Z]{2}", str(seo_cc)):
bad(f"SEO org · country_code du spec {seo_cc!r} non conforme "
f"(code pays ISO 2 lettres attendu)")
else:
countries = {(n.get("address") or {}).get("addressCountry") for n in lst_n}
if countries == {seo_cc}:
good(f"SEO org · addressCountry uniforme == country_code spec ({seo_cc}) "
f"sur {len(lst_n)} nœud(s)")
else:
bad(f"SEO org · addressCountry {sorted(map(str, countries))} ≠ {{{seo_cc}}} du spec "
f"→ géo-ciblage non uniforme/divergent")
# (e) le spec DÉCLARE l'ancrage : organization.name == marque + source cite CLAUDE.md
spec_name = _nfc_org(spec_orgn.get("name", ""))
spec_src = spec_orgn.get("source", "")
if spec_name != brand:
bad(f"SEO org · seo_spec.json organization.name {spec_name!r} ≠ CLAUDE.md {brand!r} "
f"(l'input du générateur DÉRIVE de CLAUDE.md §Entités)")
elif "CLAUDE.md" not in spec_src or brand not in spec_src:
bad(f"SEO org · seo_spec.json organization.source ne DÉCLARE pas l'ancrage "
f"CLAUDE.md/{brand!r} : {spec_src!r}")
else:
good(f"SEO org · seo_spec.json déclare l'ancrage CLAUDE.md §Entités pour {brand!r}")
# ============================================================================ # ============================================================================
# SEO · la CARTE hreflang (`alternate` FR/EN/ES + `x-default` par page) — la # SEO · la CARTE hreflang (`alternate` FR/EN/ES + `x-default` par page) — la
# 3ᵉ surface du même README SEO — décrite EN PROSE sans AUCUN gate d'IDENTITÉ. # 3ᵉ surface du même README SEO — décrite EN PROSE sans AUCUN gate d'IDENTITÉ.