docs(33-16): complete the Nuxt layers gap-closure plan
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
---
|
||||
phase: 33-seo-output-audit-and-content
|
||||
plan: 16
|
||||
subsystem: nuxt-layers
|
||||
tags: [seo, nuxt, vue-seo-layer, vue-journal-layer, vue-menu-layer, vue-faq-layer, gap-closure, route-report, html-sitemap, i18n, release-guard]
|
||||
status: complete
|
||||
|
||||
requires:
|
||||
- phase: 33-07..33-09
|
||||
provides: vue-seo-layer v0.2.0 features (route report, root files, HTML sitemap pages, build guard) and the content layers on it
|
||||
- phase: 33-13
|
||||
provides: the release decision (after-tags) and the 33-14 release checkpoint this plan unblocks
|
||||
provides:
|
||||
- "vue-seo-layer: noindexStaticRoutes, #seo-routes noindexPaths, routeReportBody(paths, noindex) with ROUTE_REPORT_MAX_BODY, route report key noindex (gap-2 contract), smoke grammar noindex= / lacksnoindex="
|
||||
- "vue-seo-layer: lib/sitemapPages.ts (seoSitemapPages, isSitemapRoute, sitemapSkipMessage), option seo.htmlSitemap.overCatchAll, seoFirstPageRedirect, SitemapLink route check and i18n locale fallback, root files 503 in the unknown state"
|
||||
- "vue-journal-layer: journalArticleMeta, journalPaginationSource, check-git-install.sh --check-links and the tag-mode guard, the playground's per-locale HTML sitemap paths"
|
||||
- "version fields seo 0.2.0, journal 0.8.0, menu 0.5.0, faq 0.5.0 with README install pins and upgrade notes; the four 33-14-notes-vue-*.md drafts updated"
|
||||
affects: [33-14, 33-15]
|
||||
|
||||
actuals:
|
||||
tokens: 16400
|
||||
tasks: 4
|
||||
commits: 13
|
||||
plan_head_before: 8d562004cb05d8d40d12253c23e406259de61006
|
||||
plan_head_after: c2e56ef
|
||||
plan_heads:
|
||||
vue-seo-layer: { before: b3a0078, after: 973142a, commits: 5 }
|
||||
vue-journal-layer: { before: e8f307b, after: 761c7fc, commits: 5 }
|
||||
vue-menu-layer: { before: 8b5da67, after: a7602c9, commits: 1 }
|
||||
vue-faq-layer: { before: 7d952fa, after: 5e5950a, commits: 1 }
|
||||
summercms.go: { before: 8d56200, after: c2e56ef, commits: 1 }
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Layer routes that must not be localised by @nuxtjs/i18n are added from a pages:resolved hook registered on ready (after every module's modules:done handler), named <name><routesNameSeparator><locale>"
|
||||
- "Report bodies bounded by the older consumer's read limit: drop the optional list from the end first, then the required one"
|
||||
- "Release guard for git-installed layers: package.json local-spec scan (link:, file:, workspace:, portal:) before tagging and on the installed tag"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../vue-seo-layer/lib/sitemapPages.ts
|
||||
- ../vue-seo-layer/tests/sitemapPages.test.ts
|
||||
modified:
|
||||
- ../vue-seo-layer/lib/routes.ts
|
||||
- ../vue-seo-layer/lib/seo-module.ts
|
||||
- ../vue-seo-layer/lib/options.ts
|
||||
- ../vue-seo-layer/server/seo-routes.d.ts
|
||||
- ../vue-seo-layer/server/lib/routeReport.ts
|
||||
- ../vue-seo-layer/server/lib/site.ts
|
||||
- ../vue-seo-layer/server/middleware/seo-root.ts
|
||||
- ../vue-seo-layer/server/plugins/seo-route-report.ts
|
||||
- ../vue-seo-layer/app/components/global/SeoHtmlSitemapPage.vue
|
||||
- ../vue-seo-layer/app/components/Seo/SitemapLink.vue
|
||||
- ../vue-seo-layer/app/utils/pageHref.ts
|
||||
- ../vue-seo-layer/.playground/nuxt.config.ts
|
||||
- ../vue-seo-layer/testing/smoke-ssr.mjs
|
||||
- ../vue-seo-layer/testing/expect-seo.txt
|
||||
- ../vue-seo-layer/testing/expect-seo-down.txt
|
||||
- ../vue-seo-layer/tests/routes.test.ts
|
||||
- ../vue-seo-layer/tests/pageHref.test.ts
|
||||
- ../vue-seo-layer/tests/rootFiles.test.ts
|
||||
- ../vue-seo-layer/package.json
|
||||
- ../vue-seo-layer/README.md
|
||||
- ../vue-journal-layer/.playground/nuxt.config.ts
|
||||
- ../vue-journal-layer/app/journal-pages/PostPage.vue
|
||||
- ../vue-journal-layer/app/utils/seo.ts
|
||||
- ../vue-journal-layer/app/utils/routes.ts
|
||||
- ../vue-journal-layer/lib/journal-module.ts
|
||||
- ../vue-journal-layer/tests/seo.test.ts
|
||||
- ../vue-journal-layer/tests/routes.test.ts
|
||||
- ../vue-journal-layer/testing/expect-acme.txt
|
||||
- ../vue-journal-layer/scripts/check-git-install.sh
|
||||
- ../vue-journal-layer/package.json
|
||||
- ../vue-journal-layer/README.md
|
||||
- ../vue-menu-layer/package.json
|
||||
- ../vue-menu-layer/README.md
|
||||
- ../vue-faq-layer/package.json
|
||||
- ../vue-faq-layer/README.md
|
||||
- .planning/phases/33-seo-output-audit-and-content/33-14-notes-vue-seo-layer-v0.2.0.md
|
||||
- .planning/phases/33-seo-output-audit-and-content/33-14-notes-vue-journal-layer-v0.8.0.md
|
||||
- .planning/phases/33-seo-output-audit-and-content/33-14-notes-vue-menu-layer-v0.5.0.md
|
||||
- .planning/phases/33-seo-output-audit-and-content/33-14-notes-vue-faq-layer-v0.5.0.md
|
||||
|
||||
key-decisions:
|
||||
- "The HTML sitemap pages are added from a pages:resolved hook registered on ready, not in modules:done: vue-seo-layer's module is set up before @nuxtjs/i18n, so a hook added from its modules:done handler ran before i18n's and i18n localised the layer's routes again (seo-sitemap-en___en___pl)"
|
||||
- "routeReportBody drops every noindex entry before it cuts routes (gap-2 contract point 6): when routes alone exceed 4,113,024 bytes, noindex is [] even if a short entry would fit in the slack"
|
||||
- "An app with only app.vue no longer needs seo.htmlSitemap: false: without the pages module no pages:resolved runs, so no sitemap route is added and the router stays off; such an app has no HTML sitemap pages"
|
||||
- "check-git-install --check-links prints `check-git-install: <dir>: depends on <name> through <spec> (<section>)`, one wording for both modes"
|
||||
|
||||
requirements-completed: [SC-1, SC-2, D-08, D-22, D-23, D-24, D-33, D-34, D-40, D-42, D-97]
|
||||
|
||||
metrics:
|
||||
duration: "19 min (2026-11-04T21:38:00Z to 21:57:35Z by the session clock)"
|
||||
completed: 2026-11-04
|
||||
---
|
||||
|
||||
# Phase 33 Plan 16: Nuxt layers gap closure Summary
|
||||
|
||||
The Nuxt layers now send the noindex static paths in the route report, inside the older plugin's body limit. The HTML sitemap pages exist once per locale with @nuxtjs/i18n, are never put over a catch-all unless `overCatchAll` is set, and page 1 never redirects to itself. robots.txt, sitemap.xml and llms.txt answer 503 while the backend is unknown. Journal post pages keep og:type article, and the journal registers its path-form routes from the SEO module's resolved pagination. A `--check-links` guard blocks tags with `link:` dependencies, and the versions and pins match the release.
|
||||
|
||||
## Commits
|
||||
|
||||
| Repo | Commit | Subject |
|
||||
|------|--------|---------|
|
||||
| vue-seo-layer | 7a26692 | feat: the route report sends the noindex static paths (tracer) |
|
||||
| vue-seo-layer | 7588aa5 | fix: HTML sitemap page 1 redirects with the locale's segment |
|
||||
| vue-seo-layer | a651de4 | fix: HTML sitemap pages once per locale, never over a catch-all by default |
|
||||
| vue-seo-layer | 8cdf359 | fix: root files answer 503 while the backend has not answered |
|
||||
| vue-seo-layer | 973142a | chore: version 0.2.0 and install pin |
|
||||
| vue-journal-layer | 1e05efc | test: the playground's HTML sitemap paths per locale |
|
||||
| vue-journal-layer | 839db51 | fix: post pages keep og:type article and their article times |
|
||||
| vue-journal-layer | d64fe47 | fix: path-form listing routes use the SEO module's resolved pagination |
|
||||
| vue-journal-layer | d4ba3e2 | feat: check-git-install --check-links refuses local dependencies |
|
||||
| vue-journal-layer | 761c7fc | chore: version 0.8.0 and install pin |
|
||||
| vue-menu-layer | a7602c9 | chore: version 0.5.0 and install pin |
|
||||
| vue-faq-layer | 5e5950a | chore: version 0.5.0 and install pin |
|
||||
| summercms.go | c2e56ef | docs(33-16): release-note drafts for the layer fixes |
|
||||
|
||||
All on each repo's default branch (layers `main`, summercms.go `master`). Nothing was pushed or tagged, and no `link:../vue-seo-layer` dependency was swapped. `git diff <base>..HEAD -- pnpm-lock.yaml` is empty in all four layers, and package.json changed only in its `version` field.
|
||||
|
||||
## Finding to test
|
||||
|
||||
| Finding | Fix | Proving test |
|
||||
|---------|-----|--------------|
|
||||
| G1 layer side (D-23, D-24, D-33) | `noindexStaticRoutes`, `#seo-routes` `noindexPaths`, `routeReportBody(paths, noindex)` with `ROUTE_REPORT_MAX_BODY` = 4,113,024 | tests/routes.test.ts: `noindexStaticRoutes: the static paths the build serves noindex`, `…client-only noindex pages kept, patterns never sent`, `…the i18n copies of a noindex page`, `routeReportBody: within the plugin limits, both lists`, `…the JSON body stays within the older plugin limit, noindex dropped first`, `…routes alone over the limit are cut from the end`; smoke expect-seo.txt `routereport 1 … noindex=/konto noindex=/szkic noindex=/prywatne lacksnoindex=/ lacksnoindex=/mapa-strony` |
|
||||
| L4 WR-01 page-1 self-redirect | `seoFirstPageRedirect`, locale segment in SeoHtmlSitemapPage | tests/pageHref.test.ts: `seoFirstPageRedirect: page 1 goes to the unnumbered URL with the locale segment`, `…never a redirect to the URL itself`; smoke `301 /mapa-strony/posts?page=1` still passes |
|
||||
| L4 WR-04 sitemap pages over a catch-all | `seoSitemapPages` catch-all rule, `htmlSitemap.overCatchAll`, build log line | tests/sitemapPages.test.ts: `a root catch-all skips every locale unless overCatchAll`, `a prefixed catch-all skips only the paths it covers`, `an app page at the path keeps it (taken)`; smoke expect-seo.txt /mapa-strony lines pass with the playground's `overCatchAll: true` |
|
||||
| L4 WR-05 i18n cross-locale copies | pages added after i18n, names `<name>___<locale>`; SitemapLink route check | tests/sitemapPages.test.ts `with i18n each route is named for its own locale, once`; built journal playground route names (below); smoke footer link line |
|
||||
| L4 WR-08 root files while unknown | `rootFileTargets` targets the fixed names in state unknown | tests/rootFiles.test.ts `a backend never reached since start (unknown) still owns the fixed root files` and `an older backend (absent) passes every path through`; expect-seo-down.txt `503 /robots.txt`, `503 /sitemap.xml`, `503 /llms.txt` with `Retry-After: 60`; expect.txt (`--no-site`) keeps `404 /robots.txt` |
|
||||
| L4 WR-02 post og:type and article times | `journalArticleMeta`, always set in PostPage | tests/seo.test.ts `journalArticleMeta: og:type article and the post times`; expect-acme.txt `/blog/heading-on count=property="og:type":1 …article:published_time…` (failed before the fix) and `/blog/uncategorised-note` |
|
||||
| L4 WR-03 journal path-form routes | `journalPaginationSource`, computed in the extendPages callback | tests/routes.test.ts `journalPaginationSource: the SEO module's resolved option, else the raw seo.pagination` and `an app that set only form path gets the default pl and en segments`; a probe build with only `pagination: { form: 'path' }` had `/blog/strona/:page(\d+)`, `/blog/page/:page(\d+)` and the `/pl/` copies |
|
||||
| L4 CR-01 link: dependency, versions, pins | `check-git-install.sh --check-links`, tag-mode guard, version fields and install pins | `check-git-install.sh --self-test` (planted link:, git+https, file:/workspace:/portal:, missing and mixed cases); `--check-links ../vue-seo-layer ../vue-journal-layer ../vue-menu-layer ../vue-faq-layer` exits 1 with the three link: lines and none for vue-seo-layer; version/pin loop prints seo 0.2.0 #v0.2.0, journal 0.8.0 #v0.8.0, menu 0.5.0 #v0.5.0, faq 0.5.0 #v0.5.0 |
|
||||
|
||||
Prohibitions: the route report stays compatible both ways. The fixture API ignores `noindex`, and the smoke with the routereport line passes. `--no-site` (404, not retried) passes in expect.txt. The guard and the noindex headers are unchanged: guard:check passes (16 checks), and the expect-seo.txt `/konto`, `/szkic`, `/prywatne` noindex lines pass.
|
||||
|
||||
## Journal playground route names (WR-05 evidence)
|
||||
|
||||
`grep -rhoE '"seo-sitemap-[a-z-]+(___[a-z]+)*"' .playground/.output/server | sort -u` after the fix:
|
||||
|
||||
```
|
||||
"seo-sitemap-en___en" /sitemap
|
||||
"seo-sitemap-group-en___en" /sitemap/:group
|
||||
"seo-sitemap-group-pl___pl" /pl/mapa-strony/:group
|
||||
"seo-sitemap-pl___pl" /pl/mapa-strony
|
||||
```
|
||||
|
||||
Before the fix the build held seo-sitemap-en___pl and seo-sitemap-pl___en. With the first `modules:done` attempt it held `___en___en` / `___en___pl` doubles (see deviation 1).
|
||||
|
||||
## The 33-09 "app without pages" item
|
||||
|
||||
It is gone. I built the menu playground (only `app.vue`) once without `seo: { htmlSitemap: false }`: it registered no `seo-sitemap-` route and its smoke passed (17 checks). The config was restored afterwards, and both the menu and FAQ playgrounds keep `htmlSitemap: false` as the plan asked. The vue-seo-layer README and the menu and FAQ upgrade notes now say that such an app gets no sitemap pages and needs no setting.
|
||||
|
||||
## Status for deferred-items.md (owned by 33-15, not edited here)
|
||||
|
||||
- **G1, layer side:** closed. Each server start sends `noindex` with the static paths the build serves noindex, including the i18n copies, under the gap-2 contract. The CMS side (the filter and the stored set) belongs to 33-15.
|
||||
- **33-09 check-git-install item (no guard against link:):** closed on the script side. `--check-links` and the tag-mode check are in vue-journal-layer d4ba3e2, and `--self-test` covers them. The swap itself is still open and belongs to 33-14 Task 4.
|
||||
- **33-09 "app without pages":** closed (see above).
|
||||
|
||||
## For 33-14 Task 4
|
||||
|
||||
- **Link swap:** not done. `link:../vue-seo-layer` is still in vue-journal-layer, vue-menu-layer and vue-faq-layer. Step order (also in the journal README, Releasing): tag vue-seo-layer v0.2.0, then in each content layer replace the link with `git+https://git.golem15.com/golem15/vue-seo-layer.git#v0.2.0` and run `pnpm install`. Then run `bash ../vue-journal-layer/scripts/check-git-install.sh --check-links ../vue-seo-layer ../vue-journal-layer ../vue-menu-layer ../vue-faq-layer`, which must exit 0, then tag and push.
|
||||
- **check-git-install `<tag>`:** now also fails when the installed tag's package.json has a local dependency, before building. Its `--self-test` is part of the gate's layers stage.
|
||||
- **Versions:** already bumped (seo 0.2.0, journal 0.8.0, menu 0.5.0, faq 0.5.0), so Task 4 does not bump them again.
|
||||
- **Release notes:** the four 33-14-notes-vue-*.md drafts changed (c2e56ef):
|
||||
- seo: the route report's noindex list, the sitemap pages per locale and not over a catch-all, `overCatchAll`, the upgrade line, and the Fixed line about the root-file 503s;
|
||||
- journal: path-form listing routes, plus an upgrade line on the catch-all and the article meta;
|
||||
- menu and FAQ: the catch-all upgrade note replaces the `htmlSitemap: false` advice.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] The sitemap page hook is registered on `ready`, not in `modules:done`**
|
||||
- **Found during:** Task 2 B (journal playground build).
|
||||
- **Issue:** vue-seo-layer's module is set up before @nuxtjs/i18n, so its `modules:done` handler runs first. A `pages:resolved` hook added there ran before the one i18n adds in its own `modules:done`. i18n then localised the layer's routes again (`seo-sitemap-en___en___pl`), which is the WR-05 bug once more.
|
||||
- **Fix:** the hook is added from `ready`, which Nuxt calls after every `modules:done` handler and before the build resolves the pages. The plan's acceptance grep `grep -n "modules:done" lib/seo-module.ts` now matches two comment lines that explain the choice, not the hook.
|
||||
- **Files:** ../vue-seo-layer/lib/seo-module.ts. **Commit:** a651de4.
|
||||
|
||||
**2. [Rule 1 - Bug] routeReportBody: routes too large means noindex is empty**
|
||||
- **Found during:** Task 1 (the unit test for routes alone over the limit).
|
||||
- **Issue:** my first version cut routes to fit and then filled the slack with noindex entries. That breaks contract point 6, which drops noindex first.
|
||||
- **Fix:** when routes alone exceed the limit, noindex is `[]`. **Commit:** 7a26692.
|
||||
|
||||
**3. [Rule 1 - Docs] README notes that were wrong after the fix**
|
||||
- The vue-seo-layer README `htmlSitemap` row and the menu and FAQ "Upgrading from 0.4" bullets said that an app with only `app.vue` must set `htmlSitemap: false`. They now describe the new behaviour.
|
||||
- The journal README "Fallback meta" bullet no longer lists the article times, which are now always set.
|
||||
- **Commits:** a651de4, 839db51, a7602c9, 5e5950a.
|
||||
|
||||
**4. Minor wording/placement choices**
|
||||
- **`--check-links` line:** it reads `check-git-install: <dir>: depends on <name> through <spec> (<section>)` instead of `<dir>: <name> depends on <spec>`, one wording for both modes.
|
||||
- **SeoHtmlSitemapPage form:** the redirect form comes from the route (path when `:page` is set, else query) instead of from the site answer. A path-form route under a query-form site answer would otherwise self-redirect too.
|
||||
- **Journal "Upgrading from 0.7":** besides the catch-all and og:type notes, it also mentions the path-form segments.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None. No new endpoint or auth path. The route report carries one more list on the existing request, as recorded in T-33-77.
|
||||
|
||||
## Gate
|
||||
|
||||
`bash scripts/check-phase33.sh --self-test --layers` from summercms.go, exit 0. Log: /tmp/claude-1000/-media-nvme-dev-golem15-summercms-io-summercms-summercms-go/cc746d7e-76ce-49f4-ae43-0e1debb2bbea/scratchpad/gate-33-16.log
|
||||
|
||||
- `PASS self-test` (45 checks, `phase33 self-test passed`)
|
||||
- `PASS layers` (`phase33 layers passed`):
|
||||
- unit tests: vue-seo-layer 152/152, vue-journal-layer 202/202, vue-menu-layer 24/24, vue-faq-layer 19/19 (tap, `# fail 0`);
|
||||
- the four playground builds;
|
||||
- vue-seo-layer `smoke:ssr` (7 runs), `guard:check`, `purge:check`;
|
||||
- the five journal smoke runs, its purge-check and purge-token-check, and `check-git-install: self-test ok`;
|
||||
- the menu and FAQ `smoke:ssr`.
|
||||
|
||||
The go, plugins, docker and consumers stages were not run. The orchestrator runs `--all` after both SUMMARYs exist.
|
||||
|
||||
Task verifies run separately along the way:
|
||||
- vue-seo-layer: `smoke:ssr` all seven runs ok (expect-seo.txt 64 checks, expect-seo-down.txt 9), guard:check 16, purge:check 18, purge-token-check 53.
|
||||
- vue-journal-layer: `smoke:ssr` five runs ok (expect-acme.txt 89 checks), plus the built route-name check above.
|
||||
- `--check-links` over the four layers: exit 1 with three link: lines (journal, menu, FAQ), as required until 33-14 Task 4.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
Both created files exist; all 13 commits exist in their repos; the plan's acceptance greps match (noindexStaticRoutes, ROUTE_REPORT_MAX_BODY in code and tests, noindexPaths in plugin and types, the README contract JSON, `^503 /robots.txt`, seoFirstPageRedirect, overCatchAll in README and playground, og:type lines in expect-acme.txt, --check-links in script and README); the `modules:done` grep matches comments only (deviation 1).
|
||||
Reference in New Issue
Block a user