docs(33-15): release notes, release log and deferred items for the gap fixes

This commit is contained in:
Jakub Zych
2026-11-04 23:30:59 +01:00
parent 1f968994e1
commit fec697bb15
6 changed files with 37 additions and 7 deletions

View File

@@ -16,8 +16,13 @@ Excerpt: One H1 per post page, a heading outline and link attributes in the edit
## Changed
- **Requires SummerCMS v0.6.1.**
- **Content descriptors list only locales with content.** Posts, categories, tags and authors still describe every enabled locale, but a locale gets an address (and so an hreflang link and a sitemap address) only when the record has content in it: a post or a category when its own translation in that locale holds text (title, content or excerpt; a category's name or description; a translated slug alone does not count), a tag or an author when at least one of its published posts is translated into it, and the default locale always. Sites with an enabled but untranslated locale stop advertising its fallback pages. Menu items keep every locale.
- The H1 to H2 conversion never rewrites a markdown body: a code sample that shows `<h1>` keeps it, and only the rendered HTML is converted (API saves, imports, `journal:fix-headings` and translations).
- Setting an image's alt text through the API or MCP on a markdown post writes it into the markdown (`![alt](src)`), so the next save keeps it; an image the markdown does not hold that way is refused with a validation error.
- **Upgrade:** run the migrations, then `journal:fix-headings` (dry run first) to convert H1s already stored, and `journal:image-variants` once to write the variants of existing featured images. For the faster WebP encoder install libwebp on the server; the binary is now dynamically linked against glibc (see SummerCMS v0.6.1).
## Fixed
- `journal:image-variants` runs from the application's CLI: without a running server it opens the database and the uploads bucket itself instead of stopping with "database is not configured".
- `journal:fix-headings` runs from the application's CLI too.
- A very tall featured image (taller than 4096 px at a variant's width) no longer stops the image variants job or the `journal:image-variants` backfill: those widths are skipped with a warning.

View File

@@ -13,7 +13,9 @@ Excerpt: Structured data, breadcrumbs, hreflang and pagination in every resolve
- Publisher, local business and home crumb settings; per-record schema type, Service fields and custom JSON-LD with validation and a graph preview in the SEO tab.
- Pagination in the query or path form with title and description suffixes.
- Site audit settings, a nightly crawl, Site audit and Crawls screens and their MCP tools. With `golem15.seo.crawl_chrome_path` set, a rendered comparison through headless Chrome (chromedp).
- Crawl cancel and time limits: cancelling a running crawl shows it as **Cancelling** until its job stops, then it ends cancelled with the pages fetched so far; no other crawl starts before that. A crawl that reaches the 6-hour job limit ends failed and keeps what it fetched, and a crawl whose job disappeared (for example after a worker restart) is ended by the next start instead of staying "running".
- A sitemap filter for sm-sitemap-plugin that leaves noindex, canonicalised and redirected addresses out.
- The route report takes an optional `noindex` list: the static paths the frontend serves noindex (pages behind a login, pages with a noindex robots meta). The sitemaps leave them out, Additional addresses included, and a changed list asks the sitemap for a rebuild. vue-seo-layer v0.2.0 sends it; a frontend that sends no list keeps working.
- Links settings (outbound rules, new tab) and site cache purges after SEO writes.
## Changed
@@ -25,3 +27,7 @@ Excerpt: Structured data, breadcrumbs, hreflang and pagination in every resolve
- Registry pages store their created and updated times in UTC, so a page without a content date no longer gets a sitemap lastmod in the future on a server east of UTC.
- A content type's plain label is named untranslated in the address-owned refusal.
- The crawler and its headless Chrome stay on the site's own port, so page code cannot reach other services on the server's address, and a link to another port is no longer reported as a page of the site.
- Addresses with non-ASCII characters (for example `/blog/zażółć`) match their CMS records, focus keyphrases and link counts in the audit.
- A Chrome that hangs while opening a tab no longer stalls the crawl: opening the tab and rendering the page share one 25-second limit.
- A noindex Additional address of the sitemap found by the audit names `sitemap_update_settings` (remove it there) instead of a tool that cannot fix it.

View File

@@ -10,17 +10,19 @@ Excerpt: The sitemap is now an index of per-type files with real lastmod, hrefla
- hreflang alternates for records with more than one translated locale, with `x-default`.
- Image sitemap files from each page's images, and a Google News sitemap (`sitemap-news.xml`) for articles published in the last 48 hours, limited to chosen types or categories.
- IndexNow: a key, its key file and pings of added, changed and removed addresses in production.
- IndexNow: a key, its key file and pings of added, changed and removed addresses in production, including a record deleted in the admin and a content edit that keeps its address.
- An HTML sitemap API (groups, category trees, paging) for the site's sitemap page.
- One settings screen (Settings › Sitemaps) with the files table, the addresses left out by reason, the content types (include, file name, headings) and the additional addresses; a management API and MCP tools.
- A filter contract (`contract.Filter`) that lets another plugin leave addresses out (noindex, canonical elsewhere, redirect) and group them.
- `contract.Rebuilder`, which lets another plugin ask for a debounced rebuild (the SEO plugin does when the frontend reports new noindex pages).
## Changed
- **Breaking.** The WinterCMS byte-format sitemap, priority tiers, the definitions screen, the old settings tile and `/sitemap.xsl` are removed. `<priority>` and `<changefreq>` are no longer written. The config keys `golem15.sitemap.cache_seconds` and `golem15.sitemap.priority_tiers` and the events `BeforeAddItem` and `AddItem` are gone.
- **Upgrade:** run the migrations: the definition's url items move to the additional addresses, `frontend_url` to the site address, `frontend_default_locale` to the default locale; the old table stays, unread. Serve the root file names from `/_sitemap/files/<name>` (vue-seo-layer v0.2.0 does it; with nginx, a proxy rule) and remove any nginx `/sitemap*` location. Run `sitemap:rebuild` once after the release. Remove additional addresses that the site serves noindex.
- **Upgrade:** run the migrations: the definition's url items move to the additional addresses (a relative path such as `o-nas` gets its leading slash, spaces are percent-encoded, a duplicate is copied once), `frontend_url` to the site address, `frontend_default_locale` to the default locale; the old table stays, unread. An item that cannot be an additional address (an absolute URL with `&`, another scheme, a malformed item) is not copied: the startup log names each one as `golem15.sitemap: definition url item not copied` with the item, its value and the reason, so check the log after the upgrade and add those addresses by hand if they are still wanted. Serve the root file names from `/_sitemap/files/<name>` (vue-seo-layer v0.2.0 does it; with nginx, a proxy rule) and remove any nginx `/sitemap*` location. Run `sitemap:rebuild` once after the release. Additional addresses that the site serves noindex are left out automatically once the frontend reports its noindex pages (sm-seo-plugin v0.2.0 with vue-seo-layer v0.2.0); otherwise remove them.
- **Requires SummerCMS v0.6.1.**
## Fixed
- An additional address that a content record already lists is written once (it is counted as "Already listed by a content type" on the settings screen).
- An HTML sitemap heading follows each language until an editor sets one; saving the settings no longer stores the type's name in the admin's language as every language's heading.

View File

@@ -19,6 +19,7 @@ Excerpt: Content descriptors carry dates, parents, authors, media and headings;
- `lagoon.Validate` checks `min` and `max` as a length on every field that is not `integer` or `numeric`: characters for a string, items for a list. **A stored value longer than its rule's `max` now fails its next admin save until it is shortened.** Before upgrading, check the long text columns your plugins validate with a `max` (for example a description with `max:5000` on a TEXT column).
- A `min` or `max` bound that is not a whole number is now an error instead of being ignored.
- **Binaries are no longer fully static when they link `attach/webp`.** The WebP encoder loads the system libwebp through purego, so a `CGO_ENABLED=0` build is dynamically linked against glibc (`ld-linux-x86-64.so.2`, `libc.so.6`, `libdl.so.2`, `libpthread.so.0`). It runs on Debian and Ubuntu servers, not in `scratch`, distroless-static or Alpine images. With libwebp installed the fast encoder is used; without it, a slower built-in one. Build with `-tags nodynamic` for a static binary that always uses the built-in encoder.
- **`attach/webp` also decodes WebP.** Importing it registers the encoder library's WebP decoder ahead of the pure Go one for the whole process, so WebP uploads (thumbnails, size checks) are decoded by the system libwebp when it is installed. Keep libwebp patched, or build with `-tags nodynamic`, which decodes with the built-in copy.
## Fixed

View File

@@ -21,6 +21,7 @@ Plan 33-01 seams:
- `feat(cabana): per-request options for plugin field types` (4cd35c9): `pact.FormFieldType.RequestOptions`, merged under the fields.yaml options of each form schema request.
- `test: phase 33 gate script` (6ea43bb, `scripts/check-phase33.sh`).
- Framework commits of later Phase 33 plans, if any, are added here by those plans.
- `docs(lagoon): attach/webp also decodes WebP` (1f96899, plan 33-15, L1 WR-02): docs only, the lagoon README and docs/database/attachments.md; no Go file changes.
Also on master since v0.6.0, carried by the tag: 22 planning-docs commits of Phase 20 (677e30f..399efed); no code, module or docs change.

View File

@@ -11,7 +11,7 @@ Out-of-scope discoveries logged during execution; not fixed in the plan that fou
## From 33-02 (sm-journal-plugin)
- **Saving Settings › Journal in the admin answers 500 (pre-existing, framework).** The admin settings save (`cabana` `settingsPut`) runs `lagoon.Validate` over every rule in `models.Settings.Rules()`. `lagoon.Validate` does not know the `string` and `url` rule tokens and returns `lagoon: unrecognized validation rule "string"`. The Journal rules have used `string` (`rss_title`) and `url` (`rss_image_url`) since before Phase 33, so the screen could not be saved before 33-02 either. The same tokens appear in sm-feedback-plugin and sm-paymentgateway-plugin models. Because of this, the new Headings and links switch cannot be saved from the admin until the framework is fixed. `PUT /_journal/api/v1/settings` and MCP `journal_update_settings` work: the 33-02 handler checks `string` and `url` itself.
status: open
status: resolved (framework d2ce3a2, `feat(lagoon): accept string and url rules in lagoon.Validate`, carried by v0.6.1; the stale entry closed in 33-15)
- **Possible fix:** teach `lagoon.Validate` the `string` and `url` tokens (`lagoon.ValidateRequest` already knows `string`). This is a framework change and belongs in v0.6.1 or a follow-up patch.
- **Why deferred:** 33-02 commits only to sm-journal-plugin, and the fix belongs in the framework. Dropping the tokens from Journal's `Rules()` would loosen validation in a core plugin.
@@ -113,11 +113,11 @@ Out-of-scope discoveries logged during execution; not fixed in the plan that fou
- **Possible fix:** in `classes.Rebuild`, skip an additional address whose absolute URL a content type already lists, or report the overlap on the settings screen.
- **Why deferred:** a plugin change in sm-sitemap-plugin, outside 33-12's repositories; the demo works around it in its deploy steps.
- **An additional address that the site serves noindex stays in the sitemap (sm-sitemap-plugin, sm-seo-plugin).** The SEO filter judges the CMS's effective SEO only, while `requiresAuth` and `seo.robots` page meta (D-23) live in Nuxt. A page marked that way and also listed as an additional address (the demo's migrated `/subscriptions`) appears in the sitemap with `X-Robots-Tag: noindex, nofollow`. smoke-seo.sh fails on it, and the demo's DEPLOY.md removes the address.
status: open, a contract gap (33-14): the sitemap plugin already asks the SEO filter about additional addresses, but the CMS cannot know a noindex that lives only in Nuxt page meta; the route report leaves those pages out, so nothing tells the CMS. Needs the route report to carry the noindex static paths (vue-seo-layer) and the SEO plugin to store and use them (a new field or table): more than a small fix
status: resolved (gap G1; CMS side 33-15: sm-seo-plugin 9de6b2e stores the route report's `noindex` paths in `golem15_seo_noindex_paths` and the sitemap filter leaves them out with the reason noindex, sm-sitemap-plugin 6b67ab7 `contract.Rebuilder` rebuilds on a changed set, and the audit names `sitemap_update_settings` for a noindex Additional address; layer side 33-16: vue-seo-layer sends the list)
- **Possible fix:** the route report also sends the noindex static paths (as `noindex` entries), and the filter leaves them out of the sitemaps.
- **Why deferred:** a contract change across vue-seo-layer and sm-seo-plugin.
- **The site answer lists the `journal-tag` pattern while `golem15.journal.tag_pages` is false (sm-seo-plugin).** `classes/site.go` adds every content type's pattern. vue-seo-layer then logs `[vue-seo-layer] drift: the CMS's journal-tag URLs (/blog/tag/:slug) match no Nuxt route` at every start of the demo, although tag paths no longer resolve (TestSEOOutput).
status: open, a descriptor gap (33-14): no content descriptor field says that a type resolves no URLs, so sm-seo-plugin cannot tell. Possible fixes: an additive `pact.ContentDescriptor` field (for example `Routable func(ctx) bool`), or sm-journal-plugin registering journal-tag without a DefaultPattern while tag pages are off (the layer already skips an empty pattern)
status: deferred to v0.6.2 (gap G2, 33-15): it needs the framework. `signpost.Activate` refuses a menu item type whose DefaultPattern does not start with `/` (modules/signpost/signpost.go), so sm-journal-plugin cannot register journal-tag without a pattern while tag pages are off; dropping the tag descriptor instead would remove the tag form's SEO tab and the sitemap's types row. Possible fixes: an additive `pact.ContentDescriptor` field (for example `Routable func(ctx) bool`) read by `classes/site.go`, or a signpost rule that accepts an empty DefaultPattern for a type that resolves no URL
- **Possible fix:** leave out a type whose descriptor resolves no URL (the Journal could report tag pages off on its descriptor).
- **Why deferred:** log noise only; a plugin change outside 33-12.
- **`Duplicated imports "JournalImageVariant"` on every build of an app with vue-journal-layer (vue-journal-layer).** The type is declared in `app/utils/srcset.ts` and re-exported from `app/utils/types.ts`, both auto-imported.
@@ -132,7 +132,7 @@ Out-of-scope discoveries logged during execution; not fixed in the plan that fou
## From 33-13 (grzybyfunkcjonalne.pl on local links)
- **An enabled locale without content gives every page an `/en/` hreflang link and sitemap address (sm-seo-plugin, sm-sitemap-plugin).** sm-grzybyfunkcjonalne-app's translate plugin kept its seeded `en` row enabled (migrate.sh only made `pl` the default). Every record then counts as available in `en` through fallback: post pages render `<link rel="alternate" hreflang="en" href="…/en/blog/…">` and `sitemap-posts-1.xml` lists each post twice, with `x-default` on the `/en/` address, all 404 on the Polish-only site. The 33-13 patch makes `pl` the only enabled locale in migrate.sh step 3 and in a DEPLOY.md release step (`UPDATE golem15_translate_locales SET is_enabled = (code = 'pl')`).
status: open
status: resolved (gap G3, 33-15, sm-journal-plugin 0a62090: the Journal descriptors give a locale a URL only when the record has content in it; posts and categories by their own translation, tags and authors through a published post translated into it, the default locale always; an untranslated locale keeps its entry with URL "", which the SEO and sitemap plugins drop). The grzyby data fix in migrate.sh and DEPLOY.md stays valid; an untranslated address still answers with the default-locale content (see "From 33-15")
- **Possible fix:** list a locale's address only when the record has content in it (a translation row, or the default locale), not through fallback.
- **Why deferred:** a plugin behaviour change; the site is fixed by its data.
- **Saving a registry page through `PUT /_seo/api/v1/pages/{id}` writes `updated_at` in local time (sm-seo-plugin).** In the 33-13 rehearsal (database in UTC, machine in CET), the renamed `/trwaja-prace` got `updated_at` 17:58:54 while the route-reported pages got 16:58:54, so `sitemap-pages-1.xml` and the index carried a lastmod one hour in the future.
@@ -152,7 +152,7 @@ Out-of-scope discoveries logged during execution; not fixed in the plan that fou
- **Possible fix:** declare `seo`, `seo:read`, `sitemap` and `sitemap:read` scopes.
- **Why deferred:** a plugin feature.
- **`journal:image-variants` cannot run from the generated CLI (framework or sm-journal-plugin).** It stops with `journal: database is not configured`, like `journal:reindex` (which grzyby wraps as `grzyby:reindex`). The one-time backfill of existing featured images that 33-11 names needs a wrapper or a framework fix; new uploads get their variants from the worker in `serve`.
status: resolved (33-14, sm-journal-plugin fc3c255: without a published database or bucket the command opens `database.dsn` and `storage.uploads.bucket_url` itself). `journal:reindex`, `journal:fix-headings`, `journal:import-posts` and `journal:export-posts` still use the published database only
status: resolved (33-14, sm-journal-plugin fc3c255: without a published database or bucket the command opens `database.dsn` and `storage.uploads.bucket_url` itself; 33-15, sm-journal-plugin 5f683cb: `journal:fix-headings` does the same, L1 WR-01). `journal:reindex`, `journal:import-posts` and `journal:export-posts` still use the published database only
- **Possible fix:** the generated CLI opens the database for plugin commands, or the app adds `grzyby:image-variants`.
- **Why deferred:** decided with 33-14's release steps.
- **The Journal's seeded `uncategorized` category is listed in `sitemap-categories-1.xml` (sm-grzybyfunkcjonalne-app data).** It has no posts and the PHP sitemap never listed it (7 categories), but `/uncategorized` answers 200 and the per-type file lists every published category.
@@ -180,3 +180,18 @@ Out-of-scope discoveries logged during execution; not fixed in the plan that fou
- **Why deferred:** framework-wide behaviour, not one of the six fixes.
- **The route report sent the i18n copies of every page as pages of their own (vue-seo-layer).** Found by the demo locale rehearsal: with `prefix_except_default` the report sent `/pl`, `/pl/contact`, … next to `/`, `/contact`, and the registry made seven untranslated pages of them.
status: resolved (33-14, vue-seo-layer b3a0078: routes named with another locale's suffix are left out; their translations are set per page in SEO › Pages). A follow-up could report the locale paths so the plugin fills each page's addresses itself (a contract change).
## From 33-15 (Go plugins: gap fixes before the release)
Deferred to the v0.6.2 backlog item, by repository. Fixed in 33-15: G1 (CMS side), G3, L1 CR-01, CR-02, WR-01, WR-03, WR-05 (WR-02 as docs), L2 WR-01 to WR-05 and IN-01, L3 CR-01, WR-01, WR-03, WR-04 and WR-06.
- **sm-sitemap-plugin:** L2 WR-06 (a default file slug can collide with a reserved or another type's slug), WR-07 (no 50 MB byte guard on image and hreflang files), WR-08 (Additional addresses on other hosts); info IN-02 to IN-11.
status: open (v0.6.2)
- **sm-seo-plugin:** L3 WR-02 (crawl limits beyond the job timeout and memory: validate `crawl_max_pages × crawl_delay`, cap the frontier, store pages in chunks), WR-05 (recheck fetches inside a write transaction); info IN-01 to IN-07; G2 (the journal-tag pattern while tag pages are off, see the 33-12 entry: needs an additive pact field such as `ContentDescriptor.Routable`, a framework change, or a signpost rule for an empty DefaultPattern).
status: open (v0.6.2)
- **sm-journal-plugin:** L1 WR-04 (images/alt keys `""` and the default locale code apart), WR-06 (author pages publish backend logins as slugs and names), WR-07 (the settings API's stale `string`/`url` workaround); info IN-01 to IN-04, IN-08. An untranslated locale's address still answers with the default-locale content and a self canonical; it is no longer advertised in hreflang or the sitemaps, and a canonical to the default locale or a 404 there is a separate decision.
status: open (v0.6.2)
- **summercms.go:** L1 WR-02 as code (decode WebP in `attach` with the pure Go decoder explicitly, or a decision note; 33-15 documents the behaviour only); info IN-05, IN-06, IN-07, IN-09; verifier warnings W1 (the home title template once `/` is registered), W2 (swr localhost canonicals), W3 (route report names without diacritics), W4 (GORM local timestamps), W5 (the min/max length change in a patch release, a release decision).
status: open (v0.6.2)
- **Nuxt layers:** L4 WR-06, WR-07 and the L4 info items, planned and deferred in 33-16.
status: open (v0.6.2)