[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:
@@ -2484,6 +2484,112 @@ if sg is not None:
|
||||
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")
|
||||
|
||||
# ============================================================================
|
||||
# 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
|
||||
# 3ᵉ surface du même README SEO — décrite EN PROSE sans AUCUN gate d'IDENTITÉ.
|
||||
|
||||
Reference in New Issue
Block a user