276 lines
31 KiB
Markdown
276 lines
31 KiB
Markdown
---
|
|
phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin
|
|
plan: 03
|
|
type: execute
|
|
wave: 3
|
|
depends_on: ["11.2-01", "11.2-02"]
|
|
files_modified:
|
|
- 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
|
|
autonomous: true
|
|
requirements: []
|
|
assumption_delta_decision: no-change
|
|
specless_probe_fallback: "skipped: phase has no requirement IDs to probe (visible skip); ROADMAP SC1-SC5 and the D-IDs are the acceptance contract"
|
|
user_setup: []
|
|
|
|
estimate:
|
|
tokens: 120000
|
|
raw_tokens: 120000
|
|
tasks: 3
|
|
confidence: low
|
|
|
|
must_haves:
|
|
truths:
|
|
- "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: "External link reachability and the verbatim terminal clone are re-run at cutover after the repository is made public (D-38)."
|
|
verification: backstop
|
|
artifacts:
|
|
- path: "../sm-summercmsio-app/plugins/golem15/summercms/static_test.go"
|
|
provides: "table-driven tests of every static serving rule"
|
|
contains: "fstest.MapFS"
|
|
- path: "../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go"
|
|
provides: "assembly, fail-closed, coexistence and identity tests"
|
|
contains: "surf.Assemble"
|
|
- path: "scripts/check-phase11.2.sh"
|
|
provides: "phase gate across the four repositories"
|
|
contains: "phase11.2 all passed"
|
|
- path: "../sm-summercmsio-app/terminal_check_test.go"
|
|
provides: "ungated tests of the D-40 helpers"
|
|
contains: "TestTerminalScript"
|
|
- path: "internal/docsite/load_test.go"
|
|
provides: "site_url and site_label branch tests"
|
|
contains: "TestCheckSiteURL"
|
|
- path: ".planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md"
|
|
provides: "validated per-task verification map"
|
|
contains: "nyquist_compliant: true"
|
|
key_links:
|
|
- from: "scripts/check-phase11.2.sh"
|
|
to: "../sm-summercmsio-app/plugins/golem15/summercms"
|
|
via: "go test -json with a detector that requires each named test to PASS"
|
|
pattern: "go -C .*summercms test"
|
|
- from: "../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go"
|
|
to: "modules/surf router"
|
|
via: "surf.Assemble(backpack.New(nil), []party.Plugin{...})"
|
|
pattern: "surf\\.Assemble\\("
|
|
- from: "scripts/check-phase11.2.sh"
|
|
to: "../sm-summercmsio-app/scripts/smoke.sh"
|
|
via: "--smoke stage boots the binary on postgres:15"
|
|
pattern: "smoke\\.sh"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@~/.claude/gsd-core/workflows/execute-plan.md
|
|
@~/.claude/gsd-core/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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.
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="tracer">
|
|
<name>Task 1: Tracer: the phase gate runs the site plugin's full rule table, and the plugin package reaches 90% coverage</name>
|
|
<files>../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</files>
|
|
<read_first>
|
|
- ../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
|
|
</read_first>
|
|
<behavior>
|
|
- 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.
|
|
</behavior>
|
|
<action>
|
|
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=<tmp> ./...` 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`.
|
|
</action>
|
|
<verify>
|
|
<automated>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</automated>
|
|
<fails_when>non-zero exit, a "--- FAIL" line, or "no tests to run"</fails_when>
|
|
<automated>scripts/check-phase11.2.sh --plugin</automated>
|
|
<fails_when>non-zero exit, a coverage line below 90.0%, or no "phase11.2 plugin passed" line</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `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.
|
|
</acceptance_criteria>
|
|
<done>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.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>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</name>
|
|
<files>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</files>
|
|
<read_first>
|
|
- 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)
|
|
</read_first>
|
|
<behavior>
|
|
- 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 `<b>&` 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 `<div class="header-spacer">`.
|
|
- 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.
|
|
</behavior>
|
|
<action>
|
|
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).
|
|
</action>
|
|
<verify>
|
|
<automated>go vet ./... && go test ./internal/docsite ./cmd/summer -run '^(TestParseSite|TestCheckSiteURL|TestSiteLabel|TestSiteURLPrecedence|TestSiteLink|TestDocsBuildSiteFlags|TestDocsSiteFlagsInHelp|TestDocsTree)$' -count=1 -v</automated>
|
|
<fails_when>non-zero exit, a "--- FAIL" line, "no tests to run", or fewer than eight top-level "--- PASS" lines</fails_when>
|
|
<automated>go -C ../sm-summercmsio-app test -run '^(TestLoadTerminal|TestTerminalScript|TestTerminalEnv)$' -count=1 -v .</automated>
|
|
<fails_when>non-zero exit, a "--- FAIL" or "--- SKIP" line, or fewer than three "--- PASS" lines</fails_when>
|
|
<automated>scripts/check-phase11.2.sh --framework && scripts/check-phase11.2.sh --app && scripts/check-phase11.2.sh --site</automated>
|
|
<fails_when>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</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `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`.
|
|
</acceptance_criteria>
|
|
<done>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.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: The whole phase passes one gate end to end, and VALIDATION.md is validated</name>
|
|
<files>scripts/check-phase11.2.sh, .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md</files>
|
|
<read_first>
|
|
- 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)
|
|
</read_first>
|
|
<action>
|
|
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 <stage> 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`.
|
|
</action>
|
|
<verify>
|
|
<automated>scripts/check-phase11.2.sh --all</automated>
|
|
<fails_when>non-zero exit, a line starting "refuse:" or "FAIL", or no "phase11.2 all passed" line</fails_when>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `scripts/check-phase11.2.sh --all` prints `phase11.2 all passed` and one `phase11.2 <stage> 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.
|
|
</acceptance_criteria>
|
|
<done>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.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<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>
|
|
|
|
<verification>
|
|
- `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.
|
|
</verification>
|
|
|
|
<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`.
|
|
|
|
<output>
|
|
Create `.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-03-SUMMARY.md` when done
|
|
</output>
|