diff --git a/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-03-SUMMARY.md b/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-03-SUMMARY.md new file mode 100644 index 0000000..b2139b3 --- /dev/null +++ b/.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-03-SUMMARY.md @@ -0,0 +1,305 @@ +--- +phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin +plan: 03 +subsystem: testing +tags: [go-test, fstest, httptest, node-test, coverage, phase-gate, bash] + +requires: + - phase: 11.2-01 + provides: vue-summercmsio-app with terminal.json, copyPayload, activeSection and the node:test suites + - phase: 11.2-02 + provides: sm-summercmsio-plugin static handler and routes, the app's build/smoke/check-deploy scripts and terminal check, docsite site_url/site_label +provides: + - table-driven tests of every static serving rule and route behaviour of the site plugin (98.2% statement coverage) + - every branch of checkSiteURL, siteLabel, the site_url/site_label parsing and the option-over-yaml precedence, the escaped header, the 404 and section pages, and the CLI flags in help + - ungated tests of the D-40 terminal-check helpers and the remaining scroll-spy edge case + - scripts/check-phase11.2.sh, a fail-closed gate across the four repositories (framework, plugin, app, site, built, smoke, deploy, terminal, full, all) + - a validated 11.2-VALIDATION.md +affects: [11.2 verify-work, cutover, 11.3] + +actuals: + tokens: 16305 + tasks: 3 + commits: 4 + commits_all_repos: 9 +plan_head_before: 2229ca3c6028fea4dd75a645f82e5f49f07fd638 +plan_head_after: a2f2d1e5c78c5aeb40363fac69ce65801d0c6031 + +tech-stack: + added: [] + patterns: + - "Phase gate per repository stage: go test -json through a detector that refuses a fail, a skip, a missing test, zero tests and no tests to run" + - "Static-handler tests set r.URL.Path directly so the handler's own path cleaning is what is tested" + - "Mutation checks in a scratch copy (plugin) or a git archive export (framework), never in the working tree" + +key-files: + created: + - scripts/check-phase11.2.sh + - ../sm-summercmsio-app/plugins/golem15/summercms/static_test.go + - ../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go + modified: + - internal/docsite/load_test.go + - internal/docsite/theme_test.go + - cmd/summer/docs_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/scrollSpy.test.ts + - .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md + +key-decisions: + - "The gate's --built stage builds in release mode only when the framework checkout build.sh uses (SUMMERCMS_FRAMEWORK, default summercms.go) has v0.1.0; with the tag deferred it runs a dev build and says so" + - "The coexistence test reads the plugin's real patterns from surf.BuildRouter(...).Routes() and registers them on a fresh ServeMux beside literal copies of cabana's admin patterns" + - "TestTerminalScript pins the shipped override form 'git clone summercms' (11.2-02 decision), not the bare 'git clone ' the plan text described" + - "TestPageLinks pins the shipped pageLinks contract: duplicates kept in document order (callers dedupe), fragment links kept, og:image appended" + +patterns-established: + - "check-phase11.2.sh named_tests DIR PKG NAME...: per-repository go -C runs with -json and a required-PASS list; environment changes run in a subshell" + +requirements-completed: [] + +coverage: + - id: D1 + description: "Every D-07/D-47 static serving rule of the site plugin is pinned: content types, immutable only for _nuxt (not _nuxt/builds) and _fonts, no-cache with a strong ETag elsewhere, 304, HEAD, Range, dot-segment and traversal 404s, docs 301s, redirect Locations inside /docs/ and never //, tree 404 pages, no X-Robots-Tag/CSP/X-Frame-Options" + verification: + - kind: unit + ref: "sm-summercmsio-plugin static_test.go#TestStaticSite,TestStaticDocs,TestStaticConditionalAndRange,TestStaticNoBlockingHeaders,TestStaticRedirectLocations,TestStaticMissing404Page,TestNewHandlersMissingIndex,TestContentType,TestSiteImmutable" + status: pass + - kind: other + ref: "scripts/check-phase11.2.sh --plugin (17 named tests, coverage 98.2% >= 90.0%)" + status: pass + human_judgment: false + - id: D2 + description: "The plugin assembles alone through surf.Assemble (GET only, POST 405, /backend falls to the site 404), fails closed on an unbuilt tree, registers via init as golem15.summercms with no admin controllers, and its patterns coexist with cabana's on one ServeMux" + verification: + - kind: unit + ref: "sm-summercmsio-plugin routes_test.go#TestRoutesAssemble,TestRoutesFailClosed,TestRoutesCoexistWithAdminPatterns,TestPluginIdentity,TestPluginEmbeddedTree" + status: pass + human_judgment: false + - id: D3 + description: "Every branch of checkSiteURL, siteLabel, site_url/site_label parsing and option precedence; header escaping, link on index, section and 404 pages, exact unset header bytes; --site-url/--site-label in help and the label-without-URL error; docsite coverage 94.2%" + verification: + - kind: unit + ref: "internal/docsite#TestCheckSiteURL,TestSiteLabel,TestSiteURLPrecedence,TestParseSite,TestSiteLink; cmd/summer#TestDocsBuildSiteFlags,TestDocsSiteFlagsInHelp" + status: pass + - kind: other + ref: "scripts/check-phase11.2.sh --framework (named tests, coverage >= 85.0%, check-phase11.1.sh --docs and --forbidden)" + status: pass + human_judgment: false + - id: D4 + description: "D-40 terminal-check helpers loadTerminal, terminalScript and terminalEnv have ungated tests; the scroll-spy negative-tops edge case is covered" + verification: + - kind: unit + ref: "sm-summercmsio-app terminal_check_test.go#TestLoadTerminal,TestTerminalScript,TestTerminalEnv" + status: pass + - kind: unit + ref: "vue-summercmsio-app tests/scrollSpy.test.ts#sections scrolled past (negative tops) still count, so the last one stays active" + status: pass + human_judgment: false + - id: D5 + description: "scripts/check-phase11.2.sh --all runs framework, plugin, app, site, built, smoke, deploy, terminal and full, and prints phase11.2 all passed" + verification: + - kind: e2e + ref: "scripts/check-phase11.2.sh --all -> nine 'phase11.2 passed' lines, 'phase11.2 all passed', exit 0" + status: pass + human_judgment: false + - id: D6 + description: "11.2-VALIDATION.md is validated: per-task map filled and green, nyquist_compliant and wave_0_complete true, manual rows kept for visual UAT, rome bring-up, external links, the verbatim clone and the release build from the real tag" + verification: + - kind: other + ref: "grep 'status: validated' / 'nyquist_compliant: true' / TestExternalLinks match; grep -c '⬜ pending' = 0" + status: pass + human_judgment: false + - id: D7 + description: "External link reachability and the verbatim terminal clone at cutover (D-38), and the release build from the real v0.1.0 tag (D-42)" + verification: [] + human_judgment: true + rationale: "Needs the user to make golem15/summercms public and create the v0.1.0 tag; the gate has --terminal --verbatim and the release branch of --built ready for that run" + +duration: 13min +completed: 2026-10-01 +status: complete +--- + +# Phase 11.2 Plan 03: Unit test coverage and the phase gate Summary + +**Table-driven fstest tests pin every static serving and route rule of the site plugin (98.2% coverage). Every site_url/site_label branch and the D-40 helpers are tested, and one fail-closed `scripts/check-phase11.2.sh --all` proves the phase across the four repositories.** + +## Performance + +- **Duration:** 13 min +- **Started:** 2026-10-01T14:25:51Z +- **Completed:** 2026-10-01T14:39:19Z +- **Tasks:** 3 (tracer + 2 auto) +- **Files modified:** 11 (4 in summercms.go plus VALIDATION.md, 3 in the plugin, 1 in the app, 1 in the site, plus 2 gitlinks) + +## Accomplishments + +- **Site plugin (98.2% statement coverage, from fixtures only).** + - `static_test.go` covers content types, cache rules, ETag/304, HEAD, Range, dot-segment and traversal 404s, docs 301s, redirect Locations, a tree with no 404 page and the load errors. + - `routes_test.go` covers the surf assembly, fail-closed boot, coexistence with the admin patterns, the plugin's identity and init registration, and the embedded tree. + - `TestPageLinks` and `TestResolve` cover the link helpers. +- **Framework (docsite coverage 94.2%).** + - `TestCheckSiteURL` has 21 cases (5 accepted, 16 rejected). + - `TestSiteLabel` and `TestSiteURLPrecedence` (12 cases) cover labels and option-over-yaml precedence. `TestParseSite` gains rows for blank and two-line labels. + - `TestSiteLink` now checks the escaped label, the section page, the 404 page and the exact unset header bytes. + - `TestDocsSiteFlagsInHelp` checks both commands' help, and `docs:build --site-label` without a URL now has an error test. +- **App and site.** + - `TestLoadTerminal`, `TestTerminalScript` and `TestTerminalEnv` run ungated. + - One scroll-spy case was added for sections scrolled past. The other listed TS edge cases were already covered by the 11.2-01 tests. +- **Gate.** `scripts/check-phase11.2.sh` has the stages `--framework`, `--plugin`, `--app`, `--site`, `--built`, `--smoke`, `--deploy`, `--terminal [--verbatim]`, `--full` and `--all`. Unknown modes exit 2. `--all` passed end to end. +- **VALIDATION.md** is validated, with one row per task across the three plans and the cutover checks kept as manual rows. + +## Task Commits + +| Task | Commit | Repo | Message | +|------|--------|------|---------| +| 1 (tracer) | `549adaf` | sm-summercmsio-plugin | test: cover every static serving rule and the plugin routes | +| 1 (tracer) | `e7d9a2e` | sm-summercmsio-app | test: bump the site plugin to its static and route tests (gitlink) | +| 1 (tracer) | `18fe100` | summercms.go | test(11.2): add the phase gate with the plugin stage | +| 2 | `c3ddd5c` | vue-summercmsio-app | test: cover terminal and scroll-spy edge cases | +| 2 | `8c718a8` | sm-summercmsio-app | test: cover the terminal check helpers | +| 2 | `f34c335` | sm-summercmsio-app | test: bump the site to its scroll-spy edge case (gitlink) | +| 2 | `f5f9387` | summercms.go | test(11.2): cover site_url and site_label and gate the framework, app and site stages | +| 3 | `29adc23` | summercms.go | test(11.2): complete the phase gate | +| 3 | `a2f2d1e` | summercms.go | docs(11.2): validate the phase verification map | + +Nothing was pushed. No commit has a co-author trailer. No v0.1.0 tag was created. + +## Verification Evidence + +**Task 1 (tracer gate).** The run is interactive in `end-of-phase` mode and the tracer's verify is automated-only, so the verify was re-run and passed: +- `go vet` is clean. +- The plan's verify regex gives 16 top-level PASS lines. +- `TestStatic.*|TestRoutes.*|TestPlugin.*` gives 47 `--- PASS` lines, subtests included. +- `scripts/check-phase11.2.sh --plugin` passes: 17 named tests and 58 passing tests, subtests included. +- Coverage is 98.2%. +- `bash -n` is clean, and `--bogus` exits 2. +- A gate copy with a renamed test refuses with "named tests did not pass (missing, renamed or filtered out)". + +**Task 1 mutation check (siteImmutable).** +- Setup: in a scratch copy of the plugin (`GOWORK=off`), `siteImmutable` was changed to `return true` for `_nuxt/`. +- Result: `TestStaticSite` failed on `/_nuxt/builds/latest.json` and `/_nuxt/builds/meta/x.json` with `Cache-Control = "public, max-age=31536000, immutable", want "no-cache"`. + +**Task 2.** +- `go vet ./...` is clean. +- Eight top-level PASS lines from the plan's verify regex. +- `TestCheckSiteURL` prints 21 `--- PASS: TestCheckSiteURL/` lines. +- The app helpers give three PASS lines and no SKIP. +- `--framework`, `--app` and `--site` pass. The site stage had 30 tests: `# pass 30`, `# fail 0`, `# skipped 0`. + +**Task 2 mutation check (checkSiteURL).** +- Setup: in a `git archive HEAD` export of summercms.go with the new `load_test.go`, the `//` rejection in `checkSiteURL` was removed. +- Result: `TestCheckSiteURL/protocol-relative` failed with `checkSiteURL("//acme.example") = , want errSiteURL`. + +**Task 3.** `scripts/check-phase11.2.sh --all` exited 0. It printed all nine `phase11.2 passed` lines and `phase11.2 all passed`, with zero `refuse:`, `FAIL`, `--- FAIL` or `--- SKIP` lines. Stage by stage: +- **built:** a dev build, because summercms.go has no v0.1.0 tag. The three build-dependent tests passed. +- **smoke:** `smoke: ok` on postgres:15. +- **deploy:** `check-deploy: ok`. +- **terminal:** used the clone override (this checkout) and passed. +- **full:** `go vet ./...` and `go test ./... -count=1` ran with the Docker database suites. + +**Release branch of `--built`.** +- Setup: `SUMMERCMS_FRAMEWORK=/fw-release`, the 11.2-02 scratch clone that carries a local v0.1.0 tag. +- Result: `built: v0.1.0 found …, release build`, then `build: bin/summercms-io (release v0.1.0) ready` and `phase11.2 built passed`. + +**Plan verification.** +- All four repositories have an empty `git status --porcelain`. summercms.go still has the user's untracked `SummerCMS landing page.zip`, which predates this plan. +- Plugin coverage is 98.2% (≥ 90.0%). Docsite coverage is 94.2% (≥ 85.0%). + +## Files Created/Modified + +**summercms.go** +- `scripts/check-phase11.2.sh`: the phase gate, with stages and a `go test -json` detector modelled on `check-phase11.1.sh`. +- `internal/docsite/load_test.go`: `TestCheckSiteURL`, `TestSiteLabel`, `TestSiteURLPrecedence` and new `TestParseSite` rows. +- `internal/docsite/theme_test.go`: `TestSiteLink` covers escaping, the section page, the 404 page and the exact unset bytes. +- `cmd/summer/docs_test.go`: `TestDocsSiteFlagsInHelp`, and the label-without-URL case in `TestDocsBuildSiteFlags`. +- `11.2-VALIDATION.md`: validated. + +**sm-summercmsio-plugin** +- `static_test.go` and `routes_test.go` are new. +- `links_test.go` gains `TestPageLinks` and `TestResolve`. The build-gated tests are unchanged. + +**sm-summercmsio-app** +- `terminal_check_test.go` gains the three ungated helper tests. The two gitlink bumps are in the commit table. + +**vue-summercmsio-app** +- `tests/scrollSpy.test.ts` gains the negative-tops case. + +## Decisions Made + +See `key-decisions` in the frontmatter. In short: +- `--built` follows the same framework checkout as `build.sh`. +- The coexistence test takes the plugin's real patterns from surf's route table. +- The tests pin the shipped `terminalScript` override form (`git clone summercms`) and the shipped `pageLinks` contract (duplicates kept). + +## Deviations from Plan + +### Interpretation notes (no production change) + +**1. `terminalScript` override form** +- **Plan text:** "the first command becomes `git clone /tmp/fw`". +- **What was tested:** the shipped behaviour from the 11.2-02 Rule 1 fix, `git clone /tmp/fw summercms`. The plan says to test the shipped name and behaviour. + +**2. `pageLinks` duplicates** +- **Plan text:** "including duplicates removed and fragment-only links kept". This is ambiguous. +- **What was tested:** the shipped function keeps duplicates in document order, and `TestLandingLinks` dedupes. The test pins that, along with kept fragment links, decoded entities and the appended og:image. + +**3. Two plugin statements are not covered** +- `plugin.go:48` and `static.go:89` are the `fs.Sub` error returns. +- `fs.Sub` fails only for an invalid directory name, and the names here are the constants `"public"`, `"site"` and `"docs"`, so the branches cannot be reached. +- Coverage is 98.2% without them. + +**4. Extra tests beyond the plan's list** +- `TestResolve` in `links_test.go` covers the redirect follower and the `SUMMERCMS_SITE_DIR` override. +- The gate requires it too. + +**5. Extra commits in sm-summercmsio-app** +- The gitlink bumps (`e7d9a2e`, `f34c335`) are separate commits from the app's own test commit. +- The sequential executor instructions require committing the updated gitlink whenever a submodule changes. + +--- + +**Total deviations:** none needed a production fix. The new tests exposed no defect, so no production code changed in any repository. There are 5 interpretation or scope notes. +**Impact on plan:** none. All acceptance criteria pass. + +## Issues Encountered + +- **Scratch-copy mutation setup.** The first run needed `GOFLAGS=-mod=mod GOPROXY=off`, because the scratch module's `go.sum` lacked entries that the app's `go.work.sum` normally provides. No network was used. +- **`env -u` and bash functions.** `env -u` cannot call a bash function, so the gate unsets variables in subshells. `named_tests` cleans up its own log. + +## Known Stubs + +None. + +## Threat Flags + +None. This plan adds tests and a local gate script only. The T-11.2-15 mitigation (the detector refuses a skip, a fail, a missing test or zero tests, and `--bogus` exits 2) and the T-11.2-16 mitigation (the gate sets `SUMMERCMS_REQUIRE_BUILD=1` and `SUMMERCMS_TERMINAL_CHECK=1` itself and requires PASS) are in place. + +## User Setup Required + +None for this plan. These cutover items are unchanged from 11.2-02: +1. Push the three site repositories. +2. Make `golem15/summercms` public (D-38). +3. Create and push v0.1.0 (D-42). +4. Run `TestExternalLinks`, `scripts/check-phase11.2.sh --terminal --verbatim`, and `--built`, which will report a release build. +5. Deploy on rome. + +## Next Phase Readiness + +- Phase 11.2 has all three plans complete, and the gate is green. It is ready for `/gsd-verify-work`, where the manual rows are visual UAT and the cutover checks. +- Phase 11.3 can reuse `TestRoutesCoexistWithAdminPatterns` when it activates the admin beside the site. + +## Self-Check: PASSED + +- Files exist: `scripts/check-phase11.2.sh`, `internal/docsite/load_test.go`, `internal/docsite/theme_test.go` and `cmd/summer/docs_test.go`; the plugin's `static_test.go`, `routes_test.go` and `links_test.go`; the app's `terminal_check_test.go`; the site's `tests/scrollSpy.test.ts`; and `11.2-VALIDATION.md`. +- Commits were found in each repository: + - summercms.go: `18fe100`, `f5f9387`, `29adc23`, `a2f2d1e` + - app: `e7d9a2e`, `8c718a8`, `f34c335` + - plugin: `549adaf` + - site: `c3ddd5c` +- The summercms.go commit count was measured: `git rev-list --count 2229ca3..HEAD` = 4. +- `git tag -l v0.1.0` in summercms.go prints nothing. + +--- +*Phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin* +*Completed: 2026-10-01*