[DTP-Worker] Sprint 8 · buffer · SEO/projets_master : IDENTITÉ du CATALOGUE PROJETS (P01..P09) ANCRÉE sur CLAUDE.md §Projets
La matière première du worker SEO est `05_deliverables_mvp/seo/fixtures/projets_master.json` — les 9 projets (`code`+`nom`) que le bundle expose à Google (un listing `Residence`/projet dans le JSON-LD indexé sous `vente.otov7.com/projets/…`). Chaque entrée DÉCLARE `source.fichiers: ["CLAUDE.md#Projets"]` et le README:80 l'affirme (« 9 projets P01..P09 · noms sourcés de `CLAUDE.md §Projets` }) MAIS AUCUN gate ne le VÉRIFIAIT. check_artifacts prouve QUE les out/ SE RECONSTRUISENT depuis la fixture (byte-for-byte), PAS que ses code/nom == CLAUDE.md §Projets ; les blocs SEO amont gatent les COMPTES de mots-clés + la COMPOSITION du graphe schema.org (bijection un-listing/projet) + l'IDENTITÉ de la marque racine `Helios RD` (§Entités), tous AVEUGLES à l'identité de la LISTE de projets. Piège #6 : RENOMMER un projet dans CLAUDE.md §Projets (« Aqua Terra Las Terrenas »→« … Bay »), le RENOMMER/AJOUTER/RETIRER dans la fixture, puis régénérer, laisse le JSON-LD PUBLIC lister un projet FANTÔME ou en OMETTRE un canonique pendant que la byte-gate reste VERTE (l'artefact se reconstruit fidèlement… depuis une fixture DÉRIVÉE) et que le compte reste 9 — « vert trompeur » de la MÊME classe que la marque org (§Entités), les entités CONFOTUR (§Entités) et les libellés projets du run_sheet démo (§Projets). Aucune suite tests/ (FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape. Gate ajouté (bloc « SEO · l'IDENTITÉ du CATALOGUE PROJETS ») : RE-DÉRIVE le catalogue de CLAUDE.md §Projets (zéro duplication · source unique) puis exige — (a) identité d'ensemble des codes fixture ⇔ CLAUDE.md (aucun fantôme/manquant · pas de doublon) ; (b) `nom` de chaque entrée == CLAUDE.md §Projets (accent-INSENSIBLE · la fixture normalise « Xamaná »→« Xamana ») ; (c) chaque entrée DÉCLARE l'ancrage `CLAUDE.md#Projets` dans source.fichiers ; (d) les `name` des listings du graphe schema.org PUBLIC == l'ensemble des noms CLAUDE.md §Projets + bijection (1 listing/projet) + MANIFEST counts projects/listings == |§Projets| ; (e) le README cite l'ancrage §Projets. Un claim absent échoue AUSSI (disparition = régression). 13 confirmations vertes ; 7 morsures vérifiées puis restaurées (CLAUDE.md rename P07 = bites b+d simultanés l'ancre est vive · fixture nom drift P03 · fixture drop P09 · fixture add fantôme P10 · fixture strip source.fichiers P01 · artefact schema.org rename Structure→Estructura = le vrai silent-green public · README retire la citation §Projets), restauré vert, 7 gates CI re-verts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -916,3 +916,24 @@ check_regression).
|
|||||||
la confirmation du **TipoCambio** USD→DOP + l'émission réelle via Compupar
|
la confirmation du **TipoCambio** USD→DOP + l'émission réelle via Compupar
|
||||||
restent côté agent ERPNext Backend / Compta · VPS).
|
restent côté agent ERPNext Backend / Compta · VPS).
|
||||||
- **Auto-score 4Big** : 96/100.
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session `20260801_080244` · Buffer S8 · SEO/projets_master — IDENTITÉ du CATALOGUE PROJETS (P01..P09) ANCRÉE sur CLAUDE.md §Projets
|
||||||
|
|
||||||
|
**Domaine** : SEO · `05_deliverables_mvp/seo/fixtures/projets_master.json` + artefacts `out/seo_schema_org.json`.
|
||||||
|
|
||||||
|
**Constat (anti-drift #6)** : la matière première du worker SEO est `projets_master.json` — 9 projets (`code` + `nom`) que le bundle SEO expose à Google (un listing `Residence` par projet dans le JSON-LD indexé sous `vente.otov7.com/projets/…`). Chaque entrée **DÉCLARE** `source.fichiers: ["CLAUDE.md#Projets"]` et le README:80 l'affirme (« 9 projets P01..P09 · noms sourcés de `CLAUDE.md §Projets` ») — **MAIS AUCUN gate ne le VÉRIFIAIT**. `check_artifacts` prouve QUE les `out/` se reconstruisent depuis la fixture (byte-for-byte), PAS que ses `code`/`nom` == CLAUDE.md §Projets ; les blocs SEO amont gatent les COMPTES de mots-clés, la COMPOSITION du graphe schema.org (bijection un-listing-par-projet) et l'IDENTITÉ de la marque racine `Helios RD` (§Entités) — tous AVEUGLES à l'identité de la **liste de projets**.
|
||||||
|
|
||||||
|
**Vert trompeur** : RENOMMER un projet dans CLAUDE.md §Projets (« Aqua Terra Las Terrenas » → « … Bay »), le RENOMMER/AJOUTER/RETIRER dans la fixture, puis régénérer, laisse le JSON-LD PUBLIC lister un projet FANTÔME ou en OMETTRE un canonique pendant que la byte-gate reste VERTE (l'artefact se reconstruit fidèlement… depuis une fixture DÉRIVÉE) et que le compte reste 9. Même classe EXACTE que la marque org (§Entités · session 050225), les entités CONFOTUR (§Entités · 053228) et les libellés projets du run_sheet démo (§Projets · déjà gaté). Aucune suite `tests/` (FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape.
|
||||||
|
|
||||||
|
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « SEO · l'IDENTITÉ du CATALOGUE PROJETS ») — RE-DÉRIVE le catalogue de CLAUDE.md §Projets (zéro duplication · source unique), puis exige :
|
||||||
|
- **(a)** identité d'ensemble des codes fixture ⇔ CLAUDE.md §Projets (aucun fantôme/manquant · pas de doublon) ;
|
||||||
|
- **(b)** `nom` de chaque entrée == CLAUDE.md §Projets (accent-**insensible** : la fixture normalise « Xamaná »→« Xamana ») ;
|
||||||
|
- **(c)** chaque entrée DÉCLARE l'ancrage `CLAUDE.md#Projets` dans `source.fichiers` ;
|
||||||
|
- **(d)** les `name` des listings du graphe schema.org PUBLIC == l'ensemble des noms CLAUDE.md §Projets + bijection (1 listing/projet) + `MANIFEST.counts` projects/listings == |§Projets| ;
|
||||||
|
- **(e)** le README cite l'ancrage CLAUDE.md §Projets. Un claim absent échoue AUSSI (disparition = régression).
|
||||||
|
|
||||||
|
**Vérif** : 13 confirmations vertes (9 noms + set codes + listings schema.org + MANIFEST + README) ; **7 morsures vérifiées puis restaurées** — CLAUDE.md rename P07 (bite b+d simultanés, l'ancre est vive) · fixture nom drift P03 · fixture drop P09 (set manquant) · fixture add fantôme P10 (set fantôme) · fixture strip `source.fichiers` P01 · artefact schema.org rename `Structure`→`Estructura` (le vrai silent-green public) · README retire la citation §Projets. Restauré vert. **7 gates CI re-verts** (`check_readme_claims`, `check_artifacts`, `check_docs`, `check_ci_integrity`, `guard_constraints`, `check_regression`, `validate_json`).
|
||||||
|
|
||||||
|
- **Auto-score 4Big** : 96/100.
|
||||||
|
|||||||
@@ -2666,6 +2666,121 @@ if brand is not None and sog is not None:
|
|||||||
else:
|
else:
|
||||||
good(f"SEO org · seo_spec.json déclare l'ancrage CLAUDE.md §Entités pour {brand!r}")
|
good(f"SEO org · seo_spec.json déclare l'ancrage CLAUDE.md §Entités pour {brand!r}")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# SEO · l'IDENTITÉ du CATALOGUE PROJETS (P01..P09) — les 9 projets (`code` +
|
||||||
|
# `nom`) que le bundle SEO expose à Google — ANCRÉE sur CLAUDE.md §Projets.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# La matière première du worker SEO est `seo/fixtures/projets_master.json`
|
||||||
|
# (README:22 « sortie canonique … le worker ne fabrique aucun fait de projet »).
|
||||||
|
# Chaque entrée DÉCLARE `source.fichiers: ["CLAUDE.md#Projets"]` et le README:80
|
||||||
|
# l'affirme (« 9 projets P01..P09 · noms sourcés de `CLAUDE.md §Projets` ») — MAIS
|
||||||
|
# AUCUN gate ne le VÉRIFIAIT. check_artifacts prouve QUE les out/ SE RECONSTRUISENT
|
||||||
|
# depuis la fixture (byte-for-byte), PAS que ses `code`/`nom` == CLAUDE.md §Projets ;
|
||||||
|
# les blocs SEO amont gatent les COMPTES de mots-clés + la COMPOSITION du graphe
|
||||||
|
# schema.org (bijection un-listing-par-projet) + l'IDENTITÉ de la marque racine
|
||||||
|
# (§Entités), tous AVEUGLES à l'identité de la LISTE de projets. Piège #6 :
|
||||||
|
# RENOMMER un projet dans CLAUDE.md §Projets (« Aqua Terra Las Terrenas » → « … Bay »),
|
||||||
|
# le RENOMMER/AJOUTER/RETIRER dans la fixture, puis régénérer, laisse le JSON-LD PUBLIC
|
||||||
|
# — celui que Google indexe sous `vente.otov7.com/projets/…` — lister un projet
|
||||||
|
# FANTÔME ou en OMETTRE un canonique pendant que la byte-gate reste VERTE (l'artefact
|
||||||
|
# se reconstruit fidèlement… depuis une fixture DÉRIVÉE) et que le compte reste 9 —
|
||||||
|
# « vert trompeur » de la MÊME classe que la marque org (§Entités), les entités
|
||||||
|
# CONFOTUR (§Entités) et les libellés projets du run_sheet démo (§Projets). Aucune
|
||||||
|
# suite tests/ (FONCTIONS de génération, jamais l'ancrage à CLAUDE.md) ne l'attrape.
|
||||||
|
# On RE-DÉRIVE le catalogue de CLAUDE.md §Projets (zéro duplication · source unique)
|
||||||
|
# puis on exige : (a) identité d'ensemble des codes fixture ⇔ CLAUDE.md (aucun
|
||||||
|
# fantôme/manquant · pas de doublon) ; (b) `nom` de chaque entrée == CLAUDE.md §Projets
|
||||||
|
# (accent-insensible · la fixture normalise « Xamaná »→« Xamana ») ; (c) chaque entrée
|
||||||
|
# DÉCLARE l'ancrage `CLAUDE.md#Projets` dans `source.fichiers` ; (d) les `name` des
|
||||||
|
# listings du graphe schema.org PUBLIC == l'ensemble des noms CLAUDE.md §Projets +
|
||||||
|
# bijection (1 listing/projet) + MANIFEST counts projects/listings == |§Projets| ;
|
||||||
|
# (e) le README cite l'ancrage CLAUDE.md §Projets. Un claim absent échoue AUSSI
|
||||||
|
# (disparition = régression). État courant : aucune valeur périmée.
|
||||||
|
# ============================================================================
|
||||||
|
_pnfc = lambda s: unicodedata.normalize("NFC", str(s))
|
||||||
|
_pai = lambda s: "".join(c for c in unicodedata.normalize("NFKD", str(s))
|
||||||
|
if unicodedata.category(c) != "Mn").lower().replace(" ", "")
|
||||||
|
try:
|
||||||
|
cm_pj = open("CLAUDE.md", encoding="utf-8").read()
|
||||||
|
_mpj = re.search(r"^## Projets\s*\n(.+)$", cm_pj, re.M)
|
||||||
|
if _mpj is None:
|
||||||
|
raise ValueError("section « ## Projets » INTROUVABLE")
|
||||||
|
seo_pmap = {}
|
||||||
|
for _t in _mpj.group(1).split("·"):
|
||||||
|
_mc = re.match(r"(P\d{2})\s+(.+)$", _pnfc(_t.strip()))
|
||||||
|
if _mc:
|
||||||
|
seo_pmap[_mc.group(1)] = _mc.group(2).strip()
|
||||||
|
if not seo_pmap:
|
||||||
|
raise ValueError("aucun projet PNN parsé")
|
||||||
|
except (OSError, ValueError) as e:
|
||||||
|
bad(f"SEO projets · CLAUDE.md §Projets illisible/inattendu : {e}"); seo_pmap = None
|
||||||
|
try:
|
||||||
|
with open(f"{D}/seo/fixtures/projets_master.json", encoding="utf-8") as _f:
|
||||||
|
seo_pm = json.load(_f)["projets"]
|
||||||
|
sg_pj = load("seo/out/seo_schema_org.json")["@graph"]
|
||||||
|
seo_cpj = load("seo/out/MANIFEST.json")["counts"]
|
||||||
|
lst_pj = [n for n in sg_pj if not str(n.get("@id", "")).endswith("#organization")]
|
||||||
|
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||||
|
bad(f"SEO projets · fixture/artefact illisible/inattendu : {e}"); seo_pm = None
|
||||||
|
if seo_pmap is not None and seo_pm is not None:
|
||||||
|
# (a) ANCRAGE : identité d'ensemble des codes fixture ⇔ CLAUDE.md §Projets
|
||||||
|
fix_codes = [p.get("code") for p in seo_pm]
|
||||||
|
if len(set(fix_codes)) != len(fix_codes) or None in fix_codes or "" in fix_codes:
|
||||||
|
bad(f"SEO projets · projets_master : codes en doublon/vides {fix_codes}")
|
||||||
|
fx = {c for c in fix_codes if c}
|
||||||
|
miss = sorted(set(seo_pmap) - fx); extra = sorted(fx - set(seo_pmap))
|
||||||
|
if miss or extra:
|
||||||
|
bad(f"SEO projets · codes projets_master ≠ CLAUDE.md §Projets"
|
||||||
|
f"{' · manquants=' + str(miss) if miss else ''}"
|
||||||
|
f"{' · fantômes=' + str(extra) if extra else ''}")
|
||||||
|
else:
|
||||||
|
good(f"SEO projets · {len(fx)} codes P01..P09 projets_master == CLAUDE.md §Projets")
|
||||||
|
# (b)+(c) nom ancré + déclaration de l'ancrage, par projet
|
||||||
|
for p in seo_pm:
|
||||||
|
code = p.get("code"); nom = _pnfc(p.get("nom", ""))
|
||||||
|
anchor = seo_pmap.get(code)
|
||||||
|
if anchor is None:
|
||||||
|
continue # code fantôme : déjà mordu par le set-diff (a)
|
||||||
|
if _pai(nom) != _pai(anchor):
|
||||||
|
bad(f"SEO projets · {code} nom « {nom} » (projets_master) DÉRIVE de "
|
||||||
|
f"CLAUDE.md §Projets « {anchor} » → aligner (ou CLAUDE.md)")
|
||||||
|
else:
|
||||||
|
good(f"SEO projets · {code} — nom « {nom} » == CLAUDE.md §Projets")
|
||||||
|
srcf = (p.get("source") or {}).get("fichiers") or []
|
||||||
|
if not any("CLAUDE.md" in str(x) and "Projets" in str(x) for x in srcf):
|
||||||
|
bad(f"SEO projets · {code} — projets_master ne DÉCLARE pas l'ancrage "
|
||||||
|
f"`CLAUDE.md#Projets` dans source.fichiers : {srcf}")
|
||||||
|
# (d) artefact PUBLIC : `name` des listings schema.org == noms CLAUDE.md §Projets
|
||||||
|
if not lst_pj:
|
||||||
|
bad("SEO projets · aucun nœud listing schema.org → graphe suspect")
|
||||||
|
else:
|
||||||
|
got_n = {_pai(n.get("name", "")) for n in lst_pj}
|
||||||
|
want_n = {_pai(v) for v in seo_pmap.values()}
|
||||||
|
gm = sorted(want_n - got_n); ge = sorted(got_n - want_n)
|
||||||
|
if gm or ge:
|
||||||
|
bad(f"SEO projets · `name` des listings schema.org (JSON-LD public) ≠ "
|
||||||
|
f"CLAUDE.md §Projets{' · manquants=' + str(gm) if gm else ''}"
|
||||||
|
f"{' · en trop=' + str(ge) if ge else ''}")
|
||||||
|
elif len(lst_pj) != len(seo_pmap):
|
||||||
|
bad(f"SEO projets · {len(lst_pj)} listing(s) schema.org MAIS "
|
||||||
|
f"{len(seo_pmap)} projet(s) CLAUDE.md §Projets (bijection cassée)")
|
||||||
|
else:
|
||||||
|
good(f"SEO projets · {len(lst_pj)} listings schema.org (name) == CLAUDE.md "
|
||||||
|
f"§Projets (JSON-LD indexé fidèle au mandat)")
|
||||||
|
if seo_cpj.get("projects") != len(seo_pmap) or seo_cpj.get("listings") != len(seo_pmap):
|
||||||
|
bad(f"SEO projets · MANIFEST counts projects={seo_cpj.get('projects')}/"
|
||||||
|
f"listings={seo_cpj.get('listings')} ≠ {len(seo_pmap)} (CLAUDE.md §Projets)")
|
||||||
|
else:
|
||||||
|
good(f"SEO projets · MANIFEST counts projects/listings == {len(seo_pmap)} (§Projets)")
|
||||||
|
# (e) le README cite l'ancrage CLAUDE.md §Projets
|
||||||
|
seo_p_rd = read_norm(f"{D}/seo/README.md")
|
||||||
|
if seo_p_rd is not None:
|
||||||
|
if re.search(r"CLAUDE\.md\s*§?\s*Projets", seo_p_rd):
|
||||||
|
good("SEO projets · README cite l'ancrage CLAUDE.md §Projets")
|
||||||
|
else:
|
||||||
|
bad("SEO projets · README ne cite pas l'ancrage CLAUDE.md §Projets "
|
||||||
|
"(attendu « noms sourcés de CLAUDE.md §Projets »)")
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 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É.
|
||||||
|
|||||||
Reference in New Issue
Block a user