docs(17-05): summary of the blog companions site integration plan

This commit is contained in:
2026-10-12 18:55:47 +02:00
parent 2440560f7c
commit 134e6640f1

View File

@@ -0,0 +1,407 @@
---
phase: 17-blog-companions
plan: 05
subsystem: integration
tags: [go, nuxt, nginx, sitemap, menu, faq, winter-import, uat, layers, submodules]
requires:
- phase: 17-02
provides: summercms v0.1.23 (RELEASE_TAG) on origin
- phase: 17-03
provides: Journal menu item types (all-journal-categories, all-journal-posts, journal-category, journal-post), sm-sitemap-plugin with sitemap:import-winter
- phase: 17-04
provides: sm-menu-plugin and sm-faq-plugin, approved /_menu/api/v1 and /_faq/api/v1 shapes, menu:import-winter and faq:import-winter
provides:
- pushed sm-journal-plugin, sm-sitemap-plugin, sm-menu-plugin, sm-faq-plugin (main, require summercms v0.1.23)
- public layers vue-menu-layer and vue-faq-layer (main, annotated tag v0.1.0)
- grzybyfunkcjonalne host with the three plugins as submodules, menu_items patterns, CORS paths, TestCompanionsHost
- site app rendering the header menu through MenuNav and the homepage FAQ through FaqBlock; Nuxt sitemap off
- scripts/import-companions.sh (ordered D-19 import), migrate.sh --companions, sitemap:import-winter --page-url
- nginx locations = /sitemap.xml, = /sitemap.xsl, ^~ /_menu/, ^~ /_faq/ on UAT (GET and HEAD only)
- UAT redeployed with the Winter menu, FAQ and sitemap rows imported
affects: [17-06 unit tests, production cutover of grzybyfunkcjonalne.pl, site-cache-purge-for-menu-and-faq todo, sm-translate-plugin cookie follow-up]
actuals:
tokens: 41645
tasks: 5
commits: 3
plan_head_before: 4ee26987006383efe4dba5059cbbf8984511ad0b
plan_head_after: 2440560f7cea46d668b5a88ee0f26764400e2597
repo_heads:
summercms.go: {before: 4ee2698, after: 2440560, commits: 2, note: "rev-list 4ee2698..2440560 counts 3; af02e6c (docs(phase-13) security verification) is another session's commit inside the range; this plan's own commits are 7290d8f and 2440560, both local (no push approved)"}
sm-journal-plugin: {before: "origin 09308d9", after: 06d0c9b, commits: 1, pushed: "06d0c9b plus 17-03/16.3 commits 7bbafcb 58851dd fd1572a c49d216"}
sm-sitemap-plugin: {before: "7d937e3 (no remote)", after: 5a260db, commits: 1, pushed: "fe39e26 4a82240 7d937e3 5a260db"}
sm-menu-plugin: {before: "33a65ad (no remote)", after: 33a65ad, commits: 0, pushed: "08f99b8 e044bc7 33a65ad"}
sm-faq-plugin: {before: "1585f06 (no remote)", after: 1585f06, commits: 0, pushed: "1c54912 7413c69 1585f06"}
vue-menu-layer: {before: "(new repository)", after: d980f4c, commits: 1, tag: "v0.1.0 annotated (tag object 11181dc0, peeled d980f4c)"}
vue-faq-layer: {before: "(new repository)", after: 2d62924, commits: 1, tag: "v0.1.0 annotated (tag object 706e734d, peeled 2d62924)"}
vue-grzybyfunkcjonalne-app: {before: 3a920c7, after: bc143fa, commits: 1}
sm-grzybyfunkcjonalne-app: {before: e44cfde, after: 6441b17, commits: 7}
tech-stack:
added: [vue-menu-layer v0.1.0, vue-faq-layer v0.1.0]
patterns:
- "Companion imports run as one ordered script (FAQ after translate locales, menu after Journal, then sitemap) that migrate.sh calls on a fresh install and that runs alone on an existing database"
- "A Winter CMS-page sitemap item is copied as a url item through an explicit --page-url reference=/path map instead of being dropped"
- "Go-served public paths get exact or ^~ nginx locations with Host and X-Forwarded-Proto, limit_except GET HEAD, and no per-location add_header (server-level security headers keep applying)"
- "Plan-time remote checks behind UAT basic auth run on the server with curl --resolve to loopback"
key-files:
created:
- ../sm-grzybyfunkcjonalne-app/companions_test.go
- ../sm-grzybyfunkcjonalne-app/config/menu_items.yaml
- ../sm-grzybyfunkcjonalne-app/scripts/import-companions.sh
- ../sm-grzybyfunkcjonalne-app/testdata/companions/menus.jsonl
- ../sm-grzybyfunkcjonalne-app/testdata/companions/entries.jsonl
- ../sm-grzybyfunkcjonalne-app/testdata/companions/faqs.jsonl
- ../sm-grzybyfunkcjonalne-app/testdata/companions/sitemap-definitions.jsonl
- ../sm-grzybyfunkcjonalne-app/testdata/companions/sitemap-settings.json
- ../vue-menu-layer/app/components/Menu/Nav.vue
- ../vue-menu-layer/app/components/Menu/NavItem.vue
- ../vue-menu-layer/app/utils/menuLinks.ts
- ../vue-faq-layer/app/components/Faq/Block.vue
- ../vue-faq-layer/app/components/Faq/List.vue
- ../vue-faq-layer/app/components/Faq/Item.vue
- ../sm-grzybyfunkcjonalne-app/vue-grzybyfunkcjonalne-app/tests/fixtures/companions/menus/main-menu.json
- ../sm-grzybyfunkcjonalne-app/vue-grzybyfunkcjonalne-app/tests/fixtures/companions/faqs/what-mushrooms-can-give-you.json
- .planning/todos/pending/site-cache-purge-for-menu-and-faq.md
modified:
- ../sm-journal-plugin/go.mod
- ../sm-sitemap-plugin/console/import_winter.go
- ../sm-grzybyfunkcjonalne-app/.gitmodules
- ../sm-grzybyfunkcjonalne-app/go.work
- ../sm-grzybyfunkcjonalne-app/go.mod
- ../sm-grzybyfunkcjonalne-app/summer.yaml
- ../sm-grzybyfunkcjonalne-app/plugins.gen.go
- ../sm-grzybyfunkcjonalne-app/config/http.yaml
- ../sm-grzybyfunkcjonalne-app/boot_test.go
- ../sm-grzybyfunkcjonalne-app/scripts/migrate.sh
- ../sm-grzybyfunkcjonalne-app/scripts/smoke.sh
- ../sm-grzybyfunkcjonalne-app/scripts/urls.txt
- ../sm-grzybyfunkcjonalne-app/scripts/check-deploy.sh
- ../sm-grzybyfunkcjonalne-app/scripts/php-export/README.md
- ../sm-grzybyfunkcjonalne-app/deploy/nginx/grzybyfunkcjonalne-staging.conf
- ../sm-grzybyfunkcjonalne-app/DEPLOY.md
- ../sm-grzybyfunkcjonalne-app/README.md
- ../sm-grzybyfunkcjonalne-app/vue-grzybyfunkcjonalne-app/nuxt.config.ts
- ../sm-grzybyfunkcjonalne-app/vue-grzybyfunkcjonalne-app/app/components/SiteHeader.vue
- ../sm-grzybyfunkcjonalne-app/vue-grzybyfunkcjonalne-app/app/components/FaqSection.vue
- .planning/phases/17-blog-companions/deferred-items.md
key-decisions:
- "MENU_CODE = main-menu (PHP menu id 1, Main Menu, active); the site header renders MenuNav code main-menu"
- "Menu types: link -> url and blog_categories -> all-journal-categories are built into menu:import-winter; no page entries exist; no --map is needed"
- "Sitemap cms-page item 'blog' is copied as a url item /blog through the new sitemap:import-winter --page-url blog=/blog (the live sitemap lists /blog); the cmsPage of the two Journal list items is dropped and the host patterns apply"
- "The PHP site never saved sitemap settings (system_settings row absent): no sitemap-settings.json export, the plugin keeps its defaults (priority tiers on)"
- "The FAQ export joins winter_translate_locales on this site (sed on the plugin SQL at run time, documented in scripts/php-export/README.md section 4); no Go code references winter_translate_*"
- "Six link entries under the missing parent 1 (Reishi to Lions's Mane, rodzaje/<slug>) are dropped by the import and reported; PHP never rendered them"
- "Remote smoke and the Task 5 curl checks run on the server through nginx with --resolve to loopback, because the UAT basic auth gate answers 401 from elsewhere"
- "Production deploy is not part of this plan (UAT only); the summercms.go docs commits stay local"
patterns-established:
- "Site integration of a shared plugin: plugin pushed with the framework tag first, then one host commit per submodule add or gitlink bump, then one wiring commit"
requirements-completed: [SC-1, SC-3, SC-4, D-05, D-15, D-18, D-19]
coverage:
- id: D1
description: "Host boots the three plugins from their pushed repositories, imports fixture exports, and serves the menu with Journal categories at /<slug>, the sanitized FAQ and the sitemap"
requirement: "SC-1"
verification:
- kind: integration
ref: "../sm-grzybyfunkcjonalne-app/companions_test.go#TestCompanionsHost"
status: pass
- kind: other
ref: "Task 2 verify (go vet, go test -short, plugin heads == origin, require v0.1.23, submodules checked out) -> host-wired"
status: pass
human_judgment: false
- id: D2
description: "vue-menu-layer and vue-faq-layer: typed clients, MenuNav and FaqBlock, URL allowlist and rel noopener tests, SSR smokes, tag v0.1.0 on origin"
requirement: "D-18"
verification:
- kind: unit
ref: "pnpm -C ../vue-menu-layer test (16 tests) && pnpm -C ../vue-faq-layer test (7 tests)"
status: pass
- kind: e2e
ref: "pnpm run smoke:ssr in both layers (17 and 10 checks) -> layers-ok"
status: pass
human_judgment: false
- id: D3
description: "Site app renders MenuNav in the header and FaqBlock on the homepage, app menu and FAQ data deleted, Nuxt sitemap disabled"
requirement: "SC-4"
verification:
- kind: unit
ref: "vue-grzybyfunkcjonalne-app pnpm test (27 tests), pnpm build, pnpm smoke:ssr (38 checks) -> site-ok"
status: pass
human_judgment: false
- id: D4
description: "UAT serves /sitemap.xml, /sitemap.xsl, /_menu/ and /_faq/ from Go through nginx with the server-level security headers, GET and HEAD only, and the full smoke passes"
requirement: "D-15"
verification:
- kind: e2e
ref: "on the server: CURL_HOME=<.curlrc resolve to 127.0.0.1> scripts/smoke.sh --url https://uat.grzybyfunkcjonalne.pl -> smoke: ok (105 urls)"
status: pass
- kind: other
ref: "Task 5 verify (check-deploy ok; sitemap content-type application/xml and FAQ data envelope through nginx) -> uat-companions-ok"
status: pass
human_judgment: false
- id: D5
description: "Winter menu, FAQ and sitemap rows exported read-only and imported on UAT by the ordered import step"
requirement: "D-19"
verification:
- kind: other
ref: "scripts/import-companions.sh dry run and real run on UAT (reports below)"
status: pass
human_judgment: false
- id: D6
description: "UAT sitemap loc set equals the live PHP sitemap's modulo host, except one UAT-only post"
requirement: "SC-3"
verification:
- kind: other
ref: "loc-set diff via nginx: 71 live paths all present on UAT; one UAT-only extra /blog/uat-16-2-blog-editor (404 on live)"
status: pass
human_judgment: true
rationale: "The plan's literal loc-sets-equal command fails on the UAT-only test post; a human accepts the explanation (UAT data, not a source difference)"
- id: D7
description: "Header menu interaction (dropdown, hover and click, mobile toggle, Esc, active link), FAQ opening, and admin editing of menus, entries, FAQ and sitemap on UAT"
requirement: "SC-4"
verification: []
human_judgment: true
rationale: "Visual and interaction checks of the end-of-phase human-check; the human confirmed the sitemap and the admin screens load, the full list is for /msd-verify-work"
duration: 93min
completed: 2026-10-12
status: complete
---
# Phase 17 Plan 05: Site integration of the blog companions Summary
**grzybyfunkcjonalne.pl on UAT now reads its header menu (`main-menu`) and homepage FAQ from the Go menu and FAQ plugins through the new tagged layers vue-menu-layer and vue-faq-layer, serves `/sitemap.xml` from the Go sitemap plugin through nginx, and carries the PHP site's menu, FAQ and sitemap rows copied by an ordered import step; smoke 105/105 through nginx.**
## Performance
- **Duration:** about 93 min wall clock, including two human waits (Task 1 exports and approval, the vhost install)
- **Started:** 2026-10-12T15:20:26Z
- **Completed:** 2026-10-12T16:55Z
- **Tasks:** 5 of 5
- **Files modified:** 30 in the host, 15 in the site app, 3 in sm-sitemap-plugin, 2 in sm-journal-plugin, 27 and 26 new files in the two layers, 2 in summercms.go
## Accomplishments
- Four plugin repositories pushed in release order, each requiring summercms v0.1.23; two new public layers pushed with annotated tags v0.1.0.
- Host: sitemap, menu and FAQ plugins as submodules (one commit each), Journal gitlink bump, wiring commit (go.work, go.mod, summer.yaml, regenerated plugins.gen.go, `config/menu_items.yaml` with `journal-post: /blog/:slug` and `journal-category: /:slug`, CORS `_menu/api/*` and `_faq/api/*`), `TestCompanionsHost` passing on Docker.
- Site app: `SiteHeader.vue` renders `MenuNav code="main-menu"`, `FaqSection.vue` renders `FaqBlock slug="what-mushrooms-can-give-you"`; `app/data/menu.ts` and `app/data/faq.pl.json` deleted; `sitemap: { enabled: false }`; robots names `/sitemap.xml`.
- `scripts/import-companions.sh` (FAQ, then menu, then sitemap; dry run; refuses a second run without `--force`), `migrate.sh --companions`, nginx locations, smoke lines, check-deploy assertions, DEPLOY.md "Blog companions" section.
- UAT redeployed and imported; the vhost installed by the human (sha256 verified), `nginx -t` passes, smoke ok through nginx, sitemap loc set matches live.
## Task Commits
1. **Task 1: repositories, read-only Winter export, menu code and maps, approval** (checkpoint, no commits; exports produced from the operator's dump, see Deviations)
2. **Task 2: host wiring (tracer)**
- sm-journal-plugin `06d0c9b` chore(deps): require summercms v0.1.23
- sm-sitemap-plugin `5a260db` feat(17-05): sitemap:import-winter --page-url copies a CMS page item as a url item
- host `2a68b15` chore: add sitemap plugin submodule
- host `d885458` chore: add menu plugin submodule
- host `df3bb06` chore: add faq plugin submodule
- host `95a4936` chore: bump journal plugin (menu item types)
- host `1ed0b48` feat(17-05): wire the sitemap, menu and FAQ plugins into the host
3. **Task 3: layers**
- vue-menu-layer `d980f4c` feat: MenuNav layer for the SummerCMS menu API (tag v0.1.0)
- vue-faq-layer `2d62924` feat: FaqBlock layer for the SummerCMS FAQ API (tag v0.1.0)
4. **Task 4: site app**
- vue-grzybyfunkcjonalne-app `bc143fa` feat: menu and FAQ from the SummerCMS plugins through vue-menu-layer and vue-faq-layer
- host `973b2b5` chore: bump the site app (menu and FAQ from the plugins, Go sitemap) (gitlink only)
5. **Task 5: import step, nginx, deploy guide, UAT**
- host `6441b17` feat(17-05): blog companions import step, nginx routes, smoke lines and deploy guide
- summercms.go `7290d8f` docs(17-05): todo for a site cache purge on menu and FAQ saves
- summercms.go `2440560` docs(17-05): deferred items from the UAT companions deploy
**Pushed heads (all verified equal to origin):** sm-journal-plugin main 06d0c9b, sm-sitemap-plugin main 5a260db, sm-menu-plugin main 33a65ad, sm-faq-plugin main 1585f06, vue-menu-layer main d980f4c + v0.1.0, vue-faq-layer main 2d62924 + v0.1.0, vue-grzybyfunkcjonalne-app main bc143fa, sm-grzybyfunkcjonalne-app master 6441b17. summercms.go is not pushed (not approved).
**Commit ledger note:** `git rev-list --count 4ee2698..2440560` is 3 because it includes `af02e6c` (docs(phase-13): add security threat verification), a concurrent session's commit. This plan made 2 summercms.go commits; its code commits live in the other repositories listed above.
## Task 1: discovery facts and the human's replies
**Human reply to the Task 1 checkpoint (verbatim; the bracket stands where the pasted discovery output was, recorded as facts below):**
> "2. not sure why do you need translate table name, we won't be using "winter" prefix in sm golem15.translate [discovery results pasted] 3. huh? I created mysqldump in that folder on server, must suffice. 4. approved"
Later update relayed by the orchestrator: the human chowned `/srv/grzybyfunkcjonalne/import/companions` to `grzybyfunkcjonalne`, and the JSONL files were written there (the canonical path).
**Human reply to the vhost install checkpoint (verbatim):**
> "vhost installed, all passes, i see sitemap loading, all plugins in backend etc"
**Recorded facts:**
| Item | Value |
|---|---|
| MENU_CODE | `main-menu` (id 1, "Main Menu", active) |
| Menu entry types | `link` x7 -> `url` (built in); `blog_categories` x1 -> `all-journal-categories` (built in); no `page` entries |
| Menu entries | id 2 Blog `/blog` (root); ids 3-8 links Reishi, Cordyceps, Chaga, Turkey Tail, Shiitake, Lions's Mane (`rodzaje/<slug>`) with parent_id 1, which does not exist -> dropped; id 9 Grzyby Funkcjonalne `blog_categories`, url `#` |
| FAQ | id 1 `what-mushrooms-can-give-you`, locale `pl` (locale_id 2 -> code pl), active, 16 questions; name "Grzyby funcjonalne - Pytania i Odpowiedzi" |
| Sitemap definitions | id 1, theme `arsha`: `url` `/` (kept); `cms-page` reference `blog` (-> `url` `/blog` via `--page-url blog=/blog`); `all-journal-posts` (kept, cmsPage `blog-post` dropped); `all-journal-categories` (kept, cmsPage `index` dropped) |
| Sitemap settings | no `system_settings` row -> no export, plugin defaults |
| Translate table | `winter_translate_locales` on this site |
| Maps passed | none (`--map`); `--page-url blog=/blog` for the sitemap |
| Export sha256 (prefix) | entries d896b5f1, faqs 623a94d1, menus 7f0c77f7, sitemap-definitions b73c0d18 |
## Import reports (UAT, 2026-10-12)
**Dry run:**
```
import-companions: faq:import-winter --file /srv/grzybyfunkcjonalne/import/companions/faqs.jsonl --dry-run
ℹ FAQ 1 'what-mushrooms-can-give-you' (pl): 16 questions
ℹ Dry run: 1 FAQs would be copied.
ℹ Dry run complete (no changes made).
import-companions: menu:import-winter --menus /srv/grzybyfunkcjonalne/import/companions/menus.jsonl --entries /srv/grzybyfunkcjonalne/import/companions/entries.jsonl --dry-run
⚠ entry 2 'Blog' (menu 1, type 'link'): type 'link' mapped to 'url'
⚠ entry 3 'Reishi' (menu 1, type 'link'): type 'link' mapped to 'url'
⚠ entry 4 'Cordyceps' (menu 1, type 'link'): type 'link' mapped to 'url'
⚠ entry 5 'Chaga' (menu 1, type 'link'): type 'link' mapped to 'url'
⚠ entry 6 'Turkey Tail' (menu 1, type 'link'): type 'link' mapped to 'url'
⚠ entry 7 'Shiitake' (menu 1, type 'link'): type 'link' mapped to 'url'
⚠ entry 8 'Lions's Mane' (menu 1, type 'link'): type 'link' mapped to 'url'
⚠ entry 9 'Grzyby Funkcjonalne' (menu 1, type 'blog_categories'): type 'blog_categories' mapped to 'all-journal-categories'
⚠ entry 3 'Reishi' (menu 1, type 'link'): dropped (its parent 1 is not in the entries export)
⚠ entry 4 'Cordyceps' (menu 1, type 'link'): dropped (its parent 1 is not in the entries export)
⚠ entry 5 'Chaga' (menu 1, type 'link'): dropped (its parent 1 is not in the entries export)
⚠ entry 6 'Turkey Tail' (menu 1, type 'link'): dropped (its parent 1 is not in the entries export)
⚠ entry 7 'Shiitake' (menu 1, type 'link'): dropped (its parent 1 is not in the entries export)
⚠ entry 8 'Lions's Mane' (menu 1, type 'link'): dropped (its parent 1 is not in the entries export)
ℹ Dry run: 1 menus, 2 entries kept, 8 remapped, 6 dropped.
ℹ Dry run complete (no changes made).
import-companions: no sitemap-settings.json, the sitemap keeps its default settings
import-companions: sitemap:import-winter --definitions /srv/grzybyfunkcjonalne/import/companions/sitemap-definitions.jsonl --page-url blog=/blog --dry-run
ℹ Copying definition 1 (theme 'arsha') into the 'default' definition.
⚠ item 1 (type 'cms-page', reference 'blog'): copied as type 'url' with url '/blog'
⚠ item 2 (type 'all-journal-posts'): cmsPage 'blog-post' dropped (CMS pages are not supported)
⚠ item 3 (type 'all-journal-categories'): cmsPage 'index' dropped (CMS pages are not supported)
ℹ Dry run: 4 items kept, 1 remapped, 0 dropped.
ℹ Dry run complete (no changes made).
import-companions: ok
```
**Real run:**
```
import-companions: faq:import-winter --file /srv/grzybyfunkcjonalne/import/companions/faqs.jsonl
ℹ FAQ 1 'what-mushrooms-can-give-you' (pl): 16 questions
✓ Copied 1 FAQs.
import-companions: menu:import-winter --menus /srv/grzybyfunkcjonalne/import/companions/menus.jsonl --entries /srv/grzybyfunkcjonalne/import/companions/entries.jsonl
✓ Copied 1 menus, 2 entries kept, 8 remapped, 6 dropped.
import-companions: no sitemap-settings.json, the sitemap keeps its default settings
import-companions: sitemap:import-winter --definitions /srv/grzybyfunkcjonalne/import/companions/sitemap-definitions.jsonl --page-url blog=/blog
ℹ Copying definition 1 (theme 'arsha') into the 'default' definition.
⚠ item 1 (type 'cms-page', reference 'blog'): copied as type 'url' with url '/blog'
⚠ item 2 (type 'all-journal-posts'): cmsPage 'blog-post' dropped (CMS pages are not supported)
⚠ item 3 (type 'all-journal-categories'): cmsPage 'index' dropped (CMS pages are not supported)
ℹ Definition copied: 4 items kept, 1 remapped, 0 dropped.
import-companions: ok
```
Dropped or remapped, in one place: 8 menu entries remapped (7 `link` -> `url`, 1 `blog_categories` -> `all-journal-categories`); 6 menu entries dropped (orphans under the missing parent 1); 1 sitemap item remapped (`cms-page` `blog` -> `url` `/blog`); 2 sitemap `cmsPage` values dropped (the host patterns apply); 0 FAQ rows dropped.
## UAT verification (through nginx)
- Release: binary sha256 `6e4d8964…` local == uploaded (previous kept as `bin/grzybyfunkcjonalne.prev`), config, scripts and Nuxt output uploaded, `bin/grzybyfunkcjonalne migrate` -> "migrations applied", import dry and real, `supervisorctl restart`; both programs RUNNING.
- Vhost: installed `/etc/nginx/sites-available/uat.grzybyfunkcjonalne.pl` sha256 `a0e3c29018b953fa568e7d40796b67ea4e7222eb1aef9164dc641e9dff6e8b79`, equal to the uploaded `deploy/grzybyfunkcjonalne-staging.conf` and to the committed file; the four locations at lines 148, 161, 176, 189. `sudo -n nginx -t`: syntax ok, test successful. The new paths answer from Go, so the reload was done (no extra reload needed).
- Security headers loop from DEPLOY.md over `/`, `/gfadmin/`, `/_journal/api/v1/categories`, `/backend`, plus `/sitemap.xml`, `/sitemap.xsl`, `/_menu/api/v1/menus/main-menu`, `/_faq/api/v1/faqs/what-mushrooms-can-give-you`: each response carries one copy of Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options, Referrer-Policy (the admin's own `same-origin`), Permissions-Policy and Content-Security-Policy-Report-Only, and no X-Powered-By. `/backend` 404. The four new paths answer 200 with `application/xml`, `text/xsl; charset=UTF-8`, `application/json`, `application/json`.
- POST to each of the four new paths through nginx: 403 (`limit_except GET HEAD`).
- Smoke, on the server with the `.curlrc` loopback resolve: `smoke: ok (105 urls)` (before the vhost install it was 101/105, exactly the four new paths failing).
- Task 5 verify: `check-deploy: ok` locally; on the server through nginx, `/sitemap.xml` Content-Type `application/xml` and the FAQ API `"data"` envelope -> `uat-companions-ok`.
## Sitemap comparison (SC-3 live)
Live `https://grzybyfunkcjonalne.pl/sitemap.xml` (fetched 2026-10-12 ~16:55Z; loc set identical to the 17:57-local baseline, only the `/` lastmod differs because PHP sets it to now) against the UAT sitemap fetched through nginx (`https://uat.grzybyfunkcjonalne.pl/sitemap.xml`, byte-equal modulo host to the earlier loopback fetch from Go):
| Check | Result |
|---|---|
| Loc paths | live 71, UAT 72; all 71 live paths present on UAT |
| Extra on UAT | `/blog/uat-16-2-blog-editor`: a post published on UAT during the Phase 16.2 UAT; live answers 404 for it. UAT data, not a source or mapping difference |
| Missing on UAT | none |
| Host | UAT locs use `https://uat.grzybyfunkcjonalne.pl` from Host and X-Forwarded-Proto (no frontend_url set); the xml-stylesheet points at `https://uat.grzybyfunkcjonalne.pl/sitemap.xsl` |
| changefreq and priority | identical on all 71 (`/` always 1.0, posts and `/blog` always 0.4, categories always 0.5) |
| lastmod | `/` and `/blog` have none in Go (URL items; PHP fakes `/` as now and used the blog.htm mtime for the cms-page); the other 69 differ in value: Go uses `updated_at`, which the Phase 16 import set to 2026-10-09 (17-05 deferred item 2) |
| Order | identical up to index 9, differs from index 10 (post title order under Postgres ICU collation vs MySQL) |
| 17-03 deferred item 4 | not triggered: the definition uses `all-journal-categories`, no nested `journal-category` item |
The plan's literal `loc-sets-equal` command therefore prints the one-line diff `> /blog/uat-16-2-blog-editor`; the plan allows this when the SUMMARY explains it, and on production (no UAT test post) the sets are equal.
## Decisions Made
See `key-decisions` in the frontmatter. In short: menu code `main-menu`, built-in type maps only, `--page-url blog=/blog` for the sitemap's CMS page, plugin-default sitemap settings, `winter_translate_locales` in the FAQ export, orphan entries dropped.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Exports produced from the operator's mysqldump instead of running the five SQL files on the server**
- **Found during:** Task 1
- **Issue:** The human placed a mysqldump in the companions folder instead of running the export SQL ("I created mysqldump in that folder on server, must suffice").
- **Fix:** The dump was copied to the scratchpad, loaded into a throwaway `mariadb:11` container with `--network none`, the plugin export SQL files were run against it, and the JSONL outputs were uploaded to `/srv/grzybyfunkcjonalne/import/companions/`. The container, its anonymous volume and every local copy of the dump were deleted afterwards (verified). The live PHP database was not touched.
- **Verification:** each JSONL parses; sha256 prefixes recorded above.
**2. [Rule 3 - Blocking] FAQ export joins `winter_translate_locales`**
- **Found during:** Task 1
- **Issue:** The plugin export SQL joins `golem15_translate_locales`; this site's tables are `winter_translate_*`.
- **Fix:** sed on the SQL at run time, documented in the host's `scripts/php-export/README.md` section 4. No Go code references `winter_translate_*` (the human's point that sm translate uses no winter prefix stands).
- **Committed in:** 6441b17 (README)
**3. [Rule 2 - Missing critical] `sitemap:import-winter --page-url reference=/path`**
- **Found during:** Task 1 discovery
- **Issue:** The PHP definition lists `/blog` through a `cms-page` item; the importer dropped CMS-page items, so the Go sitemap would have lost `/blog` and failed SC-3.
- **Fix:** New flag in sm-sitemap-plugin that copies a named CMS-page item as a `url` item (reported as remapped); `import-companions.sh` gained `--page-url` and `migrate.sh` `--companions-page-url`, used instead of the plan's `--map` (no `--map` is needed on this site).
- **Files modified:** ../sm-sitemap-plugin/console/import_winter.go, sitemap_smoke_test.go, README.md; host scripts
- **Committed in:** sm-sitemap-plugin 5a260db, host 6441b17
**4. [Rule 3 - Blocking] No `sitemap-settings.json`**
- **Found during:** Task 1
- **Issue:** The PHP site has no sitemap `system_settings` row, so the fifth export file the Task 1 verify requires cannot exist.
- **Fix:** Verify adapted to "companions-ready (sitemap-settings.json N/A: PHP row absent)"; `import-companions.sh` treats the file as optional and reports it; the host fixture keeps a settings file so the test still covers the settings import.
**5. [Rule 3 - Blocking] `go mod tidy` in sm-journal-plugin moved `goccy/go-yaml` to a direct require**
- **Found during:** Task 2
- **Fix:** Committed with the v0.1.23 require in 06d0c9b (go.mod and go.sum only).
**6. [Rule 3 - Blocking] Vhost install needs root**
- **Found during:** Task 5
- **Issue:** The deploy user's sudo rules cover `nginx -t`, reloads and `supervisorctl`, not copying into `/etc/nginx`.
- **Fix:** A dynamic human-action checkpoint; the human installed the vhost and replied (verbatim above). The installed file's sha256 matches the upload and the committed file.
**7. [Rule 3 - Blocking] Remote checks run on the server through nginx**
- **Found during:** Task 5
- **Issue:** The UAT basic auth gate answers 401 from outside, and the plan's verify ran `smoke.sh --url` and curl from the local machine.
- **Fix:** As DEPLOY.md "Smoke" prescribes: smoke and the verify curls run on the server with curl resolving the staging host to 127.0.0.1, so every request passes through nginx and its locations. `check-deploy.sh` ran locally.
---
**Total deviations:** 7 auto-fixed (1 missing critical, 6 blocking). **Impact on plan:** The `--page-url` flag is a small, reported extension of the importer that SC-3 needed; the rest adapt the plan to the operator's dump, this site's table prefix and the UAT gate. No scope creep.
## Issues Encountered
- Before the vhost install, the on-server smoke failed 4 of 105 lines (exactly `/sitemap.xml`, `/sitemap.xsl`, the `/_menu` API and the `/_faq` API, which reached Nuxt). After the install: 105/105.
- Deferred (logged in `deferred-items.md`, committed 2440560): every public Go response sets `golem15.translate.locale=en` (pre-existing translate middleware, now also on `/_menu/`, `/_faq/` and the sitemap; follow-up in sm-translate-plugin), and the sitemap `lastmod` of imported Journal rows is the 2026-10-09 import time (a one-off copy of PHP `updated_at` before the production cutover would align it).
- Todo `site-cache-purge-for-menu-and-faq.md` (7290d8f): menu and FAQ saves do not purge the Nuxt swr cache yet (edits show within 300 s).
## g15office
No g15office MCP tools were available to this executor. The orchestrator must:
1. Create the g15office task for `.planning/todos/pending/site-cache-purge-for-menu-and-faq.md` (type Improvement, description ending `Local: .planning/todos/pending/site-cache-purge-for-menu-and-faq.md`), make it a subtask of the Phase 17 task `ejoiuzy4` if appropriate, and write the ID back into the todo's frontmatter (`g15office: <id>`).
2. Post a short comment on the Phase 17 milestone task `ejoiuzy4` (status stays In progress; plan 06 remains): "17-05 executed: grzybyfunkcjonalne UAT reads menu and FAQ from the Go plugins through vue-menu-layer and vue-faq-layer v0.1.0, sitemap served by Go via nginx; Winter rows imported; smoke 105/105; sitemap loc set matches live (one UAT-only post). Production not deployed."
## User Setup Required
None beyond what Task 1 and the vhost checkpoint already covered.
## Next Phase Readiness
- Plan 17-06 (unit tests) can start; the layers and host carry node and Go tests already.
- Open human-check items for `/msd-verify-work` (end of phase): header dropdown hover and click, mobile toggle, Esc, active link on a category page, FAQ items opening; admin edit of a menu entry title, drag and keyboard reorder, reference dropdown following the type, FAQ richeditor edit and new question row with the change visible after the cache TTL, Settings, Sitemap and Sitemap settings forms; the XSL view of `/sitemap.xml`. The human already confirmed: "i see sitemap loading, all plugins in backend".
- Production deploy and pushing summercms.go are not approved.
---
*Phase: 17-blog-companions*
*Completed: 2026-10-12*