Skills Celuga
celuga-seo
Métadonnées, hreflang, robots, sitemap, `llms.txt`, JSON-LD, assistant SEO, GEO.
Quand : Métadonnées, hreflang, robots, sitemap, llms.txt, JSON-LD, assistant SEO, GEO.
Portée : machine (~/.agents/skills) · Fichiers : 6
Tout installer
Section intitulée « Tout installer »Copier le bloc et le coller dans un terminal. Il recrée tous les fichiers du skill.
dir="$HOME/.agents/skills/celuga-seo"mkdir -p "$dir"cat > "$dir/SKILL.md" <<'CELUGA_SKILL_EOF'---name: celuga-seodescription: SEO et GEO Celuga Payload + Astro. Use when changing metadata, canonical/noindex/hreflang, robots or AI crawler access, sitemap, llms.txt, JSON-LD, the CMS SEO assistant, or adding an indexable collection. Includes a separate Next.js adaptation reference.---
# SEO et GEO Celuga
Vérifier les sources du projet avant d'appliquer : les références décrivent la disposition cible et signalent ce qui varie.
| Référence | Quand la lire || --- | --- || [Contrat Astro/Payload](references/seo-contract.md) | métadonnées, origines, layout, sitemap, hreflang, assistant, tests || [JSON-LD](references/json-ld.md) | générateur de graphe et aperçu CMS || [GEO](references/geo.md) | règles externes vérifiées, crawlers, llms.txt, contenu et mesure || [Adaptation Next.js](references/nextjs.md) | seulement si le front public est Next.js |
Charger **celuga-media-react** pour la sélection des médias, OG et favicon.
## Points d'entrée
- `packages/site-contracts/src/seo.ts` : `resolveMetadata`, seule résolution, partagée par le site, le sitemap et l'aperçu CMS.- `packages/site-contracts/src/schema.ts` : `buildSchema`, un seul générateur de graphe pour le site et l'aperçu.- Registre public (`collections.ts`) : capacités `seo` et `sitemap`, type article, seuil de mots, champ de description, corps analysé.- `SiteLayout.astro` : émission centralisée des balises et du JSON-LD.- `apps/web/src/lib/public-index.ts` : URL publiées et auto-canoniques, source commune du sitemap et de `llms.txt`.- `apps/cms/src/seo/` : assistant éditorial ; le moteur Yoast reste côté serveur.
## Modifier le SEO
1. Lire le contrat et les sources. Garder les URL publiques distinctes des URL internes.2. Résoudre les champs à partir de données visibles, avec des médias peuplés. Ne rien inventer (auteur, date, avis, prix).3. Passer le résultat au layout existant. Pas de second script JSON-LD par composant.4. Nouvelle collection routée : descripteur (capacités, type article, seuil, description, corps, traduction), route par langue, tags d'invalidation, redirections. Tous les contenus Payload ne sont pas des pages à indexer.5. Nouveau bloc : adaptateur de `apps/cms/src/seo/document.ts`, fixture et test de parité avec le rendu public. Le rendu et l'analyse doivent parcourir les mêmes contenus visibles.
## Règles de rendu
| Situation | Comportement || --- | --- || `PUBLIC_ALLOW_INDEXING !== "true"` | meta noindex, `X-Robots-Tag` du middleware, `robots.txt` en `Disallow: /`, sans mention du sitemap || document `meta.noIndex` | `noindex, follow`, exclu du sitemap et des alternates || canonique personnalisée vers ailleurs | exclu du sitemap, sans ajouter sa cible || traduction incomplète | 404, absente des alternates et du sitemap || liste filtrée | `noIndex`, sans alternates || preview, réponse ≥ 400, route privée | `noindex, nofollow, noarchive`, `no-store` || domaine du CMS | `robots.txt` toujours en `Disallow: /` |
Hreflang seulement entre équivalents publiés, traduits et indexables, avec `x-default`.
## Vérifier
```shbun test tests/seo*.test.ts tests/sitemap.test.ts tests/llms.test.ts tests/schema*.test.ts```
Noms habituels, à confirmer dans le dépôt. Inspecter le HTML SSR de l'accueil, d'une page, d'un article, d'une liste filtrée, d'une page `noIndex` et de la preview selon la modification. Vérifier canonical, origines, robots, OG, favicon et graphe. Sur un environnement non indexable, un score SEO Lighthouse bloqué autour de 69 est attendu. Une réponse 200 à un user-agent simulé ne prouve pas l'accès d'un vrai crawler derrière un WAF.
Ne pas exécuter `seo:backfill`, soumettre un sitemap ni changer l'indexation d'un domaine pendant un simple audit. Pour une mise en production, utiliser **mep-celuga**.CELUGA_SKILL_EOFmkdir -p "$dir/agents"cat > "$dir/agents/openai.yaml" <<'CELUGA_SKILL_EOF'interface: display_name: "Celuga SEO & GEO" short_description: "SEO et visibilité IA : meta, crawlers, sitemap, JSON-LD, contenu citable" default_prompt: "Utilise $celuga-seo pour mettre en place ou auditer le SEO et la visibilité dans les moteurs génératifs de ce projet."CELUGA_SKILL_EOFmkdir -p "$dir/references"cat > "$dir/references/geo.md" <<'CELUGA_SKILL_EOF'# GEO — contenu et accès des moteurs
Sources primaires vérifiées le **2026-10-17**. Recontrôler la documentation du fournisseur avant une décision qui dépend d'une politique actuelle. Les constats de code sont dans [seo-contract.md](seo-contract.md).
## Google et contenu citable
Google exige pour ses liens dans AI Overviews/AI Mode une page indexée et éligible au snippet ; les bonnes pratiques SEO ordinaires restent applicables. Aucun fichier IA ni balisage spécifique n'est nécessaire. `llms.txt` n'est pas une condition de visibilité : il peut être exposé comme index public à moindre coût (généré depuis les mêmes URL que le sitemap), sans lui prêter d'effet sur le classement. L'éligibilité ne garantit ni indexation ni citation. Les données IA de Search sont incluses dans le rapport Performance, type Web ; ne pas promettre un rapport IA séparé ou un contrôle Search Console non vérifié. [Guide officiel](https://developers.google.com/search/docs/appearance/ai-features).
Recommandations éditoriales Celuga, à appliquer selon le contenu demandé :
- Sujet, public et zone d'intervention clairs tôt dans la page ; titres utiles, tableaux/listes HTML pour les comparaisons et étapes.- Informations propres à l'entreprise : cas clients, chiffres contextualisés et datés, expérience de première main ; sources liées pour les affirmations externes.- Auteur réel quand pertinent, date de publication/mise à jour exacte et visible, données structurées cohérentes avec le texte ; ne pas rafraîchir artificiellement les dates.- Nom/adresse/téléphone/profils officiels cohérents sur les pages et les sources de l'entreprise. Ne pas publier des horaires ou certifications déduits.- Texte essentiel présent dans le HTML serveur ; image originale descriptive et image de partage représentative. Ne pas supposer que chaque moteur exécute le JavaScript client.
Ce sont des critères de qualité et de vérifiabilité, pas un classement d'impact mesuré ni une garantie de citation. Ne pas supposer qu'une fiche auteur complète existe.
## Crawlers et politique du projet
Le socle autorise tous les robots quand l'indexation est active. Bloquer les crawlers d'entraînement (GPTBot, ClaudeBot, Google-Extended, Applebot-Extended, CCBot, meta-externalagent, Bytespider) est une décision par site, à prendre avec le client : lire le `robots.txt` du projet avant de la supposer. Ne jamais y mêler les crawlers de recherche.
| Fournisseur | Recherche | Requête utilisateur | Modèles / autre contrôle || --- | --- | --- | --- || Google | Googlebot | selon produit | Google-Extended, distinct de Search || OpenAI | OAI-SearchBot | ChatGPT-User | GPTBot || Anthropic | Claude-SearchBot | Claude-User | ClaudeBot || Perplexity | PerplexityBot | Perplexity-User | PerplexityBot n'est pas déclaré comme crawler d'entraînement |
Les usages sont indépendants ; un refus du crawler de modèles ne doit pas être copié aveuglément sur le crawler de recherche. OpenAI documente environ 24h pour l'ajustement des règles de recherche ; ses requêtes utilisateur peuvent ne pas respecter robots.txt. [Documentation OpenAI](https://developers.openai.com/api/docs/bots).
Anthropic distingue les trois agents et indique qu'ils respectent robots.txt. [Documentation Anthropic](https://support.anthropic.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler).
Perplexity distingue indexation/recherche et requêtes utilisateur ; Perplexity-User ignore généralement robots.txt. Vérifier IP et user-agent ensemble si une règle WAF exige l'identité du bot. [Documentation Perplexity](https://docs.perplexity.ai/docs/resources/perplexity-crawlers).
Googlebot gouverne Search ; Google-Extended contrôle certains usages d'entraînement et d'ancrage hors Search, sans effet sur l'inclusion/classement Search. Pour limiter l'extrait Search, Google documente nosnippet/data-nosnippet/max-snippet/noindex. [Crawlers Google](https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers), [contrôles Search](https://developers.google.com/search/docs/appearance/ai-features).
Robots.txt n'est pas un contrôle d'accès. Un curl avec user-agent simulé teste une réponse, pas l'identité ni l'accès réel d'un crawler. Pour un audit de visibilité, contrôler directives, HTML SSR, configuration WAF/CDN et journaux ; ne pas désactiver une protection globale sans besoin démontré.
## Découverte et mesure
Sitemap et `llms.txt` signalent des URL sans garantir leur indexation ; leur comportement est dans le contrat. Soumission du sitemap et configuration des propriétés Search Console/Bing restent des actions externes distinctes.
Suivre selon les outils déjà disponibles : Performance Search Console, visites référées par les assistants, conversions, logs des crawlers et réponses HTTP. Vérifier dans la documentation/console du fournisseur l'existence et la portée d'un éventuel rapport IA avant de le recommander. Une visite référée et une citation sans clic sont des mesures différentes.
Un panel stable de questions clients peut servir à observer présence, exactitude et URL citée ; consigner date, formulation et moteur. Corriger les informations fausses d'abord dans les sources publiques réelles de l'entreprise.CELUGA_SKILL_EOFmkdir -p "$dir/references"cat > "$dir/references/json-ld.md" <<'CELUGA_SKILL_EOF'# JSON-LD public et aperçu CMS
## Contrat
- `buildSchema` (`packages/site-contracts/src/schema.ts`) génère **un seul** graphe `@context` + `@graph`, pour le site comme pour l'aperçu de l'assistant : changer l'un change l'autre.- Graphe relié : identité (organisation, personne, établissement…), WebSite, WebPage, ImageObject, BreadcrumbList, article éventuel, sujet principal. `Question`/`Answer` pour les blocs FAQ complets.- Sérialisation échappant `<` (`<`). Pas de sortie en preview ni sur une page d'erreur.- Seulement des données réelles et visibles. Une propriété absente reste absente : ni avis, ni prix, ni note fabriqués. Scores et densité de mots clés ne sont pas des faits à exporter.- `@id` et `@type` automatiques protégés contre les propriétés libres.
## Réglages éditoriaux
`Configuration > Référencement > Schema.org` définit l'identité et les types par défaut. Dans chaque document, l'onglet Schema choisit le type de page ou d'article, l'auteur, le titre du fil d'Ariane, le sujet principal et des propriétés complémentaires en JSON. `schema-catalog.ts` liste les types proposés ; un autre type officiel peut être saisi.
Une sélection de type n'est pas une validation sémantique. Pour `QAPage` et `ProfilePage`, `mainEntity` doit être renseigné : ces types ne se déduisent pas d'un bloc FAQ.
## FAQ et fonctionnalités des moteurs
`FAQPage` et `Question` décrivent des questions visibles. Google a retiré l'affichage FAQ rich result à partir du **7 mai 2026** : ne pas le promettre. [Journal officiel, vérifié le 2026-10-17](https://developers.google.com/search/updates).
Pour un nouveau type, lire sa documentation officielle au moment de l'intégration. Un graphe valide n'est pas une garantie d'éligibilité ni d'affichage. Règles IA : [GEO](geo.md).
## Ajouter ou modifier
1. Identifier les champs Payload sources et les données réellement visibles.2. Modifier `buildSchema` ou son catalogue, jamais un composant.3. Pour un nouveau bloc qui alimente le graphe, vérifier qu'il reçoit ses données préparées (par exemple une FAQ alimentée par une collection) sur la page publique et en preview.4. Tests du dépôt, puis [validator.schema.org](https://validator.schema.org/) ; le [Rich Results Test](https://search.google.com/test/rich-results) seulement pour ce qu'il couvre.
Inspection au navigateur :
```jsJSON.parse(document.querySelector('script[type="application/ld+json"]').textContent)```CELUGA_SKILL_EOFmkdir -p "$dir/references"cat > "$dir/references/nextjs.md" <<'CELUGA_SKILL_EOF'# Adaptation Next.js single-app
Lire ce fichier uniquement pour un projet Celuga dont le front public et Payload partagent Next.js. **Ce n'est pas l'architecture cible**, où le front public est Astro. Ne pas ajouter `generateMetadata` ou `app/sitemap.ts` à un CMS séparé.
Avant de coder, lire les docs de la version Next.js installée dans `node_modules/next/dist/docs/` si présentes, sinon sa documentation officielle. L'API de cache/invalidation et ses signatures dépendent de la version ; les helpers Astro de la référence ne deviennent pas automatiquement des helpers Next.js.
## Métadonnées
- Réutiliser le contrat partagé de titre/origine/canonical et la chaîne de repli Payload, en adaptant leur emplacement au projet cible.- Définir l'origine publique depuis la variable réellement déclarée dans ce projet (par exemple NEXT_PUBLIC_SERVER_URL sur certains projets historiques). Ne pas renommer les variables du projet Astro pour suivre cet exemple.- Layout racine : metadataBase, langue HTML, favicon explorable et valeurs OG par défaut. Routes publiques : metadata statique ou generateMetadata selon la source ; ne pas dupliquer manuellement les balises gérées par Next.js.- Alternates : canonical publique, pagination conservée si nécessaire, langues réciproques pour les seuls contenus équivalents publiés/indexables, x-default si une langue par défaut existe.- Copier la logique de noIndex/preview/liste filtrée, y compris la coupure du JSON-LD. Les requêtes qui construisent les images de partage doivent peupler les médias et tailles nécessaires.- Vérifier la fusion des objets imbriqués openGraph/twitter dans les règles de la version installée : un override partiel de route doit conserver explicitement les images et champs par défaut voulus.
## Sitemap et robots
Choisir une seule source de vérité : route sitemap Next.js ou proxy de l'endpoint Payload. Reprendre les règles de publication, canonical, langues et pagination du contrat en vérifiant les limites documentées ; ne pas recopier les omissions de la référence comme exigences.
L'app publique sert robots selon l'environnement et la politique de crawlers du projet. Si le CMS possède une origine distincte, celle-ci reste bloquée séparément. Robots n'est pas une protection des routes administratives.
## Invalidation
Brancher les hooks de publication/suppression sur les APIs de cache de la version installée. Invalider le chemin courant, l'ancien chemin lors d'un renommage, les index concernés et le sitemap ; respecter skipCacheInvalidation. Vérifier explicitement la dépublication, le changement de navigation et les traductions. Ne pas réutiliser les appels de tags Astro comme s'ils avaient la même signature en Next.js.
## Vérification
Inspecter le HTML et les endpoints réels du projet cible : accueil, page, article, pagination, filtres, noIndex, preview ; publication, renommage, dépublication, suppression. Les tests d'un projet Astro sont des exemples de comportements, pas une suite exécutable telle quelle.CELUGA_SKILL_EOFmkdir -p "$dir/references"cat > "$dir/references/seo-contract.md" <<'CELUGA_SKILL_EOF'# Contrat SEO — Astro + Payload
## Sources dans la disposition cible
| Rôle | Fichier || --- | --- || Résolution des métadonnées | `packages/site-contracts/src/seo.ts` || Graphe JSON-LD, types proposés | `packages/site-contracts/src/schema.ts`, `schema-catalog.ts` || Registre public | `packages/site-contracts/src/collections.ts`, `public-content.ts` || Plugin SEO Payload | `apps/cms/src/payload.config.ts` || Assistant | `apps/cms/src/seo/` (`plugin.ts`, `server.ts`, `analyzer.ts`, `document.ts`, `serp.ts`, `useAnalysis.ts`) || Reprise des analyses | `apps/cms/src/scripts/seo-backfill.ts` || Redirections | `apps/cms/src/hooks/content.ts` || Robots du CMS | `apps/cms/src/app/robots[.]txt.ts` || Layout, middleware | `apps/web/src/layouts/SiteLayout.astro`, `apps/web/src/middleware.ts` || Index public | `apps/web/src/lib/public-index.ts` || Routes publiques | `apps/web/src/pages/robots.txt.ts`, `sitemap.xml.ts`, `llms.txt.ts` || Traductions | `apps/web/src/lib/cms.ts` (`translations`), `packages/site-contracts/src/i18n.ts` |
## Origines
| Variable | Utilisation || --- | --- || `PUBLIC_SITE_URL` | origine du site : canonical, sitemap, OG || `PUBLIC_PAYLOAD_URL` | origine du CMS || `PAYLOAD_INTERNAL_URL` | CMS joignable par Astro, jamais une canonical || `PUBLIC_ALLOW_INDEXING` | seule la chaîne `true` autorise l'indexation ; passe au build |
## Plugin et chaîne de repli
Plugin officiel sur les collections et globals du registre ayant la capacité `seo`, modules désactivés compris (le schéma reste commun), avec `noIndex` et `canonical` localisés. Les composants d'aperçu du plugin sont remplacés par ceux de l'assistant ; les chemins `meta.*` restent ceux du plugin.
`resolveMetadata({ document, configuration, siteURL, pathname, indexable })` :
| Champ | Chaîne || --- | --- || titre | `meta.title` → titre du document → SEO par défaut → nom du site ; suffixe ajouté sauf si le titre contient déjà ce nom en mots entiers || description | `meta.description` → extrait → SEO par défaut → description du site || image | `meta.image` → image du document → image par défaut → `/brand/og-default.webp` || social | Open Graph → valeurs SEO ; X → Open Graph || canonical | `meta.canonical` valide en http(s), sinon chemin sur l'origine || noIndex | `meta.noIndex` ou environnement non indexable |
## Layout
`SiteLayout.astro` émet titre, description, une meta robots, canonical, hreflang, favicon, OG, Twitter et un graphe JSON-LD. Pas de JSON-LD en preview ni sur une page d'erreur. Le fil d'Ariane structuré suit celui qui est affiché.
## Sitemap et index public
`publicIndex()` parcourt chaque langue active : l'accueil, puis les collections ayant la capacité `sitemap`, par pages de 100, `depth=0`, statut publié, `fallback-locale=none`. Il exclut traductions incomplètes, dates futures, `noIndex` et canoniques divergentes. Les listes et leurs pages suivantes sont ajoutées selon les documents réellement comptés.
`/sitemap.xml` : au-delà de 10 000 URL, index de sitemaps (`?part=N`, paramètre inclus dans la clé de cache). Cache de cinq minutes, tag `sitemap`, invalidé à chaque changement éditorial. Une nouvelle route statique publique doit y être ajoutée explicitement.
`/robots.txt` : `Allow: /` et sitemap si indexable, sinon `Disallow: /`. Une politique sur les crawlers d'entraînement est un choix par site : voir [GEO](geo.md).
`/llms.txt` : index Markdown construit depuis `publicIndex()`, mêmes URL que le sitemap, sans les pages suivantes des listes.
## Hreflang
`translations()` relit chaque autre langue en `depth=0`, statut publié, `fallback-locale=none`, et retient les versions traduites et publiées ; le layout filtre les `noIndex` et ajoute `x-default`. Les listes n'émettent d'alternates que sur leur première page sans filtre.
## Redirections
Un changement de slug publié crée ou met à jour une 301 dans la transaction ; le chargeur la lit avant le document et n'accepte qu'un chemin sûr. Un préfixe de collection renommé passe par `previousBases` (301 dans le middleware, langue et query conservées).
## Assistant
Onglet Référencement : **SEO**, **Lisibilité**, **Schema**, **Réseaux sociaux**, sur les chemins `meta.*`.
- Yoast **uniquement côté serveur** : le navigateur reçoit résultats et aperçus en JSON ; le moteur ne rejoint ni le bundle public ni celui de l'administration. Un script le vérifie après build (`verify-no-client-yoast`).- Recalcul après 300 ms d'inactivité, une seule requête, dédupliquée par empreinte ; résultats obsolètes ignorés. L'assistant n'écrit rien dans le formulaire : pas de boucle d'autosave.- Instantanés recalculés à la sauvegarde si l'empreinte change, locaux à chaque langue : scores, états, expression normalisée, conflits, version, date. Une panne de l'analyse conserve le contenu et marque l'instantané périmé.- Conflits : même expression normalisée, même langue, contenus publiés, indexables et traduits. Indique une concurrence possible, ne mesure pas la cannibalisation.- La publication reste possible quel que soit le score. Seuils indicatifs : 300 mots pour une page, 600 pour un article.- `seo:backfill` écrit des brouillons, par lots de 50, relançable, **sans rien publier**. Le lire avant de l'exécuter.- Traductions françaises du moteur (`seo/locales/fr.json`), à actualiser après une montée de `yoastseo` (`scripts/update-yoast-fr.mjs`).- Licence : le moteur déclare GPL-3.0. Toute remise d'un dépôt ou d'une image qui l'inclut conserve licence et notices.
Le bloc « Avant de publier » (`/api/publish-check`) signale liens cassés, images sans alternative et méta vides, sans bloquer.
## Favicon
Convention : carré ≥ 48 px, URL stable et explorable ; aucune validation de dimensions au téléversement. Google documente un minimum de 8×8, recommande plus de 48×48, et accepte BMP/GIF/ICO/PNG/JPEG/PPM/TIFF ; AVIF, WebP et SVG ne sont pas dans cette liste. [Documentation, vérifiée le 2026-10-17](https://developers.google.com/search/docs/appearance/favicon-in-search).
## IndexNow
Absent de la disposition cible. Sur un projet qui l'utilise, vérifier ses hooks : clé publique, envoi limité à la production, dépublication pas toujours notifiée. Aucune garantie de prise en compte.CELUGA_SKILL_EOFmkdir -p "$HOME/.claude/skills" "$HOME/.codex/skills"[ -e "$HOME/.claude/skills/celuga-seo" ] || ln -s "$dir" "$HOME/.claude/skills/celuga-seo"[ -e "$HOME/.codex/skills/celuga-seo" ] || ln -s "$dir" "$HOME/.codex/skills/celuga-seo"SKILL.md
Section intitulée « SKILL.md »---name: celuga-seodescription: SEO et GEO Celuga Payload + Astro. Use when changing metadata, canonical/noindex/hreflang, robots or AI crawler access, sitemap, llms.txt, JSON-LD, the CMS SEO assistant, or adding an indexable collection. Includes a separate Next.js adaptation reference.---
# SEO et GEO Celuga
Vérifier les sources du projet avant d'appliquer : les références décrivent la disposition cible et signalent ce qui varie.
| Référence | Quand la lire || --- | --- || [Contrat Astro/Payload](references/seo-contract.md) | métadonnées, origines, layout, sitemap, hreflang, assistant, tests || [JSON-LD](references/json-ld.md) | générateur de graphe et aperçu CMS || [GEO](references/geo.md) | règles externes vérifiées, crawlers, llms.txt, contenu et mesure || [Adaptation Next.js](references/nextjs.md) | seulement si le front public est Next.js |
Charger **celuga-media-react** pour la sélection des médias, OG et favicon.
## Points d'entrée
- `packages/site-contracts/src/seo.ts` : `resolveMetadata`, seule résolution, partagée par le site, le sitemap et l'aperçu CMS.- `packages/site-contracts/src/schema.ts` : `buildSchema`, un seul générateur de graphe pour le site et l'aperçu.- Registre public (`collections.ts`) : capacités `seo` et `sitemap`, type article, seuil de mots, champ de description, corps analysé.- `SiteLayout.astro` : émission centralisée des balises et du JSON-LD.- `apps/web/src/lib/public-index.ts` : URL publiées et auto-canoniques, source commune du sitemap et de `llms.txt`.- `apps/cms/src/seo/` : assistant éditorial ; le moteur Yoast reste côté serveur.
## Modifier le SEO
1. Lire le contrat et les sources. Garder les URL publiques distinctes des URL internes.2. Résoudre les champs à partir de données visibles, avec des médias peuplés. Ne rien inventer (auteur, date, avis, prix).3. Passer le résultat au layout existant. Pas de second script JSON-LD par composant.4. Nouvelle collection routée : descripteur (capacités, type article, seuil, description, corps, traduction), route par langue, tags d'invalidation, redirections. Tous les contenus Payload ne sont pas des pages à indexer.5. Nouveau bloc : adaptateur de `apps/cms/src/seo/document.ts`, fixture et test de parité avec le rendu public. Le rendu et l'analyse doivent parcourir les mêmes contenus visibles.
## Règles de rendu
| Situation | Comportement || --- | --- || `PUBLIC_ALLOW_INDEXING !== "true"` | meta noindex, `X-Robots-Tag` du middleware, `robots.txt` en `Disallow: /`, sans mention du sitemap || document `meta.noIndex` | `noindex, follow`, exclu du sitemap et des alternates || canonique personnalisée vers ailleurs | exclu du sitemap, sans ajouter sa cible || traduction incomplète | 404, absente des alternates et du sitemap || liste filtrée | `noIndex`, sans alternates || preview, réponse ≥ 400, route privée | `noindex, nofollow, noarchive`, `no-store` || domaine du CMS | `robots.txt` toujours en `Disallow: /` |
Hreflang seulement entre équivalents publiés, traduits et indexables, avec `x-default`.
## Vérifier
```shbun test tests/seo*.test.ts tests/sitemap.test.ts tests/llms.test.ts tests/schema*.test.ts```
Noms habituels, à confirmer dans le dépôt. Inspecter le HTML SSR de l'accueil, d'une page, d'un article, d'une liste filtrée, d'une page `noIndex` et de la preview selon la modification. Vérifier canonical, origines, robots, OG, favicon et graphe. Sur un environnement non indexable, un score SEO Lighthouse bloqué autour de 69 est attendu. Une réponse 200 à un user-agent simulé ne prouve pas l'accès d'un vrai crawler derrière un WAF.
Ne pas exécuter `seo:backfill`, soumettre un sitemap ni changer l'indexation d'un domaine pendant un simple audit. Pour une mise en production, utiliser **mep-celuga**.Fichiers annexes
Section intitulée « Fichiers annexes »agents/openai.yaml
Section intitulée « agents/openai.yaml »interface: display_name: "Celuga SEO & GEO" short_description: "SEO et visibilité IA : meta, crawlers, sitemap, JSON-LD, contenu citable" default_prompt: "Utilise $celuga-seo pour mettre en place ou auditer le SEO et la visibilité dans les moteurs génératifs de ce projet."references/geo.md
Section intitulée « references/geo.md »# GEO — contenu et accès des moteurs
Sources primaires vérifiées le **2026-10-17**. Recontrôler la documentation du fournisseur avant une décision qui dépend d'une politique actuelle. Les constats de code sont dans [seo-contract.md](seo-contract.md).
## Google et contenu citable
Google exige pour ses liens dans AI Overviews/AI Mode une page indexée et éligible au snippet ; les bonnes pratiques SEO ordinaires restent applicables. Aucun fichier IA ni balisage spécifique n'est nécessaire. `llms.txt` n'est pas une condition de visibilité : il peut être exposé comme index public à moindre coût (généré depuis les mêmes URL que le sitemap), sans lui prêter d'effet sur le classement. L'éligibilité ne garantit ni indexation ni citation. Les données IA de Search sont incluses dans le rapport Performance, type Web ; ne pas promettre un rapport IA séparé ou un contrôle Search Console non vérifié. [Guide officiel](https://developers.google.com/search/docs/appearance/ai-features).
Recommandations éditoriales Celuga, à appliquer selon le contenu demandé :
- Sujet, public et zone d'intervention clairs tôt dans la page ; titres utiles, tableaux/listes HTML pour les comparaisons et étapes.- Informations propres à l'entreprise : cas clients, chiffres contextualisés et datés, expérience de première main ; sources liées pour les affirmations externes.- Auteur réel quand pertinent, date de publication/mise à jour exacte et visible, données structurées cohérentes avec le texte ; ne pas rafraîchir artificiellement les dates.- Nom/adresse/téléphone/profils officiels cohérents sur les pages et les sources de l'entreprise. Ne pas publier des horaires ou certifications déduits.- Texte essentiel présent dans le HTML serveur ; image originale descriptive et image de partage représentative. Ne pas supposer que chaque moteur exécute le JavaScript client.
Ce sont des critères de qualité et de vérifiabilité, pas un classement d'impact mesuré ni une garantie de citation. Ne pas supposer qu'une fiche auteur complète existe.
## Crawlers et politique du projet
Le socle autorise tous les robots quand l'indexation est active. Bloquer les crawlers d'entraînement (GPTBot, ClaudeBot, Google-Extended, Applebot-Extended, CCBot, meta-externalagent, Bytespider) est une décision par site, à prendre avec le client : lire le `robots.txt` du projet avant de la supposer. Ne jamais y mêler les crawlers de recherche.
| Fournisseur | Recherche | Requête utilisateur | Modèles / autre contrôle || --- | --- | --- | --- || Google | Googlebot | selon produit | Google-Extended, distinct de Search || OpenAI | OAI-SearchBot | ChatGPT-User | GPTBot || Anthropic | Claude-SearchBot | Claude-User | ClaudeBot || Perplexity | PerplexityBot | Perplexity-User | PerplexityBot n'est pas déclaré comme crawler d'entraînement |
Les usages sont indépendants ; un refus du crawler de modèles ne doit pas être copié aveuglément sur le crawler de recherche. OpenAI documente environ 24h pour l'ajustement des règles de recherche ; ses requêtes utilisateur peuvent ne pas respecter robots.txt. [Documentation OpenAI](https://developers.openai.com/api/docs/bots).
Anthropic distingue les trois agents et indique qu'ils respectent robots.txt. [Documentation Anthropic](https://support.anthropic.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler).
Perplexity distingue indexation/recherche et requêtes utilisateur ; Perplexity-User ignore généralement robots.txt. Vérifier IP et user-agent ensemble si une règle WAF exige l'identité du bot. [Documentation Perplexity](https://docs.perplexity.ai/docs/resources/perplexity-crawlers).
Googlebot gouverne Search ; Google-Extended contrôle certains usages d'entraînement et d'ancrage hors Search, sans effet sur l'inclusion/classement Search. Pour limiter l'extrait Search, Google documente nosnippet/data-nosnippet/max-snippet/noindex. [Crawlers Google](https://developers.google.com/crawling/docs/crawlers-fetchers/google-common-crawlers), [contrôles Search](https://developers.google.com/search/docs/appearance/ai-features).
Robots.txt n'est pas un contrôle d'accès. Un curl avec user-agent simulé teste une réponse, pas l'identité ni l'accès réel d'un crawler. Pour un audit de visibilité, contrôler directives, HTML SSR, configuration WAF/CDN et journaux ; ne pas désactiver une protection globale sans besoin démontré.
## Découverte et mesure
Sitemap et `llms.txt` signalent des URL sans garantir leur indexation ; leur comportement est dans le contrat. Soumission du sitemap et configuration des propriétés Search Console/Bing restent des actions externes distinctes.
Suivre selon les outils déjà disponibles : Performance Search Console, visites référées par les assistants, conversions, logs des crawlers et réponses HTTP. Vérifier dans la documentation/console du fournisseur l'existence et la portée d'un éventuel rapport IA avant de le recommander. Une visite référée et une citation sans clic sont des mesures différentes.
Un panel stable de questions clients peut servir à observer présence, exactitude et URL citée ; consigner date, formulation et moteur. Corriger les informations fausses d'abord dans les sources publiques réelles de l'entreprise.references/json-ld.md
Section intitulée « references/json-ld.md »# JSON-LD public et aperçu CMS
## Contrat
- `buildSchema` (`packages/site-contracts/src/schema.ts`) génère **un seul** graphe `@context` + `@graph`, pour le site comme pour l'aperçu de l'assistant : changer l'un change l'autre.- Graphe relié : identité (organisation, personne, établissement…), WebSite, WebPage, ImageObject, BreadcrumbList, article éventuel, sujet principal. `Question`/`Answer` pour les blocs FAQ complets.- Sérialisation échappant `<` (`<`). Pas de sortie en preview ni sur une page d'erreur.- Seulement des données réelles et visibles. Une propriété absente reste absente : ni avis, ni prix, ni note fabriqués. Scores et densité de mots clés ne sont pas des faits à exporter.- `@id` et `@type` automatiques protégés contre les propriétés libres.
## Réglages éditoriaux
`Configuration > Référencement > Schema.org` définit l'identité et les types par défaut. Dans chaque document, l'onglet Schema choisit le type de page ou d'article, l'auteur, le titre du fil d'Ariane, le sujet principal et des propriétés complémentaires en JSON. `schema-catalog.ts` liste les types proposés ; un autre type officiel peut être saisi.
Une sélection de type n'est pas une validation sémantique. Pour `QAPage` et `ProfilePage`, `mainEntity` doit être renseigné : ces types ne se déduisent pas d'un bloc FAQ.
## FAQ et fonctionnalités des moteurs
`FAQPage` et `Question` décrivent des questions visibles. Google a retiré l'affichage FAQ rich result à partir du **7 mai 2026** : ne pas le promettre. [Journal officiel, vérifié le 2026-10-17](https://developers.google.com/search/updates).
Pour un nouveau type, lire sa documentation officielle au moment de l'intégration. Un graphe valide n'est pas une garantie d'éligibilité ni d'affichage. Règles IA : [GEO](geo.md).
## Ajouter ou modifier
1. Identifier les champs Payload sources et les données réellement visibles.2. Modifier `buildSchema` ou son catalogue, jamais un composant.3. Pour un nouveau bloc qui alimente le graphe, vérifier qu'il reçoit ses données préparées (par exemple une FAQ alimentée par une collection) sur la page publique et en preview.4. Tests du dépôt, puis [validator.schema.org](https://validator.schema.org/) ; le [Rich Results Test](https://search.google.com/test/rich-results) seulement pour ce qu'il couvre.
Inspection au navigateur :
```jsJSON.parse(document.querySelector('script[type="application/ld+json"]').textContent)```references/nextjs.md
Section intitulée « references/nextjs.md »# Adaptation Next.js single-app
Lire ce fichier uniquement pour un projet Celuga dont le front public et Payload partagent Next.js. **Ce n'est pas l'architecture cible**, où le front public est Astro. Ne pas ajouter `generateMetadata` ou `app/sitemap.ts` à un CMS séparé.
Avant de coder, lire les docs de la version Next.js installée dans `node_modules/next/dist/docs/` si présentes, sinon sa documentation officielle. L'API de cache/invalidation et ses signatures dépendent de la version ; les helpers Astro de la référence ne deviennent pas automatiquement des helpers Next.js.
## Métadonnées
- Réutiliser le contrat partagé de titre/origine/canonical et la chaîne de repli Payload, en adaptant leur emplacement au projet cible.- Définir l'origine publique depuis la variable réellement déclarée dans ce projet (par exemple NEXT_PUBLIC_SERVER_URL sur certains projets historiques). Ne pas renommer les variables du projet Astro pour suivre cet exemple.- Layout racine : metadataBase, langue HTML, favicon explorable et valeurs OG par défaut. Routes publiques : metadata statique ou generateMetadata selon la source ; ne pas dupliquer manuellement les balises gérées par Next.js.- Alternates : canonical publique, pagination conservée si nécessaire, langues réciproques pour les seuls contenus équivalents publiés/indexables, x-default si une langue par défaut existe.- Copier la logique de noIndex/preview/liste filtrée, y compris la coupure du JSON-LD. Les requêtes qui construisent les images de partage doivent peupler les médias et tailles nécessaires.- Vérifier la fusion des objets imbriqués openGraph/twitter dans les règles de la version installée : un override partiel de route doit conserver explicitement les images et champs par défaut voulus.
## Sitemap et robots
Choisir une seule source de vérité : route sitemap Next.js ou proxy de l'endpoint Payload. Reprendre les règles de publication, canonical, langues et pagination du contrat en vérifiant les limites documentées ; ne pas recopier les omissions de la référence comme exigences.
L'app publique sert robots selon l'environnement et la politique de crawlers du projet. Si le CMS possède une origine distincte, celle-ci reste bloquée séparément. Robots n'est pas une protection des routes administratives.
## Invalidation
Brancher les hooks de publication/suppression sur les APIs de cache de la version installée. Invalider le chemin courant, l'ancien chemin lors d'un renommage, les index concernés et le sitemap ; respecter skipCacheInvalidation. Vérifier explicitement la dépublication, le changement de navigation et les traductions. Ne pas réutiliser les appels de tags Astro comme s'ils avaient la même signature en Next.js.
## Vérification
Inspecter le HTML et les endpoints réels du projet cible : accueil, page, article, pagination, filtres, noIndex, preview ; publication, renommage, dépublication, suppression. Les tests d'un projet Astro sont des exemples de comportements, pas une suite exécutable telle quelle.references/seo-contract.md
Section intitulée « references/seo-contract.md »# Contrat SEO — Astro + Payload
## Sources dans la disposition cible
| Rôle | Fichier || --- | --- || Résolution des métadonnées | `packages/site-contracts/src/seo.ts` || Graphe JSON-LD, types proposés | `packages/site-contracts/src/schema.ts`, `schema-catalog.ts` || Registre public | `packages/site-contracts/src/collections.ts`, `public-content.ts` || Plugin SEO Payload | `apps/cms/src/payload.config.ts` || Assistant | `apps/cms/src/seo/` (`plugin.ts`, `server.ts`, `analyzer.ts`, `document.ts`, `serp.ts`, `useAnalysis.ts`) || Reprise des analyses | `apps/cms/src/scripts/seo-backfill.ts` || Redirections | `apps/cms/src/hooks/content.ts` || Robots du CMS | `apps/cms/src/app/robots[.]txt.ts` || Layout, middleware | `apps/web/src/layouts/SiteLayout.astro`, `apps/web/src/middleware.ts` || Index public | `apps/web/src/lib/public-index.ts` || Routes publiques | `apps/web/src/pages/robots.txt.ts`, `sitemap.xml.ts`, `llms.txt.ts` || Traductions | `apps/web/src/lib/cms.ts` (`translations`), `packages/site-contracts/src/i18n.ts` |
## Origines
| Variable | Utilisation || --- | --- || `PUBLIC_SITE_URL` | origine du site : canonical, sitemap, OG || `PUBLIC_PAYLOAD_URL` | origine du CMS || `PAYLOAD_INTERNAL_URL` | CMS joignable par Astro, jamais une canonical || `PUBLIC_ALLOW_INDEXING` | seule la chaîne `true` autorise l'indexation ; passe au build |
## Plugin et chaîne de repli
Plugin officiel sur les collections et globals du registre ayant la capacité `seo`, modules désactivés compris (le schéma reste commun), avec `noIndex` et `canonical` localisés. Les composants d'aperçu du plugin sont remplacés par ceux de l'assistant ; les chemins `meta.*` restent ceux du plugin.
`resolveMetadata({ document, configuration, siteURL, pathname, indexable })` :
| Champ | Chaîne || --- | --- || titre | `meta.title` → titre du document → SEO par défaut → nom du site ; suffixe ajouté sauf si le titre contient déjà ce nom en mots entiers || description | `meta.description` → extrait → SEO par défaut → description du site || image | `meta.image` → image du document → image par défaut → `/brand/og-default.webp` || social | Open Graph → valeurs SEO ; X → Open Graph || canonical | `meta.canonical` valide en http(s), sinon chemin sur l'origine || noIndex | `meta.noIndex` ou environnement non indexable |
## Layout
`SiteLayout.astro` émet titre, description, une meta robots, canonical, hreflang, favicon, OG, Twitter et un graphe JSON-LD. Pas de JSON-LD en preview ni sur une page d'erreur. Le fil d'Ariane structuré suit celui qui est affiché.
## Sitemap et index public
`publicIndex()` parcourt chaque langue active : l'accueil, puis les collections ayant la capacité `sitemap`, par pages de 100, `depth=0`, statut publié, `fallback-locale=none`. Il exclut traductions incomplètes, dates futures, `noIndex` et canoniques divergentes. Les listes et leurs pages suivantes sont ajoutées selon les documents réellement comptés.
`/sitemap.xml` : au-delà de 10 000 URL, index de sitemaps (`?part=N`, paramètre inclus dans la clé de cache). Cache de cinq minutes, tag `sitemap`, invalidé à chaque changement éditorial. Une nouvelle route statique publique doit y être ajoutée explicitement.
`/robots.txt` : `Allow: /` et sitemap si indexable, sinon `Disallow: /`. Une politique sur les crawlers d'entraînement est un choix par site : voir [GEO](geo.md).
`/llms.txt` : index Markdown construit depuis `publicIndex()`, mêmes URL que le sitemap, sans les pages suivantes des listes.
## Hreflang
`translations()` relit chaque autre langue en `depth=0`, statut publié, `fallback-locale=none`, et retient les versions traduites et publiées ; le layout filtre les `noIndex` et ajoute `x-default`. Les listes n'émettent d'alternates que sur leur première page sans filtre.
## Redirections
Un changement de slug publié crée ou met à jour une 301 dans la transaction ; le chargeur la lit avant le document et n'accepte qu'un chemin sûr. Un préfixe de collection renommé passe par `previousBases` (301 dans le middleware, langue et query conservées).
## Assistant
Onglet Référencement : **SEO**, **Lisibilité**, **Schema**, **Réseaux sociaux**, sur les chemins `meta.*`.
- Yoast **uniquement côté serveur** : le navigateur reçoit résultats et aperçus en JSON ; le moteur ne rejoint ni le bundle public ni celui de l'administration. Un script le vérifie après build (`verify-no-client-yoast`).- Recalcul après 300 ms d'inactivité, une seule requête, dédupliquée par empreinte ; résultats obsolètes ignorés. L'assistant n'écrit rien dans le formulaire : pas de boucle d'autosave.- Instantanés recalculés à la sauvegarde si l'empreinte change, locaux à chaque langue : scores, états, expression normalisée, conflits, version, date. Une panne de l'analyse conserve le contenu et marque l'instantané périmé.- Conflits : même expression normalisée, même langue, contenus publiés, indexables et traduits. Indique une concurrence possible, ne mesure pas la cannibalisation.- La publication reste possible quel que soit le score. Seuils indicatifs : 300 mots pour une page, 600 pour un article.- `seo:backfill` écrit des brouillons, par lots de 50, relançable, **sans rien publier**. Le lire avant de l'exécuter.- Traductions françaises du moteur (`seo/locales/fr.json`), à actualiser après une montée de `yoastseo` (`scripts/update-yoast-fr.mjs`).- Licence : le moteur déclare GPL-3.0. Toute remise d'un dépôt ou d'une image qui l'inclut conserve licence et notices.
Le bloc « Avant de publier » (`/api/publish-check`) signale liens cassés, images sans alternative et méta vides, sans bloquer.
## Favicon
Convention : carré ≥ 48 px, URL stable et explorable ; aucune validation de dimensions au téléversement. Google documente un minimum de 8×8, recommande plus de 48×48, et accepte BMP/GIF/ICO/PNG/JPEG/PPM/TIFF ; AVIF, WebP et SVG ne sont pas dans cette liste. [Documentation, vérifiée le 2026-10-17](https://developers.google.com/search/docs/appearance/favicon-in-search).
## IndexNow
Absent de la disposition cible. Sur un projet qui l'utilise, vérifier ses hooks : clé publique, envoi limité à la production, dépublication pas toujours notifiée. Aucune garantie de prise en compte.

