Files
summercms/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-03-PLAN.md

31 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, assumption_delta_decision, specless_probe_fallback, user_setup, estimate, must_haves
phase plan type wave depends_on files_modified autonomous requirements assumption_delta_decision specless_probe_fallback user_setup estimate must_haves
11.2-ready-to-share-summercms-io-website-and-newsletter-plugin 03 execute 3
11.2-01
11.2-02
scripts/check-phase11.2.sh
internal/docsite/load_test.go
internal/docsite/theme_test.go
cmd/summer/docs_test.go
.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md
../sm-summercmsio-app/plugins/golem15/summercms/static_test.go
../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go
../sm-summercmsio-app/plugins/golem15/summercms/links_test.go
../sm-summercmsio-app/terminal_check_test.go
../sm-summercmsio-app/vue-summercmsio-app/tests/terminal.test.ts
../sm-summercmsio-app/vue-summercmsio-app/tests/scrollSpy.test.ts
true
no-change skipped: phase has no requirement IDs to probe (visible skip); ROADMAP SC1-SC5 and the D-IDs are the acceptance contract
tokens raw_tokens tasks confidence
120000 120000 3 low
truths artifacts key_links
Per SC5 and the CLAUDE.md rule that unit tests are the last plan, the site plugin package `git.golem15.com/golem15/sm-summercmsio-plugin` reaches at least 90.0% statement coverage from tests that need no build output (fstest fixtures), and `internal/docsite` stays at or above 85.0%.
Every serving rule from D-07 and D-47 has a test that fails when the rule breaks: content types from the own table, immutable cache only for `_nuxt/` (not `_nuxt/builds/`) and `_fonts/`, no-cache with a strong ETag elsewhere including `/docs/assets/`, 304 on a matching If-None-Match, HEAD and Range handled, dot-segment and traversal paths 404, extension-less docs paths 301 to `.html`, `/docs` 301 to `/docs/`, tree 404 pages with status 404, and no robots-blocking, CSP or frame-deny header on any response.
No redirect the plugin emits has a Location outside `/docs/`, and no Location starts with `//` (T-11.2-05).
The plugin assembles alone through `surf.Assemble` with GET-only routes (POST 405, `/backend` falls to the site 404), fails closed with `public/site/index.html missing` on an empty tree, registers itself through init under `golem15.summercms`, declares no admin controllers, and its three patterns coexist with cabana's admin patterns on one ServeMux (ready for Phase 11.3).
Per D-41 and D-46, every branch of `checkSiteURL`, `siteLabel`, the `site_url`/`site_label` parsing rules and the flag override precedence is tested, the header escapes the label, the link appears on the 404 page, and the unset header keeps its exact bytes.
Per D-40, the terminal-check helpers `loadTerminal`, `terminalScript` and `terminalEnv` have ungated tests, and the TypeScript utilities cover their edge cases.
`scripts/check-phase11.2.sh --all` runs the framework, plugin, app, site, built-tree, smoke, deploy and terminal stages, requires every named test to PASS (a SKIP, FAIL or zero-match fails), and prints `phase11.2 all passed`.
11.2-VALIDATION.md has every per-task row filled with its command and a green status, `status: validated`, `nyquist_compliant: true` and `wave_0_complete: true`, and keeps the manual-only rows (visual UAT, rome bring-up, external links and the verbatim clone at cutover).
No production code changes in this plan except fixes for defects the new tests expose; each fix lands with its failing-then-passing test in the same commit and is listed in the SUMMARY. summercms.go commits here are tests and the gate only, after v0.1.0 (RESEARCH Pitfall 13).
statement verification
External link reachability and the verbatim terminal clone are re-run at cutover after the repository is made public (D-38). backstop
path provides contains
../sm-summercmsio-app/plugins/golem15/summercms/static_test.go table-driven tests of every static serving rule fstest.MapFS
path provides contains
../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go assembly, fail-closed, coexistence and identity tests surf.Assemble
path provides contains
scripts/check-phase11.2.sh phase gate across the four repositories phase11.2 all passed
path provides contains
../sm-summercmsio-app/terminal_check_test.go ungated tests of the D-40 helpers TestTerminalScript
path provides contains
internal/docsite/load_test.go site_url and site_label branch tests TestCheckSiteURL
path provides contains
.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md validated per-task verification map nyquist_compliant: true
from to via pattern
scripts/check-phase11.2.sh ../sm-summercmsio-app/plugins/golem15/summercms go test -json with a detector that requires each named test to PASS go -C .*summercms test
from to via pattern
../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go modules/surf router surf.Assemble(backpack.New(nil), []party.Plugin{...}) surf.Assemble(
from to via pattern
scripts/check-phase11.2.sh ../sm-summercmsio-app/scripts/smoke.sh --smoke stage boots the binary on postgres:15 smoke.sh
Bring full unit test coverage to the phase's new code and close the phase with a gate (SC5, CLAUDE.md "unit tests are always the last plan"). The site plugin's static handler and routes get table-driven tests over `fstest.MapFS` fixtures (D-07, D-47, T-11.2-04, T-11.2-05), the framework's `site_url`/`site_label` work gets every branch tested (D-41, D-46, T-11.2-09), the D-40 terminal-check helpers and the TypeScript utilities get their edge cases, and `scripts/check-phase11.2.sh` runs all four repositories' checks fail-closed. VALIDATION.md is filled and validated.

Purpose: plans 11.2-01 and 11.2-02 shipped smoke tests only; this plan pins every rule so a regression fails go test.

Output: test files in sm-summercmsio-plugin, sm-summercmsio-app, vue-summercmsio-app and summercms.go, the phase gate script in summercms.go, and a validated 11.2-VALIDATION.md.

<execution_context> @/.claude/gsd-core/workflows/execute-plan.md @/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @CLAUDE.md @.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-CONTEXT.md @.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-RESEARCH.md @.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md @.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-01-SUMMARY.md @.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-02-SUMMARY.md @.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-02-PLAN.md @scripts/check-phase11.1.sh @modules/boardwalk/boardwalk_test.go @modules/surf/example_test.go

Paths. Commands run from the summercms.go root. APP = ../sm-summercmsio-app, PLUG = APP/plugins/golem15/summercms, SITE = APP/vue-summercmsio-app. Each task names the repositories it commits to.

Interfaces under test are the ones plan 11.2-02 fixed in its <interfaces> block (Plugin, newHandlers, tree, newTree, errMissingIndex, siteImmutable, contentTypes, contentType, redirectTo, cacheImmutable, cacheNoCache; checkSiteURL, siteLabel, Options.SiteURL, Options.SiteLabel; terminalGroup, loadTerminal, terminalScript, terminalEnv, terminalCloneURL) and plan 11.2-01's TypeScript utilities (copyPayload, activeSection, SPY_THRESHOLD). Read the 11.2-02 SUMMARY for any name that changed during execution and test the shipped name.

Conventions. Stdlib testing only (no testify in these packages), table-driven where natural, t.TempDir() for files, t.Fatalf("x = %v, want %v"). Commit per repository, one logical change per commit, never a co-author tag. In summercms.go stage only the listed files; the VALIDATION.md update is a separate planning-docs commit. If a test exposes a production defect, fix it in the same commit as its test and list it in the SUMMARY; make no other production change.

Task 1: Tracer: the phase gate runs the site plugin's full rule table, and the plugin package reaches 90% coverage ../sm-summercmsio-app/plugins/golem15/summercms/static_test.go, ../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go, ../sm-summercmsio-app/plugins/golem15/summercms/links_test.go, scripts/check-phase11.2.sh - ../sm-summercmsio-app/plugins/golem15/summercms/plugin.go, static.go, smoke_test.go and links_test.go (as shipped by plan 11.2-02) - modules/boardwalk/boardwalk_test.go lines 1-44 (fstest fixture and `get` helper) - modules/surf/example_test.go lines 114-140 (surf.Assemble with backpack.New(nil)) - modules/party/registry.go lines 29-60 (Register, Activate) - modules/cabana/http.go and modules/cabana/prefix.go (the admin route patterns to mirror in the coexistence test) - scripts/check-phase11.1.sh lines 1-60 and 171-240 (mode layout, detect_json over go test -json) - .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-RESEARCH.md "Catch-all conflicts", "Cache-Control by path", "MIME table", Pitfalls 8-11 - Fixture site tree: `index.html`, `404.html`, `200.html`, `_payload.json`, `_nuxt/entry.abc.js`, `_nuxt/entry.abc.css`, `_nuxt/builds/latest.json`, `_nuxt/builds/meta/x.json`, `_fonts/r.woff2`, `_i18n/h/en/messages.json`, `robots.txt`, `sitemap.xml`, `og-image.png`, `favicon.ico`, `sun-logo.webp`, `.hidden`. Fixture docs tree: `index.html`, `404.html`, `.summer-docs`, `assets/site.css`, `assets/site.js`, `backend/admin-spa.html`, `backend/admin-spa.md`, `guide/index.html`, `llms.txt`, `search-index.json`. - Site: `/` 200 `text/html; charset=utf-8` `no-cache`; `/_nuxt/entry.abc.js` `text/javascript; charset=utf-8` immutable; `/_nuxt/entry.abc.css` `text/css; charset=utf-8` immutable; `/_nuxt/builds/latest.json` and `/_nuxt/builds/meta/x.json` `application/json` no-cache; `/_fonts/r.woff2` `font/woff2` immutable; `/_i18n/h/en/messages.json`, `/_payload.json`, `/robots.txt`, `/sitemap.xml`, `/og-image.png`, `/favicon.ico`, `/sun-logo.webp` no-cache with their table types; `/.hidden`, `/_nuxt/../.hidden`, `/missing` 404 with the site 404 body, `text/html; charset=utf-8`, `no-cache`. - Docs: `/docs/` and `/docs/index.html` 200; `/docs/backend/admin-spa` and `/docs/backend/admin-spa/` 301 to `/docs/backend/admin-spa.html`; `/docs/backend/admin-spa.md` `text/markdown; charset=utf-8`; `/docs/assets/site.css` no-cache (never immutable); `/docs/guide` and `/docs/guide/` serve `guide/index.html`; `/docs/.summer-docs` and `/docs/assets/../.summer-docs` 404 with the docs 404 body; `/docs/missing` 404; a request whose path is `/docs//evil.example/x` never yields a Location starting with `//`. - ETag is `"` + 16 lowercase hex + `"`; equal content gives equal ETags, different content different ones; If-None-Match with the ETag gives 304 and no body; HEAD gives 200, a Content-Length and no body; `Range: bytes=0-3` gives 206. - Every response (200, 301, 304, 404) carries no X-Robots-Tag, Content-Security-Policy or X-Frame-Options header; served files carry `X-Content-Type-Options: nosniff`. - A tree without `404.html` answers a miss with Go's plain 404; `newHandlers` on a fixture without `site/index.html` or without `docs/index.html` returns an error naming `public/site/index.html missing` or `public/docs/index.html missing`. - `contentType` returns the table value for every listed extension, lower-cases the extension (`X.HTML`), falls back to `mime` for an unlisted known type (`.pdf`), and to `application/octet-stream` for an unknown one; `siteImmutable` is true for `_nuxt/a.js` and `_fonts/x.woff2`, false for `_nuxt/builds/latest.json`, `index.html` and `_i18n/x.json`. - Routes: `surf.Assemble(backpack.New(nil), []party.Plugin{&Plugin{fsys: fixture}})` gives `GET /` 200, `HEAD /` 200, `GET /docs` 301 to `/docs/`, `GET /docs/` 200, `POST /` 405, `GET /backend` 404 with the site 404 body; an empty fixture makes Assemble fail with `public/site/index.html missing`; a fresh ServeMux holding the plugin's three patterns plus `GET /backend`, `GET /backend/{path...}`, `GET /backend/assets/{vendor}/{plugin}/{file...}`, `POST /backend/api/v1/auth/login` and `GET /backend/api/v1/{vendor}/{plugin}/{controller}` registers without a panic and routes `/backend/x` to the admin handler and `/` to the site; `ID()` is `golem15.summercms`, `Requires()` is nil, `Register` and `Boot` return nil, the plugin does not satisfy `pact.HasAdminControllers`, and `party.Activate(backpack.New(nil), []string{"golem15.summercms"})` finds the init-registered plugin; `&Plugin{}` (embedded tree) either assembles (tree built) or fails with the missing-index error (tree not built), never panics. - `pageLinks` extracts every `href` and `src` value, including duplicates removed and fragment-only links kept. 1. `static_test.go` in PLUG (package `summercms`): one fixture builder returning the `fstest.MapFS` from the behavior block (distinct bodies per file so ETags differ), a `do(h, method, target, header)` helper, and table-driven tests `TestStaticSite`, `TestStaticDocs`, `TestStaticConditionalAndRange`, `TestStaticNoBlockingHeaders`, `TestStaticRedirectLocations`, `TestStaticMissing404Page`, `TestNewHandlersMissingIndex`, `TestContentType` and `TestSiteImmutable` covering every bullet. For paths that `httptest.NewRequest` would normalise, set `r.URL.Path` directly so the handler's own cleaning is what is tested. Assert exact header values, not substrings, for Content-Type and Cache-Control. 2. `routes_test.go` in PLUG: `TestRoutesAssemble`, `TestRoutesFailClosed`, `TestRoutesCoexistWithAdminPatterns` (mirror cabana's patterns as literal strings, register with a `recover` guard), `TestPluginIdentity` and `TestPluginEmbeddedTree` per the behavior block. 3. `links_test.go` in PLUG: add the ungated `TestPageLinks` for the `pageLinks` helper; leave the build-gated tests as shipped. 4. Coverage: `go -C PLUG test -count=1 -coverprofile= ./...` then `go tool cover -func` total at least 90.0%. Add cases until it is, or record in the SUMMARY any line that is unreachable without a build and why. 5. `scripts/check-phase11.2.sh` in summercms.go (bash, `set -euo pipefail`, structure and `go test -json` detector modelled on `scripts/check-phase11.1.sh` `detect_json`: fail on a build failure, any FAIL, any SKIP of a named test, zero matched tests or "no tests to run"). This task implements `--plugin`: `go -C "$PLUG" vet ./...`, the named tests from steps 1-3 run with `-json` through the detector, and the coverage threshold; it prints `phase11.2 plugin passed`. Paths resolve from the script location (`APP="$ROOT/../sm-summercmsio-app"`). Unknown modes print usage and exit 2. Later tasks add the other modes. Commit in PLUG as `test: cover every static serving rule and the plugin routes`, and in summercms.go as `test(11.2): add the phase gate with the plugin stage`. go -C ../sm-summercmsio-app/plugins/golem15/summercms vet ./... && go -C ../sm-summercmsio-app/plugins/golem15/summercms test ./... -run '^(TestStatic|TestNewHandlersMissingIndex|TestContentType|TestSiteImmutable|TestRoutes|TestPlugin|TestPageLinks)' -count=1 -v non-zero exit, a "--- FAIL" line, or "no tests to run" scripts/check-phase11.2.sh --plugin non-zero exit, a coverage line below 90.0%, or no "phase11.2 plugin passed" line - `go -C ../sm-summercmsio-app/plugins/golem15/summercms test -cover ./... -count=1` prints a `coverage:` value of at least 90.0%. - `go -C ../sm-summercmsio-app/plugins/golem15/summercms test ./... -run '^(TestStatic.*|TestRoutes.*|TestPlugin.*)$' -count=1 -v` prints at least 10 `--- PASS` lines (subtests included). - Changing `siteImmutable` to also return true for `_nuxt/builds/` paths in a scratch copy makes `TestStaticSite` fail (checked once by hand during execution and recorded in the SUMMARY), proving the cache test fails when broken. - `bash -n scripts/check-phase11.2.sh` exits 0 and `scripts/check-phase11.2.sh --bogus` exits 2. Every static serving rule and route behaviour of the site plugin is pinned by a test that fails when the rule breaks, the plugin package is at 90% or more, and the phase gate runs that stage fail-closed. Task 2: Every new framework and app branch is pinned: site_url and site_label rules, override precedence, header rendering, CLI flags, terminal-check helpers and the TypeScript edge cases internal/docsite/load_test.go, internal/docsite/theme_test.go, cmd/summer/docs_test.go, ../sm-summercmsio-app/terminal_check_test.go, ../sm-summercmsio-app/vue-summercmsio-app/tests/terminal.test.ts, ../sm-summercmsio-app/vue-summercmsio-app/tests/scrollSpy.test.ts, scripts/check-phase11.2.sh - internal/docsite/load.go, internal/docsite/docsite.go, internal/docsite/emit.go and internal/docsite/theme/templates/header.html (as shipped by plan 11.2-02: checkSiteURL, siteLabel, the override block in load) - internal/docsite/load_test.go (TestParseSite table), internal/docsite/theme_test.go (themeTree, buildTheme, TestSiteLink), cmd/summer/docs_test.go (TestDocsBuildSiteFlags, TestToolCommandNames helpWants) - ../sm-summercmsio-app/terminal_check_test.go (helpers as shipped) - ../sm-summercmsio-app/vue-summercmsio-app/tests/terminal.test.ts, tests/scrollSpy.test.ts, app/utils/terminal.ts, app/utils/scrollSpy.ts (as shipped by plan 11.2-01) - checkSiteURL accepts `https://acme.example`, `https://acme.example/`, `http://acme.example:8080/x`, `/` and `/home`; rejects the empty string, `javascript:alert(1)`, `JavaScript:alert(1)`, `data:text/html,x`, `//acme.example`, `acme.example`, `docs/x`, `https://`, `https://user:pw@acme.example`, `ftp://acme.example`, `/ x`, a value with a newline and a value with a leading tab. - siteLabel returns a trimmed explicit label; else `acme.example` for `https://acme.example/docs`, `acme.example:8080` for `http://acme.example:8080/`; else `Home` for `/` and `/home`. - ParseSite: `site_url` alone is accepted; `site_label` without `site_url`, a blank label and a two-line label are rejected with their messages. - Load precedence: a yaml `site_url` plus an `Options.SiteURL` uses the option; a yaml `site_label` plus `Options.SiteLabel` uses the option; `Options.SiteLabel` with no URL anywhere fails with the `--site-label needs` message; an invalid `Options.SiteURL` fails with the `--site-url:` message; a yaml URL with no label derives the label. - Rendering: a label `&` appears as `<b>&` in the header; the link is on `index.html`, a section page and `404.html`; with nothing set, the header contains the wordmark's closing tag immediately followed by a newline and `
`. - CLI: `summer help docs:build` and `summer help docs:serve` (or the command's flag listing the existing help test uses) list `--site-url` and `--site-label`; `docs:build --site-label x` with no site URL returns an error. - loadTerminal reads the real `vue-summercmsio-app/app/data/terminal.json` as 3 groups and 6 commands, and returns an error for a missing file and for malformed JSON. - terminalScript with no override returns the six commands joined by `\n`; with an override `/tmp/fw` the first command becomes `git clone /tmp/fw` and the other five are unchanged. - terminalEnv drops `SUMMER_ENV`, `SUMMER_DATABASE__DSN` and `GOWORK`, keeps `HOME`, sets `GOBIN`, makes `PATH` start with the gobin directory followed by `:`, and sets `PATH` to the gobin directory when the base has no `PATH`. - copyPayload: three groups give the six-line payload; an empty list gives `''`; a group with no commands adds nothing; activeSection: the exact-140 boundary is inactive, a custom threshold is honoured, and negative tops (sections scrolled past) still count. 1. summercms.go: add `TestCheckSiteURL`, `TestSiteLabel` and `TestSiteURLPrecedence` to `internal/docsite/load_test.go` and new `TestParseSite` rows; extend `TestSiteLink` in `theme_test.go` with the escaping, 404-page, section-page and exact-unset-bytes cases; add `TestDocsSiteFlagsInHelp` and the label-without-URL error case to `cmd/summer/docs_test.go`. Keep `go test -cover ./internal/docsite` at or above 85.0%. 2. APP `terminal_check_test.go`: add the ungated `TestLoadTerminal`, `TestTerminalScript` and `TestTerminalEnv` per the behavior block; `TestTerminalCommands` stays gated. 3. SITE tests: add only the behavior cases that the plan 11.2-01 tests do not already cover (check first; if all are covered, leave the files unchanged and say so in the SUMMARY). 4. `scripts/check-phase11.2.sh`: add `--framework` (`go vet ./...`; `go test ./internal/docsite ./cmd/summer -count=1 -json` through the detector with the named tests `TestDocsTree`, `TestDocsBuildRealTree`, `TestParseSite`, `TestCheckSiteURL`, `TestSiteLabel`, `TestSiteURLPrecedence`, `TestSiteLink`, `TestDocsBuildSiteFlags`, `TestDocsSiteFlagsInHelp`; docsite coverage at least 85.0%; then `scripts/check-phase11.1.sh --docs` and `--forbidden`), `--app` (`go -C "$APP" vet ./...`; `go -C "$APP" list ./...` must print only the app package; named `TestLoadTerminal`, `TestTerminalScript`, `TestTerminalEnv`), and `--site` (`pnpm -C "$SITE" install --frozen-lockfile`, `pnpm -C "$SITE" run generate`, `pnpm -C "$SITE" test` with a `# fail 0` line required). Commit per repository: summercms.go `test(11.2): cover site_url and site_label and gate the framework, app and site stages`; APP `test: cover the terminal check helpers`; SITE `test: cover terminal and scroll-spy edge cases` (only if changed). go vet ./... && go test ./internal/docsite ./cmd/summer -run '^(TestParseSite|TestCheckSiteURL|TestSiteLabel|TestSiteURLPrecedence|TestSiteLink|TestDocsBuildSiteFlags|TestDocsSiteFlagsInHelp|TestDocsTree)$' -count=1 -v non-zero exit, a "--- FAIL" line, "no tests to run", or fewer than eight top-level "--- PASS" lines go -C ../sm-summercmsio-app test -run '^(TestLoadTerminal|TestTerminalScript|TestTerminalEnv)$' -count=1 -v . non-zero exit, a "--- FAIL" or "--- SKIP" line, or fewer than three "--- PASS" lines scripts/check-phase11.2.sh --framework && scripts/check-phase11.2.sh --app && scripts/check-phase11.2.sh --site non-zero exit, a docsite coverage value below 85.0%, or a missing "phase11.2 framework passed", "phase11.2 app passed" or "phase11.2 site passed" line - `go test -cover ./internal/docsite -count=1` prints a `coverage:` value of at least 85.0%. - `go test ./internal/docsite -run '^TestCheckSiteURL$' -count=1 -v` prints at least 18 `--- PASS: TestCheckSiteURL/` subtest lines (five accepted, thirteen rejected values). - Making `checkSiteURL` accept `//host` URLs in a scratch copy makes `TestCheckSiteURL` fail (checked once by hand and recorded in the SUMMARY). - `pnpm -C ../sm-summercmsio-app/vue-summercmsio-app test` prints `# fail 0`. Every new framework, app-helper and TypeScript branch has a test, docsite coverage holds at 85% or more, and the gate's framework, app and site stages pass fail-closed. Task 3: The whole phase passes one gate end to end, and VALIDATION.md is validated scripts/check-phase11.2.sh, .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md - scripts/check-phase11.2.sh (as written in Tasks 1-2) - ../sm-summercmsio-app/scripts/build.sh, scripts/smoke.sh, scripts/check-deploy.sh (as shipped by plan 11.2-02) - .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md (draft map and manual-only rows) - .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-0{1,2}-PLAN.md verify blocks (the commands to list per task) - .planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-VALIDATION.md (a validated example of the format) 1. `scripts/check-phase11.2.sh`: add the remaining stages. `--built`: run `"$APP/scripts/build.sh"` in release mode when `git -C "$ROOT" rev-parse -q --verify refs/tags/v0.1.0` succeeds, else `build.sh dev` (and say which); then run the plugin with `SUMMERCMS_REQUIRE_BUILD=1` through the detector requiring `TestLandingLinks`, `TestTerminalCommandsInPage` and `TestDocsHeaderSiteLink` to PASS (a SKIP fails the stage). `--smoke`: `"$APP/scripts/smoke.sh"` and require `smoke: ok`. `--deploy`: `"$APP/scripts/check-deploy.sh"` and require `check-deploy: ok`. `--terminal`: `SUMMERCMS_TERMINAL_CHECK=1 SUMMERCMS_CLONE_URL="$ROOT"` with `TestTerminalCommands` required to PASS; with `--verbatim` the clone override is left unset (the cutover run after D-38). `--full`: `go vet ./... && go test ./... -count=1` in summercms.go (Docker suites included). `--all`: framework, plugin, app, site, built, smoke, deploy, terminal and full, in that order, then `phase11.2 all passed`. Every stage prints `phase11.2 passed`. 2. Run `scripts/check-phase11.2.sh --all` until it passes. Fix only test or gate defects; a production defect gets its fix plus a failing-then-passing test in the same commit and a SUMMARY entry. 3. `11.2-VALIDATION.md`: fill the per-task verification map with one row per task of plans 11.2-01, 11.2-02 and 11.2-03 (task id, the SC or D-ID it proves, test type, the exact automated command from the plan, file exists, status green), set the frontmatter to `status: validated`, `nyquist_compliant: true`, `wave_0_complete: true`, tick the Wave 0 and sign-off checklists, and keep the manual-only rows (visual fidelity at 1280/721/720/375px, rome bring-up, external links via `TestExternalLinks`, and the verbatim clone via `--terminal --verbatim`, both at cutover after D-38). Commit the gate in summercms.go as `test(11.2): complete the phase gate`, then VALIDATION.md separately as `docs(11.2): validate the phase verification map`. scripts/check-phase11.2.sh --all non-zero exit, a line starting "refuse:" or "FAIL", or no "phase11.2 all passed" line - `scripts/check-phase11.2.sh --all` prints `phase11.2 all passed` and one `phase11.2 passed` line for each of framework, plugin, app, site, built, smoke, deploy, terminal and full. - `grep -n 'nyquist_compliant: true' .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md` and `grep -n 'status: validated' .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md` both find a match. - `grep -c '⬜ pending' .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md` prints 0 for automated rows (manual-only rows sit in their own table). - `grep -n 'TestExternalLinks' .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md` finds the manual-at-cutover row. One command proves the whole phase across the four repositories, and VALIDATION.md records every automated check as green with the cutover-only checks kept as manual rows.

<threat_model>

Trust Boundaries

Boundary Description
test results → phase sign-off A gate that misreads skipped or filtered tests certifies behaviour it never measured
environment gates → integration tests Build-dependent tests skip when their variable is unset

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-11.2-15 Repudiation (false green) scripts/check-phase11.2.sh medium mitigate go test -json detector requires every named test to PASS; a SKIP, FAIL, zero match or "no tests to run" fails the stage; --bogus exits 2
T-11.2-16 Tampering (silent skip) build-gated plugin tests and the terminal check medium mitigate The gate sets SUMMERCMS_REQUIRE_BUILD=1 and SUMMERCMS_TERMINAL_CHECK=1 itself and requires PASS; requireBuild fails rather than skips when the variable is set and the tree is missing
T-11.2-SC Tampering dependency installs low accept Tests use the stdlib and the existing node:test runner; no package is added; pnpm install --frozen-lockfile reuses plan 11.2-01's lockfile
</threat_model>
- `scripts/check-phase11.2.sh --all` prints `phase11.2 all passed`. - `go -C ../sm-summercmsio-app/plugins/golem15/summercms test -cover ./...` at least 90.0%; `go test -cover ./internal/docsite` at least 85.0%. - `git status --porcelain` is empty in summercms.go, sm-summercmsio-app, sm-summercmsio-plugin and vue-summercmsio-app after the last commit.

<success_criteria>

  • SC5: the new Go code has unit tests delivered in the phase's last plan (plugin at 90% or more, docsite at 85% or more, app helpers covered).
  • The gate proves SC1 to SC4 automatically where they are automatable; the manual rows (visual UAT, rome bring-up, cutover link and clone checks) are recorded in VALIDATION.md. </success_criteria>

Artifacts this phase produces

  • Plugin tests: TestStaticSite, TestStaticDocs, TestStaticConditionalAndRange, TestStaticNoBlockingHeaders, TestStaticRedirectLocations, TestStaticMissing404Page, TestNewHandlersMissingIndex, TestContentType, TestSiteImmutable, TestRoutesAssemble, TestRoutesFailClosed, TestRoutesCoexistWithAdminPatterns, TestPluginIdentity, TestPluginEmbeddedTree, TestPageLinks.
  • Framework tests: TestCheckSiteURL, TestSiteLabel, TestSiteURLPrecedence, extended TestParseSite and TestSiteLink, TestDocsSiteFlagsInHelp.
  • App tests: TestLoadTerminal, TestTerminalScript, TestTerminalEnv.
  • Gate: scripts/check-phase11.2.sh with modes --framework, --plugin, --app, --site, --built, --smoke, --deploy, --terminal (with --verbatim), --full, --all.
  • Planning: validated 11.2-VALIDATION.md.
Create `.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-03-SUMMARY.md` when done