[DTP-Worker] Sprint 8 · buffer · SEO/fiche agent : gate d'IDENTITÉ des 2 métriques data-derived (schema.org 10 nœuds · hreflang 10 pages FR/EN/ES) de 03_agents/seo/AGENT.md ancré sur seo_schema_org.json ⨯ seo_hreflang.json
Les blocs SEO schema.org/hreflang ne gataient que seo/README.md ; la fiche restituait N nœuds (Organization + M Residence) et N pages × langues sans gate. Recompute des memes artefacts byte-gates + coherence arithmetique total==1+M. 8 morsures verifiees, working tree byte-restaure, 7 gates re-verts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1166,3 +1166,56 @@ interdit absolu) · **7 gates re-verts** (`check_readme_claims`/`check_artifacts
|
||||
aucune écriture dans un `out/` ⇒ 0 dérive d'artefact ; le câblage réel de l'endpoint
|
||||
OTOIA + web-component reste côté agent Frontend / VPS).
|
||||
- **Auto-score 4Big** : 96/100.
|
||||
|
||||
## Session `20260801_110311` · Buffer S8 · SEO — IDENTITÉ des 2 métriques data-derived (schema.org · hreflang) de la fiche agent
|
||||
|
||||
**Contexte** : Sprint 8 · buffer (roadmap fonctionnellement close ; poursuite de la
|
||||
série anti-dérive CLAUDE.md #6, classe « fiche agent qui restitue des attributs
|
||||
data-derived, gatée uniquement en AGRÉGAT » — mémoire
|
||||
[[agent-fiche-role-attrs-ungated]] ; même patron que la cellule chat_otoia de la
|
||||
fiche Frontend Console traitée à la session précédente).
|
||||
|
||||
**Dérive silencieuse fermée** :
|
||||
- `03_agents/seo/AGENT.md:25-26` — le tableau « Livrable réellement produit »
|
||||
restitue DEUX sorties byte-gatées du bundle SEO à côté du compte de mots-clés
|
||||
(lui déjà gaté sur README ET fiche) :
|
||||
- L25 : « graphe JSON-LD : **10 nœuds** (`Organization` + 9 `Residence`) »
|
||||
- L26 : « **10 pages** × FR/EN/ES + `x-default` »
|
||||
- Les blocs SEO schema.org (`SEO_SG_RD`) et hreflang (`SEO_HL_RD`) amont ne lisent
|
||||
QUE `seo/README.md` — JAMAIS la fiche. `SEO_FI` n'était utilisé que par le bloc
|
||||
de COMPTE de mots-clés. Ces deux cellules étaient donc des surfaces data-derived
|
||||
ungated : RENOMMER le `@type` des listings, AJOUTER/RETIRER un projet (→ N nœuds
|
||||
& N pages dérivent) ou ÉCHANGER une langue de contenu périme la fiche pendant que
|
||||
les trois artefacts byte-gatés (`seo_schema_org.json`/`MANIFEST.json` et
|
||||
`seo_hreflang.json`) disent autre chose — « vert trompeur » qu'aucune suite
|
||||
`tests/` (FONCTIONS du builder, jamais la prose de la fiche) n'attrape.
|
||||
- **État courant** : aucune métrique périmée — 10 nœuds (1 `Organization` + 9
|
||||
`Residence`) · 10 pages · langues `fr/en/es` + `x-default` (anti-invention #6,
|
||||
rien à réécrire). Le défaut est la **surface ungated**.
|
||||
|
||||
**Gate ajouté** (`ci/check_readme_claims.sh`, bloc « SEO · FICHE AGENT » inséré
|
||||
après le bloc hreflang README, réutilise les vérités déjà recomputées par les blocs
|
||||
schema.org/hreflang amont — zéro duplication #6) :
|
||||
- L25 (regex `graphe JSON-LD : \*\*(\d+) nœuds\*\* \(`<racine>` \+ (\d+)
|
||||
`<listing>`\)`) : total == `schema_org_nodes` · racine == `@type` singleton du
|
||||
graphe · M == `listings` · type listing == `@type` uniforme des listings ·
|
||||
cohérence arithmétique `total == 1 + M`.
|
||||
- L26 (regex `\*\*(\d+) pages\*\* × ([A-Z/]+) \+ `x-default``) : `|pages|` == carte
|
||||
hreflang · jeu de langues (lower) == langues de contenu de l'artefact (set-diff :
|
||||
absent ET en trop).
|
||||
- Un claim absent échoue AUSSI (INTROUVABLE = régression #6).
|
||||
|
||||
**8 morsures vérifiées** (restauré vert après chacune) : nœuds `10→11` · listing
|
||||
`Residence→Apartment` · racine `Organization→LocalBusiness` · listings `9→8`
|
||||
(casse l'arithmétique ET la bijection) · pages `10→12` · langue `es→pt` · retrait
|
||||
`es` (`FR/EN`) · retrait du claim L25 (INTROUVABLE). Working tree byte-restauré
|
||||
(`git checkout --`, **JAMAIS** `git clean`, interdit absolu) · **7 gates re-verts**
|
||||
(`check_readme_claims`/`check_artifacts`/`check_docs`/`guard_constraints`/
|
||||
`check_regression`/`check_ci_integrity`/`validate_json`).
|
||||
|
||||
- `ci/README.md` (clause de la table récap du pipeline + paragraphe détaillé « Fiche
|
||||
agent SEO — 2 métriques data-derived » après le paragraphe hreflang) mis à jour.
|
||||
- **Hors périmètre worker (VPS · #8)** : néant (gate bash/python3 stdlib en-repo ;
|
||||
aucune écriture dans un `out/` ⇒ 0 dérive d'artefact ; l'injection réelle des
|
||||
balises hreflang/JSON-LD dans les pages reste côté agent Frontend/SEO / VPS).
|
||||
- **Auto-score 4Big** : 96/100.
|
||||
|
||||
+20
-1
File diff suppressed because one or more lines are too long
@@ -3235,6 +3235,80 @@ if hl_pages:
|
||||
f"{'+x-default' if has_xdef else ''} MAIS carte "
|
||||
f"{sorted(content_langs)}+x-default{detail} → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# SEO · FICHE AGENT (03_agents/seo/AGENT.md) · les cellules « Métrique vérifiée »
|
||||
# restituent DEUX attributs data-derived du bundle SEO — gatées seulement côté
|
||||
# README, JAMAIS sur la fiche.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le tableau « Livrable réellement produit » (fiche:24-26) transcrit À LA MAIN,
|
||||
# à côté du compte de mots-clés (lui déjà gaté sur README **et** fiche via le
|
||||
# bloc SEO_FI amont), deux sorties byte-gatées mais AVEUGLÉES côté fiche :
|
||||
# L25 « graphe JSON-LD : **10 nœuds** (`Organization` + 9 `Residence`) »
|
||||
# L26 « **10 pages** × FR/EN/ES + `x-default` ».
|
||||
# Les blocs SEO schema.org / hreflang ci-dessus ne lisent QUE le README
|
||||
# (SEO_SG_RD / SEO_HL_RD) : RENOMMER le @type des listings, AJOUTER/RETIRER un
|
||||
# projet (→ N nœuds & N pages dérivent) ou ÉCHANGER une langue de contenu laisse
|
||||
# ces cellules périmées pendant que les trois artefacts byte-gatés disent autre
|
||||
# chose — « vert trompeur » de la classe des fiches-agent qui restituent des
|
||||
# attributs data-derived gatés seulement en AGRÉGAT (mémoire
|
||||
# agent-fiche-role-attrs-ungated ; même patron que la cellule chat_otoia de la
|
||||
# fiche Frontend Console). Aucune suite tests/ (FONCTIONS du builder, jamais la
|
||||
# prose de la fiche) ne l'attrape. On RECOMPUTE depuis les MÊMES artefacts
|
||||
# byte-gatés (schema.org + hreflang) réutilisés par les blocs README ci-dessus
|
||||
# (zéro duplication #6) et on exige l'IDENTITÉ EXACTE ; un claim absent échoue
|
||||
# AUSSI (INTROUVABLE = régression #6). Cohérence arithmétique en bonus : total
|
||||
# nœuds == 1 racine + M listings (un graphe interne incohérent est mordu).
|
||||
# ============================================================================
|
||||
seo_fi = read_norm(SEO_FI) # SEO_FI défini au bloc « mots-clés » amont
|
||||
if seo_fi is not None and sg is not None:
|
||||
# fiche:25 — « graphe JSON-LD : **N nœuds** (`<racine>` + M `<listing>`) »
|
||||
root_t = sorted(org_types)[0] if len(org_types) == 1 else None
|
||||
list_t = sorted(list_types)[0] if len(list_types) == 1 else None
|
||||
n_nodes = seo_cnt.get("schema_org_nodes")
|
||||
n_list = seo_cnt.get("listings")
|
||||
m = re.search(r"graphe JSON-LD : \*\*(\d+) nœuds\*\* "
|
||||
r"\(`([A-Za-z]+)` \+ (\d+) `([A-Za-z]+)`\)", seo_fi)
|
||||
if m is None:
|
||||
bad(f"SEO fiche · {SEO_FI} — cellule « graphe JSON-LD : **N nœuds** "
|
||||
f"(`<racine>` + M `<listing>`) » INTROUVABLE (attendu {n_nodes} nœuds : "
|
||||
f"1 `{root_t}` + {n_list} `{list_t}`)")
|
||||
else:
|
||||
g_tot, g_root = int(m.group(1)), m.group(2)
|
||||
g_m, g_list = int(m.group(3)), m.group(4)
|
||||
errs = []
|
||||
if g_tot != n_nodes: errs.append(f"total {g_tot}≠{n_nodes}")
|
||||
if g_root != root_t: errs.append(f"racine `{g_root}`≠`{root_t}`")
|
||||
if g_m != n_list: errs.append(f"listings {g_m}≠{n_list}")
|
||||
if g_list != list_t: errs.append(f"type listing `{g_list}`≠`{list_t}`")
|
||||
if g_tot != 1 + g_m: errs.append(f"arith {g_tot}≠1+{g_m}")
|
||||
if errs:
|
||||
bad(f"SEO fiche · {SEO_FI}:25 — {' · '.join(errs)} → régénérer la fiche")
|
||||
else:
|
||||
good(f"SEO fiche · {SEO_FI}:25 — {g_tot} nœuds (1 `{g_root}` + {g_m} "
|
||||
f"`{g_list}`) == seo_schema_org.@graph")
|
||||
if seo_fi is not None and hl_pages:
|
||||
# fiche:26 — « **N pages** × <langues> + `x-default` »
|
||||
exp_langs = "/".join(c.upper() for c in sorted(content_langs))
|
||||
m = re.search(r"\*\*(\d+) pages\*\* × ([A-Z/]+) \+ `x-default`", seo_fi)
|
||||
if m is None:
|
||||
bad(f"SEO fiche · {SEO_FI} — cellule « **N pages** × <langues> + `x-default` » "
|
||||
f"INTROUVABLE (attendu {len(hl_pages)} pages × {exp_langs})")
|
||||
else:
|
||||
g_pages = int(m.group(1))
|
||||
g_langs = {t.lower() for t in m.group(2).split("/") if t}
|
||||
errs = []
|
||||
if g_pages != len(hl_pages): errs.append(f"pages {g_pages}≠{len(hl_pages)}")
|
||||
if g_langs != content_langs:
|
||||
miss = sorted(content_langs - g_langs); extra = sorted(g_langs - content_langs)
|
||||
errs.append(f"langues {sorted(g_langs)}≠{sorted(content_langs)}"
|
||||
+ (f" absents={miss}" if miss else "")
|
||||
+ (f" en trop={extra}" if extra else ""))
|
||||
if errs:
|
||||
bad(f"SEO fiche · {SEO_FI}:26 — {' · '.join(errs)} → régénérer la fiche")
|
||||
else:
|
||||
good(f"SEO fiche · {SEO_FI}:26 — {g_pages} pages × {exp_langs} + "
|
||||
f"`x-default` == seo_hreflang")
|
||||
|
||||
# ============================================================================
|
||||
# CRM/dossier_vente (2e surface) · CATALOGUE DE PROJETS « P01..P09 ancré sur
|
||||
# CLAUDE.md » — le Select `projet` du DocType porteur.
|
||||
|
||||
Reference in New Issue
Block a user