docs(07): plan SEO blog — 4 plans (schema-org, useSeoMeta enrich, sitemap Nitro) .planning/phases/07-seo-blog/07-01-PLAN.md .planning/phases/07-seo-blog/07-02-PLAN.md .planning/phases/07-seo-blog/07-03-PLAN.md .planning/phases/07-seo-blog/07-04-PLAN.md .planning/ROADMAP.md
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
---
|
||||
phase: 07-seo-blog
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: [07-01]
|
||||
files_modified:
|
||||
- app/pages/blog/index.vue
|
||||
autonomous: true
|
||||
requirements: [SEO-10, SEO-13, SEO-15]
|
||||
must_haves:
|
||||
truths:
|
||||
- "curl /fr/blog et /en/blog retournent og:image absolu = https://killiandalcin.fr/og-blog-default.jpg"
|
||||
- "og:locale = fr_FR (ou en_US) et og:locale:alternate = en_US (ou fr_FR) — le listing existe toujours dans les 2 langues"
|
||||
- "Le HTML contient un JSON-LD @type: CollectionPage (via defineWebPage) pour le listing"
|
||||
- "Le HTML contient un JSON-LD BreadcrumbList Accueil → Blog"
|
||||
artifacts:
|
||||
- path: "app/pages/blog/index.vue"
|
||||
provides: "useSeoMeta enrichi (D-16) + useSchemaOrg CollectionPage + Breadcrumb"
|
||||
contains: "defineWebPage"
|
||||
key_links:
|
||||
- from: "app/pages/blog/index.vue"
|
||||
to: "app/utils/resolve-og-image.ts"
|
||||
via: "import resolveOgImage"
|
||||
pattern: "resolveOgImage"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Enrichir la page listing `/blog` avec (a) `useSeoMeta` étendu (D-16 — og:image fallback, og:locale, og:locale:alternate, twitter), et (b) `useSchemaOrg([defineWebPage({ '@type': 'CollectionPage' }), defineBreadcrumb])` (D-03, SEO-15).
|
||||
|
||||
Purpose: Le listing doit être partageable socialement (card OG branded) et porter un breadcrumb JSON-LD cohérent avec les articles.
|
||||
Output: 1 page enrichie.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/07-seo-blog/07-CONTEXT.md
|
||||
@.planning/phases/07-seo-blog/07-RESEARCH.md
|
||||
@.planning/phases/07-seo-blog/07-PATTERNS.md
|
||||
@.planning/phases/07-seo-blog/07-01-SUMMARY.md
|
||||
@app/pages/blog/index.vue
|
||||
|
||||
<interfaces>
|
||||
Depuis `app/pages/blog/index.vue` (existant, à étendre — ne PAS remplacer) :
|
||||
- `const { t, locale } = useI18n()` (ligne 2)
|
||||
- `const localePath = useLocalePath()` (ligne 3)
|
||||
- `const isFr = computed(() => locale.value === 'fr')` (ligne 4)
|
||||
- `useSeoMeta({ title, description, ogTitle, ogDescription, ogType: 'website' })` (lignes 37-43) — à ÉTENDRE
|
||||
|
||||
Auto-imports : `useSchemaOrg`, `defineWebPage`, `defineBreadcrumb`.
|
||||
|
||||
`resolveOgImage(null)` retourne `https://killiandalcin.fr/og-blog-default.jpg` (fallback, D-06).
|
||||
|
||||
**Note**: `app/utils/resolve-og-image.ts` est créé dans 07-02 (Wave 2, parallèle). Plan 07-03 a DÉJÀ une dépendance implicite (runtime) sur ce fichier : si 07-03 exécute avant 07-02, `import { resolveOgImage }` échouera. L'exécuteur DOIT lancer 07-02 d'abord OU créer provisoirement le helper ici. **Recommandation** : exécuteur vérifie `test -f app/utils/resolve-og-image.ts` et, si absent, utilise la constante littérale `const OG_FALLBACK = 'https://killiandalcin.fr/og-blog-default.jpg'` en dur dans ce fichier (évite le couplage). Plan 07-02 n'écrit QUE `[slug].vue` + utils, donc pas de conflit de fichier.
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Enrichir app/pages/blog/index.vue — useSeoMeta D-16 + useSchemaOrg CollectionPage + Breadcrumb</name>
|
||||
<files>app/pages/blog/index.vue</files>
|
||||
<read_first>
|
||||
- app/pages/blog/index.vue (fichier entier 1-151)
|
||||
- .planning/phases/07-seo-blog/07-RESEARCH.md §Open Question #1 (CollectionPage via defineWebPage), §useSeoMeta Enrichment
|
||||
- .planning/phases/07-seo-blog/07-PATTERNS.md §index.vue (modify)
|
||||
- .planning/phases/07-seo-blog/07-CONTEXT.md D-03, D-16
|
||||
</read_first>
|
||||
<action>
|
||||
Dans `app/pages/blog/index.vue`, zone `<script setup lang="ts">` uniquement.
|
||||
|
||||
1. **Import** — tout en haut du script :
|
||||
```ts
|
||||
import { resolveOgImage } from '~/utils/resolve-og-image'
|
||||
```
|
||||
|
||||
2. **Computeds SEO** — après la constante `totalLanguages = 2` (ligne 34), avant `useSeoMeta` :
|
||||
```ts
|
||||
const SITE_URL = 'https://killiandalcin.fr'
|
||||
const ogImage = resolveOgImage(null) // fallback absolute URL (D-16)
|
||||
const canonicalUrl = computed(() => `${SITE_URL}${localePath('/blog')}`)
|
||||
```
|
||||
|
||||
3. **Remplacer** le `useSeoMeta({...})` existant (lignes 37-43) par la version enrichie D-16 :
|
||||
```ts
|
||||
useSeoMeta({
|
||||
title: () => t('blog.title'),
|
||||
description: () => t('blog.subtitle'),
|
||||
ogTitle: () => t('blog.title'),
|
||||
ogDescription: () => t('blog.subtitle'),
|
||||
ogType: 'website',
|
||||
ogImage,
|
||||
ogUrl: canonicalUrl,
|
||||
ogLocale: () => (isFr.value ? 'fr_FR' : 'en_US'),
|
||||
ogLocaleAlternate: () => [isFr.value ? 'en_US' : 'fr_FR'],
|
||||
twitterCard: 'summary_large_image',
|
||||
twitterImage: ogImage,
|
||||
})
|
||||
```
|
||||
|
||||
4. **Ajouter** après `useSeoMeta(...)` :
|
||||
```ts
|
||||
useSchemaOrg([
|
||||
defineWebPage({
|
||||
'@type': 'CollectionPage',
|
||||
name: () => t('blog.title'),
|
||||
description: () => t('blog.subtitle'),
|
||||
inLanguage: () => (isFr.value ? 'fr-FR' : 'en-US'),
|
||||
url: canonicalUrl,
|
||||
}),
|
||||
defineBreadcrumb({
|
||||
itemListElement: [
|
||||
{ name: () => t('blog.breadcrumb.home'), item: () => localePath('/') },
|
||||
{ name: () => t('blog.breadcrumb.blog'), item: () => localePath('/blog') },
|
||||
],
|
||||
}),
|
||||
])
|
||||
```
|
||||
Ne PAS toucher aux computeds `totalArticles`, `uniqueTags`, `totalLanguages`, au `useAsyncData`, ni au `<template>`.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>grep -q "defineWebPage" app/pages/blog/index.vue && grep -q "defineBreadcrumb" app/pages/blog/index.vue && grep -q "resolveOgImage" app/pages/blog/index.vue && grep -q "ogLocaleAlternate" app/pages/blog/index.vue && pnpm typecheck && pnpm dev --port 3000 & sleep 12 && curl -s http://localhost:3000/fr/blog | tee /tmp/blog.html | grep -q 'property="og:image".*og-blog-default.jpg' && grep -q '"@type":"CollectionPage"' /tmp/blog.html && grep -q '"@type":"BreadcrumbList"' /tmp/blog.html && curl -s http://localhost:3000/en/blog | grep -q 'property="og:locale" content="en_US"' && kill %1</automated>
|
||||
</verify>
|
||||
<done>curl /fr/blog et /en/blog retournent og:image pointant vers og-blog-default.jpg absolu, og:locale correct, JSON-LD CollectionPage + BreadcrumbList. typecheck vert.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| (aucune nouvelle) | Rien de user-input ; i18n strings déjà trustées |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-07-05 | Information Disclosure | JSON-LD listing (URLs publiques) | accept | Par design — le listing doit être crawlable |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- og:image listing : `curl /fr/blog | grep 'og-blog-default.jpg'`
|
||||
- og:locale correct : `curl /en/blog | grep 'content="en_US"'`
|
||||
- JSON-LD CollectionPage : `curl /fr/blog | grep '"@type":"CollectionPage"'`
|
||||
- JSON-LD Breadcrumb : `curl /fr/blog | grep '"@type":"BreadcrumbList"'`
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
1. SEO-10 étendu : og:title, og:description, og:image distincts du site par défaut
|
||||
2. SEO-13 : og:image = `/og-blog-default.jpg` absolu (jamais `og-image.png`)
|
||||
3. SEO-15 : BreadcrumbList Accueil → Blog présent sur le listing
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
Après complétion, créer `.planning/phases/07-seo-blog/07-03-SUMMARY.md`.
|
||||
</output>
|
||||
Reference in New Issue
Block a user