diff --git a/.planning/WINDOWS.md b/.planning/WINDOWS.md index d63196a5..2f1cac7f 100644 --- a/.planning/WINDOWS.md +++ b/.planning/WINDOWS.md @@ -2,9 +2,9 @@ schema_version: 1 open_count: 23 waived_count: 0 -fixed_count: 7 -total_count: 30 -last_updated: 2026-11-04T10:26:56.940Z +fixed_count: 8 +total_count: 31 +last_updated: 2026-11-04T11:42:13.158Z --- # Broken Windows Ledger @@ -43,8 +43,9 @@ last_updated: 2026-11-04T10:26:56.940Z | 26 | 19.7 | deviation | modules/cabana | | 19.7-01: TestRecordActionPromptRun added on Postgres to prove Run receives the trimmed prompt value | open | | 2026-10-22T14:44:56.449Z | | | 27 | 19.7 | unrun-verify | scripts/check-phase19.6.1.sh | | 19.7-01: check-phase19.6.1.sh --all stopped by the host (memory pressure) after the cabana and surf Postgres suites passed; re-run on the Docker host | open | | 2026-10-22T14:44:56.679Z | | | 28 | 33 | deviation | modules/lagoon/validate.go | | 33-02: lagoon.Validate rejects the string and url rule tokens, so the Journal admin settings save answers 500 (pre-existing); the Headings and links switch is saved through the settings API/MCP until fixed | open | | 2026-11-04T09:39:12.905Z | | -| 29 | 33 | stub | contract/contract.go | | sm-sitemap-plugin: contract.ListingSettings HTMLEnabled, HTMLPaths, HTMLPageSize and KeyFile stay zero and golem15_sitemap_urls.images stays [] until 33-04 adds the HTML sitemap, IndexNow and image settings | open | | 2026-11-04T10:26:56.754Z | | +| 29 | 33 | stub | contract/contract.go | | sm-sitemap-plugin: contract.ListingSettings HTMLEnabled, HTMLPaths, HTMLPageSize and KeyFile stay zero and golem15_sitemap_urls.images stays [] until 33-04 adds the HTML sitemap, IndexNow and image settings | fixed | | 2026-11-04T10:26:56.754Z | 2026-11-04T11:41:58.774Z | | 30 | 33 | deviation | models/settings/fields.yaml | | sm-sitemap-plugin: the Additional addresses lastmod is a text field read as YYYY-MM-DD (server-checked, errors.lastmod) because the framework repeater accepts no datepicker column; swap to a datepicker once cabana supports it in repeaters | open | | 2026-11-04T10:26:56.940Z | | +| 31 | 33 | deviation | models/settings/fields.yaml | | sm-sitemap-plugin: News sources is a taglist with custom tags and no options (values listed by the news_sources partial, server-checked) because cabana taglist options are a fixed YAML list and form extensions take no taglist; switch to options from the article types and categories once the framework offers per-request taglist options | open | | 2026-11-04T11:42:13.158Z | | ````json [ @@ -419,10 +420,10 @@ last_updated: 2026-11-04T10:26:56.940Z "file": "contract/contract.go", "line": null, "description": "sm-sitemap-plugin: contract.ListingSettings HTMLEnabled, HTMLPaths, HTMLPageSize and KeyFile stay zero and golem15_sitemap_urls.images stays [] until 33-04 adds the HTML sitemap, IndexNow and image settings", - "status": "open", + "status": "fixed", "reason": "", "recorded_at": "2026-11-04T10:26:56.754Z", - "resolved_at": null, + "resolved_at": "2026-11-04T11:41:58.774Z", "milestone": "v1.0" }, { @@ -437,6 +438,19 @@ last_updated: 2026-11-04T10:26:56.940Z "recorded_at": "2026-11-04T10:26:56.940Z", "resolved_at": null, "milestone": "v1.0" + }, + { + "id": 31, + "kind": "deviation", + "phase": "33", + "file": "models/settings/fields.yaml", + "line": null, + "description": "sm-sitemap-plugin: News sources is a taglist with custom tags and no options (values listed by the news_sources partial, server-checked) because cabana taglist options are a fixed YAML list and form extensions take no taglist; switch to options from the article types and categories once the framework offers per-request taglist options", + "status": "open", + "reason": "", + "recorded_at": "2026-11-04T11:42:13.158Z", + "resolved_at": null, + "milestone": "v1.0" } ] ```` diff --git a/.planning/phases/33-seo-output-audit-and-content/33-04-SUMMARY.md b/.planning/phases/33-seo-output-audit-and-content/33-04-SUMMARY.md new file mode 100644 index 00000000..d50fbeab --- /dev/null +++ b/.planning/phases/33-seo-output-audit-and-content/33-04-SUMMARY.md @@ -0,0 +1,320 @@ +--- +phase: 33-seo-output-audit-and-content +plan: 04 +subsystem: sitemap +tags: [sm-sitemap-plugin, image-sitemap, news-sitemap, indexnow, fetchguard, html-sitemap, management-api, mcp, settings-singleton] + +requires: + - phase: 33-seo-output-audit-and-content + provides: "33-03 slot table, rebuild (RebuildResult Added/Changed/Removed), settings singleton, contract.Listing; 33-01 content facts (Kind, PublishedAt, Parents, Images)" +provides: + - "Image sitemap files sitemap-images-.xml (image_file_no, stable numbering, at most 1000 images per page), images_enabled default on (D-31)" + - "Google News sitemap sitemap-news.xml: articles of the last 48 hours, newest first, at most 1000, news_sources type:/category: values, news_enabled default off (D-31)" + - "IndexNow: 32-hex key on save (crypto/rand), indexnow_new_key rotation, GET /_sitemap/indexnow/{file}, job golem15.sitemap.indexnow from rebuild diffs in production only, fetchguard client limited to api.indexnow.org, S2 status partial (D-31)" + - "HTML sitemap API GET /_sitemap/api/v1/html and /_sitemap/api/v1/html/{group} in the contract vue-seo-layer 33-08 consumes (D-34, D-24)" + - "Settings tabs Images and News, IndexNow, HTML sitemap; contract.Listing.Settings fills HTMLEnabled, HTMLPaths, HTMLPageSize, KeyFile" + - "Management API GET/PUT /_sitemap/api/v1/settings, POST /_sitemap/api/v1/rebuild and MCP tools sitemap_get_settings, sitemap_update_settings, sitemap_rebuild (D-10)" +affects: [33-05, 33-07, 33-08, 33-13, 33-14] + +actuals: + tokens: 55000 + tasks: 3 + commits: 6 +plan_head_before: 64453d2a914f00a8310f76f806a8858d5d4345da +plan_head_after: 291cf176db68ef498f8b8a57361d2418f7b0ebd0 + +tech-stack: + added: [] + patterns: + - "A second numbering of the same slot rows (image_file_no) with AssignSlots and the same renumber flag as file_no" + - "Outbound vendor call as a conga job through a single-host fetchguard client; the test routes it with fetchguard.WithTransport" + - "Production-only side effect checked twice: before queueing and again in the job" + - "Values a fixed-option taglist cannot offer listed by a server-rendered partial next to it, and checked on save" + +key-files: + created: + - ../sm-sitemap-plugin/updates/202610280004_add_golem15_sitemap_media_news_indexnow.go + - ../sm-sitemap-plugin/classes/images.go + - ../sm-sitemap-plugin/classes/news.go + - ../sm-sitemap-plugin/classes/indexnow.go + - ../sm-sitemap-plugin/classes/htmlgroups.go + - ../sm-sitemap-plugin/classes/files.go + - ../sm-sitemap-plugin/classes/typesettings.go + - ../sm-sitemap-plugin/controllers/api/indexnow.go + - ../sm-sitemap-plugin/controllers/api/html.go + - ../sm-sitemap-plugin/controllers/api/auth.go + - ../sm-sitemap-plugin/controllers/api/settings.go + - ../sm-sitemap-plugin/controllers/api/logger.go + - ../sm-sitemap-plugin/controllers/settings/_news_sources.htm + - ../sm-sitemap-plugin/controllers/settings/_indexnow_status.htm + - ../sm-sitemap-plugin/mcp.go + - ../sm-sitemap-plugin/sitemap_extras_test.go + modified: + - ../sm-sitemap-plugin/classes/rebuild.go + - ../sm-sitemap-plugin/classes/writer.go + - ../sm-sitemap-plugin/classes/listing.go + - ../sm-sitemap-plugin/classes/settings.go + - ../sm-sitemap-plugin/contract/contract.go + - ../sm-sitemap-plugin/models/url.go + - ../sm-sitemap-plugin/models/settings.go + - ../sm-sitemap-plugin/models/settings/fields.yaml + - ../sm-sitemap-plugin/models/settings_test.go + - ../sm-sitemap-plugin/controllers/settings.go + - ../sm-sitemap-plugin/extensions_settings.go + - ../sm-sitemap-plugin/jobs.go + - ../sm-sitemap-plugin/routes.go + - ../sm-sitemap-plugin/config/config.yaml + - ../sm-sitemap-plugin/lang/en/lang.yaml + - ../sm-sitemap-plugin/lang/pl/lang.yaml + - ../sm-sitemap-plugin/README.md + - ../sm-sitemap-plugin/plugin.yaml + - ../sm-sitemap-plugin/content_fixture_test.go + - ../sm-sitemap-plugin/sitemap_smoke_test.go + - ../sm-sitemap-plugin/commands_documented_test.go + - .planning/phases/33-seo-output-audit-and-content/COVERAGE.md + +key-decisions: + - "HTML sitemap API contract follows what vue-seo-layer 33-08 already reads: overview {groups:[{key, slug, title, total, more, entries:[{title, url, children?}]}]}, group page {group, title, page, pages, entries}; groups are addressed by their file slug; locale and page and page_size and total are extra fields" + - "html_path is stored as a JSONB locale map (html_paths) through the plugin's own form extension, not the translation writer (same reason as 33-03's type titles: the settings row has no translatable host model); a save merges the locales sent" + - "news_sources is a taglist with custom tags and no options; the values (type:, category::) are listed by the news_sources partial and checked on save, because cabana taglist options are a fixed YAML list and form extensions take no taglist" + - "IndexNow 5xx answers and network errors retry like 429 (five attempts); 400, 403 and 422 are recorded without retry; S2's err_rate time is the queued retry's scheduled_at in river_job, shown only while one is pending" + - "Image sitemap files use the same file size and renumber flag as the per-type files" + - "S1 and the index share classes.SitemapFiles, so the files table now lists the image and News files too (the index count counts addresses only once)" + - "The content type settings write moved to classes.SaveTypeSettings, shared by the form extension and PUT /_sitemap/api/v1/settings (types.. refusals)" + +patterns-established: + - "Plugin management API mirrors the SEO plugin's manage wrapper: cabana.ResolveBackend (token cover golem15.sitemap.*) plus cabana.Allows, 422 validation_failed with translated messages, X-Robots-Tag noindex" + - "A compile-only pier.Mount in the plugin's tests proves the MCP declarations bind to registered routes" + +requirements-completed: [SC-1, D-31, D-34, D-24, D-10, D-30] + +coverage: + - id: D1 + description: "Image sitemap: featured then body images, deduplicated, unsafe dropped, absolute, at most 1000 per page, only image:image/image:loc, stable numbering with holes and renumber on file size change, images_enabled off removes the files from the index and answers 404" + requirement: "D-31" + verification: + - kind: integration + ref: "../sm-sitemap-plugin/sitemap_extras_test.go#TestImageSitemap" + status: pass + human_judgment: false + - id: D2 + description: "News sitemap: 1 h and 47 h old articles listed, 49 h not, never another kind, publication name (news_publication else app.name), language, date, escaped title, type and category sources, newest 1000 of 1007, off by default (no index entry, 404), malformed source refused, partial lists the values" + requirement: "D-31" + verification: + - kind: integration + ref: "../sm-sitemap-plugin/sitemap_extras_test.go#TestNewsSitemap" + status: pass + human_judgment: false + - id: D3 + description: "IndexNow in production: key on save (32 hex), key file text/plain, other names 404, one job with the new and the removed address, POST {host, key, urlList} to api.indexnow.org without keyLocation, 202 recorded, 403/422/400 recorded without retry, 429 retries, S2 states and callouts, other hosts never sent, new key makes the old file 404, off takes the file down" + requirement: "D-31" + verification: + - kind: integration + ref: "../sm-sitemap-plugin/sitemap_extras_test.go#TestIndexNow" + status: pass + - kind: integration + ref: "../sm-sitemap-plugin/sitemap_extras_test.go#TestIndexNowStaging" + status: pass + human_judgment: false + - id: D4 + description: "HTML sitemap API: L3 group order, titles in the locale, category tree, posts newest first, others alphabetical, 150 + more, page 2 of 2, 404 past the last / malformed page / unknown group / off, excluded and other-locale addresses absent, noindex; HTML tab refusals; Listing HTML and KeyFile fields" + requirement: "D-34" + verification: + - kind: integration + ref: "../sm-sitemap-plugin/sitemap_extras_test.go#TestHTMLGroups" + status: pass + human_judgment: false + - id: D5 + description: "Management API and MCP: 401/403, GET body, PUT refusals store nothing, PUT stores settings, HTML paths merge, types, IndexNow key and rotation, rebuild queued; MCP dispatch as the linked administrator; pier compiles sitemap_get_settings, sitemap_rebuild, sitemap_update_settings" + requirement: "D-10" + verification: + - kind: integration + ref: "../sm-sitemap-plugin/sitemap_extras_test.go#TestSitemapTools" + status: pass + human_judgment: false + - id: D6 + description: "The new settings tabs (Images and News, IndexNow with S2, HTML sitemap) in the real admin: field layout, triggers hiding the News fields and the new-key checkbox, the partial tables and callouts" + verification: [] + human_judgment: true + rationale: "Rendering in the embedded admin SPA; the tests cover the schema, the saves and the partial node trees, not the visual layout" + - id: D7 + description: "A HTML-sitemap group larger than html_page_size shows its first page plus the sitemap.all link and its group page is paginated (layer rendering in 33-08)" + requirement: "D-34" + verification: + - kind: integration + ref: "../sm-sitemap-plugin/sitemap_extras_test.go#TestHTMLGroups" + status: pass + human_judgment: false + +duration: 30min +completed: 2026-11-04 +status: complete +--- + +# Phase 33 Plan 04: Sitemap extras Summary + +**sm-sitemap-plugin now adds image sitemap files and a Google News sitemap to the index, notifies IndexNow of each rebuild's changes in production through a single-host fetchguard client, serves the HTML sitemap's data in the JSON contract vue-seo-layer already reads, and exposes all sitemap settings through a management API that is also the MCP tools sitemap_get_settings, sitemap_update_settings and sitemap_rebuild.** + +## Read first: versions, breaking changes, consumers + +- **Versions.** Framework target v0.6.1 on base v0.6.0 (33-01 ruling). `sm-sitemap-plugin/go.mod` still requires `summercms v0.6.0` with `replace => ../summercms.go`; the bump is 33-14's. The plugin stays at v0.1.2 plus the 7 unpushed 33-03 commits plus these 6. Nothing was pushed or tagged; the breaking next-minor tag is 33-14's. +- **No new breaking change.** Everything here is additive to the 33-03 format: new columns with defaults (migration `202610280004`), new routes, new settings, a second job kind. The image sitemap is on by default, so after the next rebuild the index of a site with images gains `sitemap-images-.xml` entries; News and IndexNow are off by default; the HTML sitemap API answers by default (the layer only renders it while the site answer has it on with a path). +- **For consumers (33-13, 33-14).** The application needs the conga worker for the IndexNow job as well. A site that turns IndexNow on must serve `/.txt` at the root: vue-seo-layer does it from the SEO plugin's `root_files` (which should include `contract.ListingSettings.KeyFile`, 33-05's part), nginx with the README snippet. No core plugin (user, blog, pages, payment, journal) was touched, and the framework got no code (D-90; `--marker` passed). + +## Performance + +- **Duration:** about 30 min +- **Started:** 2026-11-04T11:14:58Z +- **Completed:** 2026-11-04T11:45Z +- **Tasks:** 3 +- **Commits:** 6 in sm-sitemap-plugin (main, not pushed), 1 planning-docs commit in summercms.go (COVERAGE.md) + +## Accomplishments + +- **Image sitemap (D-31).** The rebuild stores each page's featured image and body images (absolute on the site, each once, at most 1000; `data:` and `javascript:` dropped) and numbers the listed rows with images into `sitemap-images-.xml` with the per-type rules: stable numbers, holes, renumbering only with the file size. The files carry only `image:image` and `image:loc`. +- **News sitemap (D-31).** `sitemap-news.xml` lists articles (content kind `article`) first published in the last 48 hours, newest first, at most 1000, with the publication name, language, date and title. `news_sources` narrows it to `type:` or `category::` values. +- **IndexNow (D-31).** + - Saving with IndexNow on generates the key; "Generate a new key on save" replaces it and the old key file answers 404 at once. + - After each committed rebuild, in production only, the added, changed and removed addresses on the site host are queued as `golem15.sitemap.indexnow` jobs of at most 10 000. + - The job posts `{host, key, urlList}` to `https://api.indexnow.org/indexnow` through a fetchguard client limited to that host, records every answer, and retries 429, 5xx and network errors (five attempts). + - S2 shows the key or `no_key`, the last notification or `never`, and the 403, 422 and 429 callouts. +- **HTML sitemap API (D-34, D-24).** The overview lists the non-empty groups in the L3 order with titles in the locale, the category tree, posts newest first and the rest alphabetical, each with the first `html_page_size` links and `more`. A group page pages the rest. Only the XML sitemaps' listed addresses appear. Public, `noindex`, bucket `sitemap-html`. +- **Settings tabs.** Images and News, IndexNow (with S2) and HTML sitemap, with the C26 copy and the `errors.html_path` and `errors.page_size` refusals. `contract.Listing.Settings` now answers `HTMLEnabled`, `HTMLPaths`, `HTMLPageSize` and `KeyFile`. +- **Management API and MCP (D-10).** GET and PUT settings and POST rebuild behind the sitemap permission, and the three tools. pier compiles them against the plugin's routes in the test. + +## Task Commits (sm-sitemap-plugin, branch main, not pushed) + +1. **Task 1 (tracer):** `5bf56fd` feat: image sitemap files (tracer) +2. **Task 2:** + - group A `a10ce2d` feat: Google News sitemap + - group B `e02d0b1` feat: IndexNow key, key file and pings + - group C (summercms.go) `01a316a` docs(33-04): update the IndexNow coverage matrix +3. **Task 3:** + - group A `8f4ff2a` feat: HTML sitemap groups API + - group B `2a05516` feat: sitemap management API and MCP tools + - group C `291cf17` docs: images, News, IndexNow, HTML sitemap and MCP in README + +**Plan metadata:** this SUMMARY commit in summercms.go (docs), then ROADMAP alone. + +## Decisions Made + +See `key-decisions` in the frontmatter. + +## Deviations from Plan + +### Auto-fixed and plan-shape deviations + +**1. [Plan shape] HTML sitemap addresses stored as a JSONB locale map** +- **Issue:** P33-13 says `html_path` goes through the translation writer. The settings row has no translatable host model, and without a writer cabana refuses a translated value on the main form. +- **Fix:** column `html_paths jsonb` (default pl `/mapa-strony`, en `/sitemap`), edited as the mltext `html_path` added by the plugin's own form extension after `html_enabled`. A save merges the locales sent. Same approach as 33-03's type titles. +- **Commit:** 8f4ff2a + +**2. [Rule 3 - Blocking] News sources taglist without options** +- **Issue:** C26 asks for `customTags: false` with options from the article types and their categories. cabana taglist options are a fixed YAML list, and form extensions accept no taglist field, so dynamic options are impossible. +- **Fix:** the taglist accepts custom tags. A `news_sources` partial under it lists the values the site has (article types and the categories their articles name, `classes.NewsSources`). Each value is checked on save (`models.ValidNewsSource`, new `errors.news_source`). The API answers the values as `news_source_options`. Logged in deferred-items.md and WINDOWS.md #31. +- **Commit:** a10ce2d + +**3. [Rule 2 - Correctness] IndexNow 5xx and network errors retry** +- P33-14 names 429 only. A 5xx or a network failure says nothing about the request, so dropping the notification would lose the change. These are retried within the five attempts as well. COVERAGE.md gained that row and the precise key file route (group C). +- **Commit:** e02d0b1, 01a316a + +**4. [Plan shape] S2's 429 time** +- `err_rate` needs ":time" of the next attempt. The queue owns the retry schedule, so S2 reads the pending job's `scheduled_at` from `river_job` and shows the callout only while a retry is queued. No extra column. +- **Commit:** e02d0b1 + +**5. [Plan shape] The key file route, not a root route** +- As planned (`/_sitemap/indexnow/{file}`); no `/.txt` root route in Go, so a site served at `/` keeps its one-segment pages. vue-seo-layer maps the root name from the site answer's `root_files`. + +**6. [Rule 2 - Correctness] S1 and the index share one file listing** +- With image and News files in the index, S1 would otherwise have hidden them. `classes.SitemapFiles` now feeds both, and the index row of S1 counts addresses once. +- **Commit:** 2a05516 + +**7. [Rule 3 - Blocking] Type settings write shared with the API** +- `PUT /_sitemap/api/v1/settings` must check content types like the form. The 33-03 save logic moved unchanged to `classes.SaveTypeSettings` (field-name refusals; the API renames them `types..`). +- **Commit:** 2a05516 + +**8. [Rule 3 - Blocking] Test adjustments** +- `TestScheduleAndJobs` now expects two jobs. +- `TestSettingsValidate` builds settings with the default HTML page size, because 0 is now refused. +- `bootPlugins` takes an `__env` override, so `TestIndexNow` runs in production. +- The fake content record carries `PublishedAt` and `Parents`. +- A fake blog plugin (`acme.journal`, an article type and a collection type) is booted only by the new tests, so the 33-03 tests are unchanged. + +**9. [Plan shape] Copy additions** +- New phrases outside C26: + - `errors.news_publication`, `errors.news_source`, `errors.type_unknown`; + - `news.sources`, `news.source_value`, `news.source_label`, `news.source_type`, `news.none`. +- Every C26 key is used as written. + +**10. [TDD note] RED evidence** +- The plan is `type: execute`, and each task is one commit per its commit-group table. +- Each test was written against the behaviour list before its implementation compiled. The tests of a group were run once the group compiled, and passed on that first run. +- No separate RED commit was made. + +### Auth gates + +None. + +--- + +**Total deviations:** 10. One is a framework gap (deferred: taglist options), one is storage shape, two are correctness additions, one is a status-source choice, and the rest are refactors, tests and copy. +**Impact on plan:** every must-have truth is met. The visible difference from UI-SPEC C26 is the News sources tag list, which accepts typed values with the value table under it instead of a fixed choice. + +## Known Stubs + +None. WINDOWS.md #29 (the zero ListingSettings fields and the empty images column from 33-03) is resolved here and marked fixed. + +## Threat Flags + +None outside the plan's threat model: +- **T-33-13:** fetchguard AllowHostsMode with `api.indexnow.org` only, HTTPS, no redirects, a 10 s timeout. `classes.SiteURLs` keeps only addresses on the site host, and the test sends a foreign host and a look-alike host. +- **T-33-15:** a production check before queueing and again in the job (`TestIndexNowStaging`). +- **T-33-16:** paging capped at `html_page_size` (at most 500), the tree read capped at 10 000 rows, bucket `sitemap-html`. +- **T-33-17:** `cabana.ResolveBackend` and the permission check on every management route; the 401 and 403 cases are in `TestSitemapTools`. +- **T-33-14** (key disclosure) is accepted as planned. +- **New public surface, in the plan:** `GET /_sitemap/indexnow/{file}` and the two HTML routes; they serve only data the sitemaps already publish. + +## Issues Encountered + +- `go test ./... -run X` prints "no tests to run" for the subpackages while the named root tests pass (as in 33-02 and 33-03). +- The 33-09 executor worked in sm-journal-plugin at the same time. `--plugins` still passed with its working tree as found. + +## Verification run + +- sm-sitemap-plugin, after every commit: + - `go vet ./...` and `go test -short ./...`: green. + - Docker `go test ./... -count=1`: green. +- The plan's named tests all pass: TestImageSitemap, TestSitemapIndex, TestNewsSitemap, TestIndexNow (plus TestIndexNowStaging), TestHTMLGroups, TestSitemapTools. The 33-03 suite passes too. +- Acceptance greps: + - one line each for `sitemap-image/1.1` (writer.go), `image_file_no` (migration), `crypto/rand` and `AllowHostsMode` (indexnow.go), and `sitemap-news/0.9` (news.go); + - `/_sitemap/api/v1/html` appears in routes.go and README.md; + - `"get_settings"`, `"update_settings"` and `"rebuild"` appear in mcp.go; + - `X-Robots-Tag` appears in html.go; + - COVERAGE.md has 3 OPT-OUT rows, each with a reason. +- From summercms.go: + - `bash scripts/check-phase33.sh --plugins --marker`: PASS (sitemap, seo and journal vet and short tests; vitest 77 and 244). + - `bash scripts/check-phase33.sh --docker`: PASS (29 sitemap, 56 seo and 124 journal top-level Postgres tests). + - `--layers` was not run: it is known to be broken and is logged in deferred-items.md. + +## User Setup Required + +None for this plan. When a site turns IndexNow on (after 33-13): +- it must run in the `production` environment with a conga worker; +- the key file must be reachable at `https:///.txt` (vue-seo-layer from the site answer, or the nginx rule in the README). + +## Next Phase Readiness + +- **33-05:** the SEO plugin's site answer can take `html_sitemap` (enabled, paths, page_size) and the key file for `root_files` from `contract.Listing.Settings`. +- **33-13:** consumers move to the new sitemap and may enable News or IndexNow per site. +- **33-14:** bump `go.mod` to v0.6.1 and tag the next minor of sm-sitemap-plugin, covering 33-03 and 33-04. + +## Self-Check: PASSED + +- FOUND: every file in key-files.created (checked with `[ -f ]`). +- FOUND commits in sm-sitemap-plugin: 5bf56fd, a10ce2d, e02d0b1, 8f4ff2a, 2a05516, 291cf17. `git rev-list --count 64453d2..HEAD` = 6. +- FOUND commit in summercms.go: 01a316a. +- No uncommitted changes in sm-sitemap-plugin. + +--- +*Phase: 33-seo-output-audit-and-content* +*Completed: 2026-11-04* diff --git a/.planning/phases/33-seo-output-audit-and-content/deferred-items.md b/.planning/phases/33-seo-output-audit-and-content/deferred-items.md index 7081325a..26871fc4 100644 --- a/.planning/phases/33-seo-output-audit-and-content/deferred-items.md +++ b/.planning/phases/33-seo-output-audit-and-content/deferred-items.md @@ -30,3 +30,10 @@ Out-of-scope discoveries logged during execution; not fixed in the plan that fou - **What 33-08 ran instead:** each step of the stage by hand: `npm run test`, `npm run build:playground` and the smokes of all four layers, `testing/guard-check.mjs` in vue-seo-layer. All passed. - **Possible fix:** a `node_tap` helper in the gate (TAP `# fail 0` and no `not ok`) used for the layers, keeping `npm_vitest` for sm-seo-plugin and sm-journal-plugin. - **Why deferred:** 33-08 commits only to vue-seo-layer; the gate script is framework planning tooling of 33-01. + +## From 33-04 (sm-sitemap-plugin) + +- **A taglist cannot offer per-request options (framework).** C26 asks for the News sources taglist with `customTags: false` and options from the article types and their categories. cabana compiles taglist `options` as a fixed YAML list, and a form extension may not add a taglist field, so the options cannot come from the records. The field accepts custom tags; the `news_sources` partial under it lists the values the site has, each value is checked on save (`errors.news_source`), and MCP `sitemap_get_settings` answers them as `news_source_options`. WINDOWS.md #31. + status: open + - **Possible fix:** per-request options for core taglist fields (as `pact.FormFieldType.RequestOptions` does for plugin field types), or taglist support in form extensions; then set `customTags: false` with those options in `sm-sitemap-plugin/models/settings/fields.yaml`. + - **Why deferred:** 33-04 commits only to sm-sitemap-plugin; the gap is generic, additive framework work.