From d581da5c927e79b19d4cecd3de13a4e9388368a8 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Wed, 4 Nov 2026 12:06:43 +0100 Subject: [PATCH] docs(33-08): complete vue-seo-layer build guard, noindex routes and components plan --- .../33-08-SUMMARY.md | 406 ++++++++++++++++++ .../deferred-items.md | 8 + 2 files changed, 414 insertions(+) create mode 100644 .planning/phases/33-seo-output-audit-and-content/33-08-SUMMARY.md diff --git a/.planning/phases/33-seo-output-audit-and-content/33-08-SUMMARY.md b/.planning/phases/33-seo-output-audit-and-content/33-08-SUMMARY.md new file mode 100644 index 00000000..6ac813f8 --- /dev/null +++ b/.planning/phases/33-seo-output-audit-and-content/33-08-SUMMARY.md @@ -0,0 +1,406 @@ +--- +phase: 33-seo-output-audit-and-content +plan: 08 +subsystem: vue-seo-layer +tags: [nuxt-layer, build-guard, ssr, noindex, route-report, drift-check, breadcrumbs, pagination, html-sitemap] + +requires: + - phase: 33-07 + provides: vue-seo-layer runtime (resolve middleware, useCmsSeo, site answer cache, labels, tokens, smoke harness, fixture API) + - phase: 33-03 + provides: sitemap index /sitemap.xml and per-type files /sitemap--.xml through /_sitemap/files/{file} (unchanged here) +provides: + - SSR build guard (lib/guard.ts) with options seo.noindexRoutes and seo.indexable, guard:check over four fixture apps + - automatic noindex for declared routes and for pages whose meta sets requiresAuth or seo.robots (app plugin seo-route-robots) + - route artefact #seo-routes, the route report (POST /registry/routes) and the startup drift check + - GET /api/__seo__/site and useSeoSite() + - , + usePagination(), HTML sitemap pages (SeoHtmlSitemapPage, GET /api/__seo__/sitemap) and + - options seo.routeReport, seo.pagination, seo.htmlSitemap +affects: [33-09, 33-12, 33-13, 33-14] + +actuals: + tokens: 42200 + tasks: 3 + commits: 7 +plan_head_before: 6636cde70a6d866e6d7511c24f5d3b70258455f9 +plan_head_after: b051bda0809b9a8ca58f2c55c55d12d6477e8127 + +tech-stack: + added: [] + patterns: + - "Build-time checks in the local module: nitro:config (rules merged) runs before pages:resolved, so the guard runs once the pages are resolved; a production build throws, nuxt dev and nuxt prepare warn" + - "Build-time route data for runtime plugins through addServerTemplate (#seo-routes), no network call at build time" + - "Pagination and breadcrumb links through NuxtLink's custom slot, so the router's aria-current does not mark every page of the same path" + - "Helpers in app/utils carry the seo prefix (auto-imported next to the journal layer's paginationWindow)" + +key-files: + created: + - ../vue-seo-layer/lib/guard.ts + - ../vue-seo-layer/lib/routes.ts + - ../vue-seo-layer/lib/options.ts + - ../vue-seo-layer/testing/guard-check.mjs + - ../vue-seo-layer/testing/guard/{indexable-ssr-false,noindex-ok,client-page,global-ssr-false}/ + - ../vue-seo-layer/app/plugins/seo-route-robots.ts + - ../vue-seo-layer/server/plugins/seo-route-report.ts + - ../vue-seo-layer/server/plugins/seo-drift.ts + - ../vue-seo-layer/server/lib/drift.ts + - ../vue-seo-layer/server/lib/routeReport.ts + - ../vue-seo-layer/server/lib/htmlSitemap.ts + - ../vue-seo-layer/server/seo-routes.d.ts + - ../vue-seo-layer/server/api/__seo__/site.get.ts + - ../vue-seo-layer/server/api/__seo__/sitemap.get.ts + - ../vue-seo-layer/app/composables/useSeoSite.ts + - ../vue-seo-layer/app/composables/usePagination.ts + - ../vue-seo-layer/app/components/Seo/Breadcrumbs.vue + - ../vue-seo-layer/app/components/Seo/Pagination.vue + - ../vue-seo-layer/app/components/Seo/SitemapLink.vue + - ../vue-seo-layer/app/components/Seo/SitemapList.vue + - ../vue-seo-layer/app/components/global/SeoHtmlSitemapPage.vue + - ../vue-seo-layer/app/seo-pages/SitemapPage.vue + - ../vue-seo-layer/app/seo-pages/SitemapGroupPage.vue + - ../vue-seo-layer/app/utils/paginationWindow.ts + - ../vue-seo-layer/app/utils/pageHref.ts + - ../vue-seo-layer/app/utils/breadcrumbClaim.ts + - ../vue-seo-layer/app/utils/sitePath.ts + - ../vue-seo-layer/testing/expect-seo-drift.txt + - ../vue-seo-layer/testing/fixtures/seo/site-drift.json + - ../vue-seo-layer/testing/fixtures/seo/html.json + - ../vue-seo-layer/.playground/app/pages/{konto,szkic}.vue, prywatne/index.vue, blog/index.vue + - ../vue-seo-layer/tests/{guard,routes,paginationWindow,pageHref,breadcrumbClaim,htmlSitemap}.test.ts + modified: + - ../vue-seo-layer/lib/seo-module.ts + - ../vue-seo-layer/server/lib/config.ts + - ../vue-seo-layer/app/composables/useCmsSeo.ts + - ../vue-seo-layer/app/composables/useSeoLabels.ts + - ../vue-seo-layer/app/utils/seo.ts + - ../vue-seo-layer/app/utils/types.ts + - ../vue-seo-layer/app/assets/css/seo-base.css + - ../vue-seo-layer/package.json + - ../vue-seo-layer/README.md + - ../vue-seo-layer/testing/smoke-ssr.mjs + - ../vue-seo-layer/testing/fixture-api.mjs + - ../vue-seo-layer/testing/expect-seo.txt, expect-seo-off.txt, expect-seo-down.txt, expect-seo-notoken.txt + - ../vue-seo-layer/testing/fixtures/seo/site.json, resolve.json + - ../vue-seo-layer/.playground/nuxt.config.ts, app/app.vue, app/pages/[...slug].vue + +key-decisions: + - "The build guard runs when the pages are resolved (pages:resolved), not inside nitro:config: Nuxt fires nitro:config first; the rules are read from the final Nitro config (nitro:init), and nitro:build:before is the fallback for an app without pages" + - "The guard throws only in a production build or generate; nuxt dev and nuxt prepare (run on install) warn, so an install never fails" + - "Declared noindex and page-meta robots win over the CMS robots (tagPriority -10 against the head entry's critical -8), except on staging" + - "The route report retries once after 60 s only on no answer or a non-404/401/403 status; a 404 (no page registry) and a refused token are logged, not retried" + - "A root catch-all page does not count as serving a CMS content-type pattern in the drift check; a prefixed catch-all does" + - "app/utils helpers are seo-prefixed (seoPaginationWindow, seoPageHref, seoClaimBreadcrumbs, seoSitePath): the journal layer auto-imports its own paginationWindow with another return shape" + - "HTML sitemap pages answer 404 unless the CMS's site answer has the HTML sitemap on with a path for the locale, 503 when the backend has not answered; an app page at the same path keeps it" + +patterns-established: + - "Smoke grammar: routereport N [has=PATH] [lacks=PATH], log TEXT:N, --site-file NAME" + - "Split commits verified on the staged tree: git checkout-index into a scratch dir, then test, build and smoke there" + +requirements-completed: [SC-2, D-70, D-23, D-21, D-33, D-20c, D-86, D-40, D-41, D-42, D-71, D-34, D-97] + +coverage: + - id: D1 + description: "nuxt build fails with the D-70 message for a Nuxt route rule, a Nitro route rule, a .client.vue page and a global ssr: false, and passes when the route is declared in seo.noindexRoutes; the declared route is served with X-Robots-Tag: noindex, nofollow and / without it; inline route rules are refused; seo.indexable false checks nothing" + requirement: "D-70" + verification: + - kind: integration + ref: "npm run guard:check (testing/guard-check.mjs, 16 checks)" + status: pass + - kind: unit + ref: "tests/guard.test.ts" + status: pass + human_judgment: false + - id: D2 + description: "A page with requiresAuth meta, a page with seo.robots and a path under noindexRoutes get the robots meta tag and X-Robots-Tag without page code; an ordinary page keeps the CMS robots" + requirement: "D-23" + verification: + - kind: integration + ref: "npm run smoke:ssr (expect-seo.txt /konto, /szkic, /prywatne, /plain lines)" + status: pass + - kind: unit + ref: "tests/routes.test.ts#pageMetaRobots" + status: pass + human_judgment: false + - id: D3 + description: "One route report at server start with the token and the static paths (not /konto, /szkic, /prywatne, the sitemap pages); none without a token or with routeReport false" + requirement: "D-33" + verification: + - kind: integration + ref: "npm run smoke:ssr (routereport lines in expect-seo.txt, expect-seo-notoken.txt, expect-seo-drift.txt)" + status: pass + - kind: unit + ref: "tests/routes.test.ts#staticRoutes, routeReportBody, routeReportOutcome" + status: pass + human_judgment: false + - id: D4 + description: "Startup drift check: no warning when CMS and build agree, three warnings (URL pattern, pagination form, pl HTML sitemap path) against site-drift.json, and the server still serves pages" + requirement: "D-20c" + verification: + - kind: integration + ref: "npm run smoke:ssr (log lines in expect-seo.txt and expect-seo-drift.txt)" + status: pass + - kind: unit + ref: "tests/routes.test.ts#driftWarnings" + status: pass + human_judgment: false + - id: D5 + description: ": one nav per page with a layout and a page instance, server-rendered links, last item aria-current text; none on the home page or a page without a trail" + requirement: "D-86" + verification: + - kind: integration + ref: "npm run smoke:ssr (expect-seo.txt L1 lines, count=