diff --git a/.planning/phases/33-seo-output-audit-and-content/33-01-SUMMARY.md b/.planning/phases/33-seo-output-audit-and-content/33-01-SUMMARY.md new file mode 100644 index 00000000..29e36328 --- /dev/null +++ b/.planning/phases/33-seo-output-audit-and-content/33-01-SUMMARY.md @@ -0,0 +1,254 @@ +--- +phase: 33-seo-output-audit-and-content +plan: 01 +subsystem: framework +tags: [pact, signpost, cabana, content-descriptor, field-types, gate-script, release-ledger] + +requires: + - phase: 32-seo-foundation + provides: pact.ContentDescriptor, signpost content registry, plugin field types, form extensions, check-phase32.sh engine +provides: + - pact.ContentDescriptor.Kind and Tools with ContentKind*/ContentTool* constants + - pact.ContentRecord.PublishedAt, Parents, Authors, TopHeading + - pact.ContentLocale.Images, Videos, Headings and the value types ContentRef, ContentPerson, ContentImage, ContentVideo, ContentHeading + - pact.FormFieldType.RequestOptions merged into served typeOptions per schema request + - scripts/check-phase33.sh (eleven stages plus --all) + - 33-release-log.md (v0.6.1 planned contents and release order) +affects: [33-02, 33-03, 33-04, 33-05, 33-06, 33-07, 33-08, 33-09, 33-10, 33-11, 33-12, 33-13, 33-14] + +actuals: + tokens: 24700 + tasks: 4 + commits: 4 +plan_head_before: 399efed845bc7dc7e6a1dbd26ed74d78172684e4 +plan_head_after: 21aa4e5 + +tech-stack: + added: [] + patterns: + - "Optional descriptor facts: new slices are nil-or-empty alike; signpost hands records through untouched" + - "Per-request field options: computed once per type per schema request, merged into a copy, fields.yaml wins, errors fail open with one WARN" + +key-files: + created: + - modules/signpost/content_fields_smoke_test.go + - modules/cabana/field_request_options_smoke_test.go + - scripts/check-phase33.sh + - .planning/phases/33-seo-output-audit-and-content/33-release-log.md + modified: + - modules/pact/content.go + - modules/pact/capabilities.go + - modules/cabana/field_type.go + - modules/cabana/http.go + - modules/cabana/schema_types.go + - modules/cabana/admin_openapi.go + - modules/signpost/example_content_test.go + - modules/cabana/example_field_type_test.go + - admin/openapi/admin.json + - admin/src/api/schema.d.ts + - docs/plugins/menu-item-types.md + - docs/backend/field-types.md + - modules/pact/README.md + - modules/signpost/README.md + - modules/cabana/README.md + +key-decisions: + - "Task 1 Decision A (Jakub 2026-11-04): as-planned; P33-01 descriptor fields/constants/value types and P33-02 FormFieldType.RequestOptions exactly as the plan names them" + - "Task 1 Decision B (Jakub 2026-11-04): WR-02 accepted; pact.RecordSaved.Locale stays the primary tag of the request locale (released in v0.5.2)" + - "Task 1 Decision C (Jakub 2026-11-04): Phase 33 delivers SummerCMS v0.6.1 (patch, additive); additive and marker stages compare with v0.6.0; plans reading v0.5.1/v0.5.2 mean v0.6.0/v0.6.1" + - "RequestOptions receives the application database bound to the request context, nil when none is configured" + +patterns-established: + - "Content facts: a content type may set Kind/Tools and per-record PublishedAt/Parents/Authors/TopHeading/Images/Videos/Headings; consumers read them through signpost without importing the content plugin" + - "Phase gate: check-phase33.sh stage engine; a missing repository fails with 'missing '" + +requirements-completed: [SC-2, SC-3, D-93, D-90, D-106, D-43, D-86, D-31, D-85, D-50, D-51, D-96, D-10] + +coverage: + - id: D1 + description: "Additive content descriptor fields and value types, handed through signpost.Describe and signpost.Records unchanged; a type without them describes as before" + requirement: "D-93" + verification: + - kind: unit + ref: "modules/signpost/content_fields_smoke_test.go#TestContentDescriptorFields" + status: pass + - kind: unit + ref: "modules/signpost/example_content_test.go#ExampleRegistry_MatchPath" + status: pass + human_judgment: false + - id: D2 + description: "pact.FormFieldType.RequestOptions merged into served typeOptions (every screen, fields.yaml wins, once per type per request, error fails open with one warning, unchanged schema without it, form extension fields, authenticated handler only)" + requirement: "SC-3" + verification: + - kind: unit + ref: "modules/cabana/field_request_options_smoke_test.go#TestFieldTypeRequestOptions" + status: pass + - kind: other + ref: "scripts/check-admin-openapi.sh --check" + status: pass + human_judgment: false + - id: D3 + description: "Framework API strictly additive against v0.6.0 and no framework line names SEO, a sitemap, JSON-LD or an application" + requirement: "D-106" + verification: + - kind: other + ref: "bash scripts/check-phase33.sh --go --docs --marker --additive" + status: pass + - kind: other + ref: "bash scripts/check-phase33.sh --compat" + status: pass + human_judgment: false + - id: D4 + description: "check-phase33.sh with eleven stages; self-test detects an API removal, an interface method, a marker hit, a failing go test and a missing repository" + verification: + - kind: other + ref: "bash scripts/check-phase33.sh --self-test" + status: pass + human_judgment: false + - id: D5 + description: "33-release-log.md records v0.6.1 planned contents, the six Phase 32 fixes as already released in v0.5.2, plugin/layer tag state and the release order" + verification: [] + human_judgment: true + rationale: "A planning ledger; its accuracy and the release order are Jakub's call before 33-14 tags anything" + +duration: 22min +completed: 2026-11-04 +status: complete +--- + +# Phase 33 Plan 01: Framework seams, phase gate and release ledger Summary + +**Optional content facts on pact.ContentDescriptor/Record/Locale (kind, MCP tools, first publication, parents, authors, top heading, images, videos, heading outline), per-request plugin field options through pact.FormFieldType.RequestOptions, the eleven-stage check-phase33.sh gate and the v0.6.1 release ledger** + +## Task 1 answers (Jakub, 2026-11-04) + +Later plans (33-02 to 33-14) use these: + +1. **Decision A: as-planned.** P33-01 and P33-02 are adopted exactly as written, no rename: + - `pact.ContentDescriptor.Kind string` with `ContentKindArticle = "article"`, `ContentKindCollection = "collection"`, `ContentKindProfile = "profile"`, `ContentKindPage = "page"`; + - `pact.ContentDescriptor.Tools map[string]string` with `ContentToolEdit = "edit"`, `ContentToolHeadings = "headings"`, `ContentToolImageAlt = "image_alt"` (values are full MCP tool names); + - `pact.ContentRecord.PublishedAt *time.Time`, `Parents []ContentRef`, `Authors []ContentPerson`, `TopHeading int`; + - `pact.ContentLocale.Images []ContentImage`, `Videos []ContentVideo`, `Headings []ContentHeading`; + - `ContentRef{Type, ID string}`, `ContentPerson{Name string; Ref ContentRef}`, `ContentImage{URL, Alt string; Decorative bool; Width, Height int}`, `ContentVideo{Provider, ID string}`, `ContentHeading{Level int; Text string}`; + - `pact.FormFieldType.RequestOptions func(ctx context.Context, db *gorm.DB) (map[string]any, error)` with `json:"-"`. +2. **Decision B: WR-02 accepted.** `pact.RecordSaved.Locale` stays the primary tag of the request locale, as released in v0.5.2 (commit 1583651). 32-UAT.md is left to verify-work. +3. **Decision C: version base.** Tags v0.5.2, v0.5.3 and v0.6.0 already exist and contain all six Phase 32 fixes (676ca53, 5471b66, d660cd0, 468af06, 1583651, 0e187cf). This phase delivers **SummerCMS v0.6.1** (patch, additive per D-106). `check-phase33.sh --additive` compares against the newest tag not containing HEAD (computed, currently v0.6.0); `--marker` diffs from v0.6.0 (`PHASE33_BASE`). **Where a Phase 33 plan says v0.5.1 (base) or v0.5.2 (target), read v0.6.0 and v0.6.1.** Nothing is pushed or tagged before 33-14. + +## Performance + +- **Duration:** 22 min (continuation; Task 1 answered before this run) +- **Started:** 2026-11-04T07:26:44Z +- **Completed:** 2026-11-04T07:49:00Z +- **Tasks:** 4 (Task 1 decision, Tasks 2-4 executed) +- **Files modified:** 19 + +## Accomplishments + +- Content types can describe kind, fixing MCP tools, first publication, ordered parents, authors, top heading level, images (with alt and a decorative flag), videos and the heading outline; signpost hands them through, and a type without them is unaffected. +- A plugin field type can serve per-request options to its element through the existing `typeOptions` attribute; the compiled schema is never mutated and errors fail open. +- `scripts/check-phase33.sh` gates the phase: `--self-test`, `--go`, `--docs`, `--marker`, `--additive`, `--admin`, `--plugins` and `--compat` all pass on this host. +- `33-release-log.md` records the v0.6.1 contents and the release order 33-14 follows. + +## Task Commits + +1. **Task 1: Confirm descriptor fields, RequestOptions, RecordSaved.Locale meaning**: no commit (decision, answers above) +2. **Task 2: Content descriptor fields (tracer)**: `561a56c` (feat) +3. **Task 3: Per-request options for plugin field types**: `4cd35c9` (feat) +4. **Task 4: Phase gate script and release log**: `6ea43bb` (test, group A), `21aa4e5` (docs, group B) + +**Plan metadata:** this SUMMARY commit (docs) + +## Files Created/Modified + +- `modules/pact/content.go`: Kind, Tools, the kind and tool constants, the record and locale fields and the five value types. +- `modules/pact/capabilities.go`: `FormFieldType.RequestOptions` with its doc comment (no secrets, fails open). +- `modules/cabana/field_type.go`: `mergeRequestOptions` (once per type, copy-merge, fields.yaml wins, one WARN on error). +- `modules/cabana/http.go`: form schema route calls it on the request's own field slice; `requestDB` helper. +- `modules/cabana/schema_types.go`, `modules/cabana/admin_openapi.go`, `admin/openapi/admin.json`, `admin/src/api/schema.d.ts`: typeOptions description now names per-request options. +- `modules/signpost/content_fields_smoke_test.go`: `TestContentDescriptorFields` (full type, plain type, nil vs empty slices). +- `modules/cabana/field_request_options_smoke_test.go`: `TestFieldTypeRequestOptions`, one subtest per behaviour line plus the authenticated-handler check (T-33-01). +- `modules/signpost/example_content_test.go`, `modules/cabana/example_field_type_test.go`: runnable examples that the docs embed. +- `docs/plugins/menu-item-types.md`, `docs/backend/field-types.md`, `modules/pact/README.md`, `modules/signpost/README.md`, `modules/cabana/README.md`: documentation of every new identifier. +- `scripts/check-phase33.sh`: phase gate. +- `.planning/phases/33-seo-output-audit-and-content/33-release-log.md`: release ledger. + +## Decisions Made + +- The Task 1 answers above. +- `RequestOptions` gets the application database bound to the request context, or nil when the application has none. The test service has no database, and a type that needs one can check for nil. +- The compat stage reads the sitemap and Journal plugins at their releases before the phase (v0.1.2, v0.3.1) and sm-demo-app at its HEAD when 33-01 ran (da481c9). It uses read-only snapshots, not worktrees in the sibling repos. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Version base moved from v0.5.1/v0.5.2 to v0.6.0/v0.6.1 (Jakub's ruling, Decision C)** +- **Found during:** Task 1 (checkpoint), applied in Task 4 +- **Issue:** The plan was written before v0.5.2, v0.5.3 and v0.6.0 were tagged, so the six Phase 32 fixes are already released. +- **Fix:** The additive stage still computes the newest tag that does not contain HEAD (v0.6.0). The marker base is `PHASE33_BASE=v0.6.0`. The release log lists the fixes as "already released in v0.5.2" and plans v0.6.1. The commit subject of group B is `docs(33-01): release log for v0.6.1`, not `... v0.5.2`. +- **Files modified:** scripts/check-phase33.sh, 33-release-log.md +- **Committed in:** 6ea43bb, 21aa4e5 + +**2. [Rule 3 - Blocking] Plugin tags moved past the plan's ranges** +- **Found during:** Task 4 +- **Issue:** The plan pasted `sm-seo-plugin v0.1.0..HEAD` and `sm-journal-plugin v0.2.5..HEAD` as unreleased fixes. Those commits are now released in sm-seo-plugin v0.1.1 and sm-journal-plugin v0.2.6 to v0.3.1, and no plugin or layer has unreleased commits. +- **Fix:** The release log has a table of the newest tag per repository with "none" unreleased. Versions are still read from `git tag` in 33-14. +- **Committed in:** 21aa4e5 + +**3. [Rule 3 - Blocking] Docs checker requires src= snippets for go code blocks** +- **Found during:** Task 3 +- **Issue:** The docs checker refuses a hand-written go snippet in docs/backend/field-types.md. +- **Fix:** Added a runnable `headingLevels` func and `ExampleCompileFormWithTypes_requestOptions` to modules/cabana/example_field_type_test.go. The page embeds the func through `src=`. +- **Committed in:** 4cd35c9 + +**4. [Rule 2 - Marker] pact README addition moved to a new bullet** +- **Found during:** Task 2 +- **Issue:** Editing the existing descriptor bullet re-added its pre-existing word "sitemaps" as a changed line, which the D-90 marker stage would flag. +- **Fix:** The new facts are described in a separate "Content facts" bullet, and the original line is unchanged. +- **Committed in:** 561a56c + +**5. [TDD] Task 3 RED evidence not committed separately** +- **Found during:** Task 3 +- **Issue:** The plan's commit-group table gives Task 3 one commit (`feat(cabana): ...`), while tdd.md's cycle commits `test(...)` first. The plan is `type: execute`, not `type: tdd`. +- **Fix:** I followed the plan's single commit. RED was recorded before implementing: TestFieldTypeRequestOptions failed on 4 behaviour subtests (`typeOptions=map[rows:4]`, `calls=0`, no warning logged), and `check tdd-red-evidence` returned RED_EVIDENCE_OK. GREEN then passed all 8 subtests. + +--- + +**Total deviations:** 5 (3 blocking, 1 marker-safety, 1 TDD commit-shape note) +**Impact on plan:** The API shape and behaviour are exactly as planned. Only version numbers, the ledger contents and the doc mechanics changed. + +## Issues Encountered + +- The compat stage reports sm-feedback-plugin, sm-presentations-plugin and sm-quizzes-plugin as "skipped (fails at v0.6.0 too)": they do not resolve the framework through a scratch workspace at either revision. These failures predate this plan and are not caused by it. +- shellcheck warns SC2034 (`type` unused) in the copied `snapshot_submodules`. check-phase32.sh has the same warning, so I left it as is. + +## Verification run + +- `go vet ./... && go test -short ./...`: green after every commit. +- `go test ./modules/signpost -run 'TestContentDescriptorFields|Example' -v`: PASS (5 subtests, 3 examples). +- `go test ./modules/cabana -run 'TestFieldTypeRequestOptions|TestFormExtensionUnextendedSchemaUnchanged' -v`: PASS (8 subtests). +- `scripts/check-admin-openapi.sh --check`: exit 0. `check-phase33.sh --admin` (openapi + dist): passed. +- `go test ./cmd/summer -run TestDocsTree` and `summer docs:build --check`: no problems. +- `bash scripts/check-phase33.sh --self-test`: passed (37 checks). Without an argument it exits 2 and prints the usage. +- `bash scripts/check-phase33.sh --go --docs --marker --additive`: PASS all four; additive: cabana +0, pact +35, signpost +0 lines against v0.6.0. +- `bash scripts/check-phase33.sh --plugins`: passed (sm-seo-plugin 77 and sm-journal-plugin 232 vitest tests). +- `bash scripts/check-phase33.sh --compat`: passed. +- Not run here: `--layers` (vue-seo-layer `testing/guard-check.mjs` arrives in 33-08), `--consumers`, `--docker`. + +## User Setup Required + +None. No external service configuration is required. + +## Next Phase Readiness + +- 33-02 (Blog Editor) can use `RequestOptions` for `headingLevels` and `genericAnchors`, and 33-03 onward can fill and read the content facts. +- 33-14 tags framework **v0.6.1** and follows 33-release-log.md. + +## Self-Check: PASSED + +- FOUND: modules/pact/content.go, modules/pact/capabilities.go, modules/signpost/content_fields_smoke_test.go, modules/cabana/field_request_options_smoke_test.go, scripts/check-phase33.sh, 33-release-log.md +- FOUND commits: 561a56c, 4cd35c9, 6ea43bb, 21aa4e5 + +--- +*Phase: 33-seo-output-audit-and-content* +*Completed: 2026-11-04*