From 0b1152c8a1a7aa55822e081027b2ae8b1fb66214 Mon Sep 17 00:00:00 2001 From: kayjaydee Date: Wed, 22 Apr 2026 11:25:57 +0200 Subject: [PATCH] docs(07): mark Phase 7 complete in ROADMAP (4/4 plans) .planning/ROADMAP.md .planning/phases/07-seo-blog/07-VERIFICATION.md --- .planning/ROADMAP.md | 6 +- .../phases/07-seo-blog/07-VERIFICATION.md | 101 ++++++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 .planning/phases/07-seo-blog/07-VERIFICATION.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index b8ebf34..2bd01f7 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -108,8 +108,8 @@ Plans: ## Phases (M1.1) - [x] **Phase 5: @nuxt/content Setup & Renderer** - Integration @nuxt/content, markdown renderer complet avec syntax highlighting et images — Completed 2026-04-22 (2/2 plans) -- [ ] **Phase 6: Blog Pages** - Page listing /blog et page article /blog/[slug] SSR, bilingue, avec TOC et nav prev/next -- [ ] **Phase 7: SEO Blog** - useSeoMeta par article, JSON-LD Article, sitemap etendu, og:image, BreadcrumbList +- [x] **Phase 6: Blog Pages** - Page listing /blog et page article /blog/[slug] SSR, bilingue, avec TOC et nav prev/next — Completed 2026-04-22 (4/4 plans) +- [x] **Phase 7: SEO Blog** - useSeoMeta par article, JSON-LD Article, sitemap etendu, og:image, BreadcrumbList — Completed 2026-04-22 (4/4 plans) - [ ] **Phase 8: Content & Cocon Semantique** - 2 articles seed Hytale, liens internes blog-hytale --- @@ -191,5 +191,5 @@ Plans: |-------|----------------|--------|-----------| | 5. @nuxt/content Setup & Renderer | 2/2 | Complete | 2026-04-22 | | 6. Blog Pages | 4/4 | Complete | 2026-04-22 | -| 7. SEO Blog | 0/? | Not started | - | +| 7. SEO Blog | 4/4 | Complete | 2026-04-22 | | 8. Content & Cocon Semantique | 0/? | Not started | - | diff --git a/.planning/phases/07-seo-blog/07-VERIFICATION.md b/.planning/phases/07-seo-blog/07-VERIFICATION.md new file mode 100644 index 0000000..fd62744 --- /dev/null +++ b/.planning/phases/07-seo-blog/07-VERIFICATION.md @@ -0,0 +1,101 @@ +--- +phase: 07-seo-blog +verified: 2026-04-22T00:00:00Z +status: human_needed +score: 8/8 must-haves verified (static) +overrides_applied: 0 +human_verification: + - test: "Boot dev server (pnpm dev) and curl http://localhost:3000/fr/blog/{slug}" + expected: "HTML contains og:image absolute https://..., article:published_time, JSON-LD Article (author @id=#killian), JSON-LD BreadcrumbList" + why_human: "Static grep confirms source emits correct calls; runtime SSR output requires a live server (not booted during verification per curl-optional instructions)" + - test: "curl http://localhost:3000/sitemap.xml" + expected: "Contains /fr/blog/ and /en/blog/ entries, xhtml:link hreflang fr/en/x-default for bilingual pairs, no draft slugs (e.g. test-kotlin-syntax absent)" + why_human: "Sitemap XML generation combines @nuxtjs/sitemap merging + Nitro endpoint — only a running server can confirm the final merged XML" + - test: "Visual/social validation of /og-blog-default.jpg" + expected: "1200x630 branded fallback image renders correctly on Twitter/LinkedIn/Facebook sharing debuggers" + why_human: "Placeholder accepted as deferred design; final branding is a UX judgment" + - test: "pnpm typecheck" + expected: "exit 0" + why_human: "Quality signal declared as optional in verification context; requires local run" +--- + +# Phase 07: SEO Blog — Verification Report + +**Phase Goal:** Chaque page blog indexable avec meta tags complets, JSON-LD Article+BreadcrumbList+Blog/CollectionPage, sitemap avec alternates hreflang. Validation curl (SSR pur). + +**Status:** human_needed (static verification complete; runtime curl + typecheck require live server) + +## Goal Achievement — Observable Truths + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| 1 | [slug].vue emits useSchemaOrg([defineArticle, defineBreadcrumb]) + useSeoMeta D-15 | ✓ VERIFIED | `app/pages/blog/[slug].vue` lines 113-149 — defineArticle, defineBreadcrumb, articlePublishedTime, articleModifiedTime, ogLocaleAlternate, ogImage, canonicalUrl all present | +| 2 | blog/index.vue emits defineWebPage(CollectionPage) + defineBreadcrumb | ✓ VERIFIED | `app/pages/blog/index.vue` lines 57-71 — `'@type': 'CollectionPage'` and defineBreadcrumb present | +| 3 | Sitemap endpoint filters draft=false + emits hreflang fr/en/x-default | ✓ VERIFIED | `server/api/__sitemap__/urls.ts` lines 22,28 (`.where('draft', '=', false)`), lines 54-56 (fr/en/x-default alternates) | +| 4 | nuxt.config.ts has sitemap.sources + nuxt-schema-org module | ✓ VERIFIED | `nuxt.config.ts` line 12 (`'nuxt-schema-org'`), lines 35-37 (`sitemap.sources: ['/api/__sitemap__/urls']`) | +| 5 | app/app.vue uses useSchemaOrg(definePerson + defineWebSite) | ✓ VERIFIED | `app/app.vue` lines 13-19 | +| 6 | public/og-blog-default.jpg exists | ✓ VERIFIED | File present (placeholder accepted, deferred design noted in 07-02 SUMMARY) | +| 7 | content.config.ts schema blog_fr/blog_en contains `updated` optional | ✓ VERIFIED | `content.config.ts` line 7 — `updated: z.string().optional(),` applied to shared blogSchema used by both collections | +| 8 | package.json has nuxt-schema-org ^6.0.4 | ✓ VERIFIED | `package.json` line 32 | + +**Static Score:** 8/8 + +## Required Artifacts + +| Artifact | Status | Details | +|----------|--------|---------| +| `app/utils/seo-person.ts` | ✓ VERIFIED | exports KILLIAN_PERSON_ID + killianPerson; derived from siteConfig | +| `app/utils/resolve-og-image.ts` | ✓ VERIFIED | exports resolveOgImage returning absolute URL with /og-blog-default.jpg fallback | +| `public/og-blog-default.jpg` | ✓ VERIFIED | File exists (placeholder) | +| `server/api/__sitemap__/urls.ts` | ✓ VERIFIED | defineSitemapEventHandler with bilingual pair detection | +| `app/pages/blog/[slug].vue` | ✓ VERIFIED | Enriched with useSeoMeta D-15 + useSchemaOrg([defineArticle, defineBreadcrumb]) | +| `app/pages/blog/index.vue` | ✓ VERIFIED | Enriched with useSeoMeta D-16 + useSchemaOrg([defineWebPage, defineBreadcrumb]) | +| `app/app.vue` | ✓ VERIFIED | Global useSchemaOrg definePerson + defineWebSite | +| `nuxt.config.ts` | ✓ VERIFIED | nuxt-schema-org module + sitemap.sources wired | +| `content.config.ts` | ✓ VERIFIED | `updated` field added | + +## Key Link Verification + +| From | To | Via | Status | +|------|----|----|--------| +| app/app.vue | app/utils/seo-person.ts | `import { killianPerson }` | ✓ WIRED | +| nuxt.config.ts | /api/__sitemap__/urls | sitemap.sources | ✓ WIRED | +| app/pages/blog/[slug].vue | app/utils/resolve-og-image.ts | `import { resolveOgImage }` | ✓ WIRED | +| [slug].vue defineArticle.author | app.vue definePerson | `'@id': KILLIAN_PERSON_ID` | ✓ WIRED | +| blog/index.vue | OG fallback | hardcoded constant (07-03 independence note documented in plan) | ✓ WIRED (intentional deviation from resolveOgImage import — plan 07-03 explicitly permits this) | + +## Requirements Coverage + +| Requirement | Status | Evidence | +|-------------|--------|----------| +| SEO-10 (unique og meta per article) | ✓ SATISFIED | useSeoMeta D-15 in [slug].vue with arrow-fn reactive ogTitle/ogDescription/ogImage | +| SEO-11 (JSON-LD Article) | ✓ SATISFIED | defineArticle with headline, datePublished, dateModified, author/publisher @id | +| SEO-12 (sitemap with hreflang alternates) | ✓ SATISFIED | urls.ts emits fr/en/x-default for bilingual pairs; draft filter applied | +| SEO-13 (og:image fallback branded) | ✓ SATISFIED | resolveOgImage helper + /og-blog-default.jpg fallback + absolute URL always | +| SEO-15 (JSON-LD BreadcrumbList) | ✓ SATISFIED | defineBreadcrumb on both [slug].vue (3-level) and index.vue (2-level) | + +## Anti-Patterns Scan + +No blockers. Minor notes: +- `app/pages/blog/index.vue` uses hardcoded `OG_FALLBACK` constant instead of `resolveOgImage(null)` — explicitly documented in 07-03 PLAN as acceptable Wave-2 decoupling; not a stub. +- `inLanguageTag` in [slug].vue uses `as unknown as ComputedRef<'fr-FR'>` cast — documented type-narrowing for defineArticle; not a smell. + +## Gaps Summary + +No structural gaps. All 8 must-haves satisfied by static inspection of code + config + artifacts. Goal-backward chain is complete: + +Goal (blog indexable with meta + JSON-LD + sitemap hreflang) +→ requires [slug].vue emits Article + Breadcrumb + D-15 meta ✓ +→ requires blog/index.vue emits CollectionPage + Breadcrumb ✓ +→ requires dynamic sitemap with bilingual alternates + draft exclusion ✓ +→ requires global Person/@id identity ✓ +→ requires module + schema extension + fallback asset ✓ + +All wiring verified (imports, @id references, sitemap.sources → endpoint). + +**Outstanding:** Runtime validation (curl against live dev server) + `pnpm typecheck` are the last-mile confirmations. These were explicitly marked optional in the verification context ("preferably curl/grep, pas de dev server boot obligatoire si vérification statique suffit"). Static verification suffices for structural goal achievement; runtime validation is routed to human for final sign-off. + +--- + +_Verified: 2026-04-22_ +_Verifier: Claude (gsd-verifier)_