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

22 KiB

phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
phase plan subsystem tags requires provides affects actuals plan_head_before plan_head_after tech-stack key-files key-decisions patterns-established requirements-completed coverage duration completed status
11.2-ready-to-share-summercms-io-website-and-newsletter-plugin 02 infra
go-embed
static-site
nuxt
docsite
nginx
supervisor
postgres15
release
phase provides
11.2-01 vue-summercmsio-app (Nuxt landing page, terminal.json single command source, en.json copy)
phase provides
11.1 summer docs:build static docs site and the docs checker
sm-summercmsio-plugin (golem15.summercms) serving the embedded Nuxt build at / and the docs at /docs
sm-summercmsio-app with go.work workspace, config, build (release/dev), smoke and check-deploy scripts
framework docs say PostgreSQL 15 or newer (verified on postgres:15)
docsite site_url/site_label keys and --site-url/--site-label flags (header link back to the main site)
link, terminal-command, docs-header and external-link checks against the built site
DEPLOY.md with nginx, supervisor, env template and rollback copy for rome
11.2-03
cutover
v0.1.0 tag
tokens tasks commits commits_all_repos
24561 5 2 8
130b6ac38f a494375db7
added patterns
Site plugin embeds build output with //go:embed all:public and serves in-memory trees with strong ETags and per-path Cache-Control
Release builds compile the framework from a tag export through a temporary go.work replace
Env-gated build-dependent tests (SUMMERCMS_REQUIRE_BUILD, SUMMERCMS_TERMINAL_CHECK, SUMMERCMS_CHECK_EXTERNAL)
created modified
../sm-summercmsio-app/plugins/golem15/summercms/plugin.go
../sm-summercmsio-app/plugins/golem15/summercms/static.go
../sm-summercmsio-app/plugins/golem15/summercms/links_test.go
../sm-summercmsio-app/plugins/golem15/summercms/README.md
../sm-summercmsio-app/scripts/build.sh
../sm-summercmsio-app/scripts/smoke.sh
../sm-summercmsio-app/scripts/check-deploy.sh
../sm-summercmsio-app/terminal_check_test.go
../sm-summercmsio-app/DEPLOY.md
../sm-summercmsio-app/README.md
../sm-summercmsio-app/deploy/nginx/summercms.io.conf
../sm-summercmsio-app/deploy/supervisor/summercms-io.conf
../sm-summercmsio-app/deploy/env.example
../sm-summercmsio-app/deploy/rollback/under-construction/index.html
../sm-summercmsio-app/deploy/rollback/under-construction/logo.png
README.md
docs/setup/installation.md
internal/docsite/load.go
internal/docsite/docsite.go
internal/docsite/emit.go
internal/docsite/theme/templates/header.html
internal/docsite/theme/assets/site.css
cmd/summer/docs.go
docs/console/utilities.md
D-42 resolved as defer-tag: no v0.1.0 tag created or pushed in summercms.go; the release path was proven against a scratch clone with a local tag, and creating + pushing v0.1.0 (and pushing master) is a DEPLOY.md launch step
Terminal check clone override appends the page's clone directory (git clone <override> summercms) so the page's own cd summercms works for a local path override
check-deploy.sh moves listen ports to unprivileged loopback ports in its temp copy because nginx -t binds the listeners
DEPLOY.md installs the supervisor program on the first release (after the binary exists) and uploads with --rsync-path="sudo rsync" into root-owned bin/ and config/
Build-dependent plugin tests skip unless SUMMERCMS_REQUIRE_BUILD=1, and fail (not skip) on a missing build when it is set
Landing links are resolved through the real surf-assembled handler with at most one root-relative 301
id description verification human_judgment
D1 One summercms-io binary embeds the Nuxt site and the docs and serves / and /docs with indexable responses, cache rules, ETag/304, 301s, real 404s, no /backend and 405 on POST, booted on postgres:15
kind ref status
e2e scripts/smoke.sh (release binary, postgres:15) -> smoke: ok pass
kind ref status
unit plugins/golem15/summercms/smoke_test.go#TestStaticSmoke pass
false
id description verification human_judgment
D2 Framework database suites pass on postgres:15 and README/installation docs say PostgreSQL 15 or newer (D-25)
kind ref status
integration HEAD export with postgres:15: go test -count=1 -p 4 lagoon, lagoon/attach, cabana, beachcomber, lighthouse, bouncer, conga, docs/examples/blog pass
false
id description verification human_judgment
D3 docsite site_url/site_label keys and --site-url/--site-label flags add a validated header link back to the main site; unset output unchanged apart from additive CSS (D-41/D-46)
kind ref status
unit go test ./internal/docsite ./cmd/summer -run '^(TestDocsTree|TestDocsBuildRealTree|TestToolCommandNames|TestParseSite|TestSiteLink|TestDocsBuildSiteFlags)$' pass
kind ref status
other scripts/check-phase11.1.sh --docs && --forbidden pass
false
id description verification human_judgment
D4 Release build takes docs, summer CLI and compiled framework from the v0.1.0 tag (proven on a scratch clone with a local tag; real tag deferred)
kind ref status
e2e SUMMERCMS_FRAMEWORK=<scratch clone> scripts/build.sh -> build: bin/summercms-io (release v0.1.0) ready; go version -m shows summercms v0.1.0 => <tmp>/fw pass
false
id description verification human_judgment
D5 Every landing link resolves against the built site, the page shows every terminal command and comment, and the docs header links back to / (SC3, D-40, D-44, D-46)
kind ref status
integration SUMMERCMS_REQUIRE_BUILD=1 plugins/golem15/summercms#TestLandingLinks,TestTerminalCommandsInPage,TestDocsHeaderSiteLink pass
kind ref status
e2e SUMMERCMS_TERMINAL_CHECK=1 SUMMERCMS_CLONE_URL=../summercms.go go test -run TestTerminalCommands . (handled=true) pass
false
id description verification human_judgment rationale
D6 DEPLOY.md, nginx and supervisor configs, env template and rollback copy cover launch, setup, build, upload, release, cutover, verification and rollback on rome (SC4)
kind ref status
other scripts/check-deploy.sh -> check-deploy: ok (nginx -t + supervisor parse) pass
true Bringing the site up on rome by following DEPLOY.md (and the external link / verbatim terminal checks, which need the public repository) is the cutover UAT item; it cannot run before the user pushes the repos, makes golem15/summercms public and creates the tag.
22min 2026-10-01 complete

Phase 11.2 Plan 02: summercms.io app, site plugin, release path and deploy Summary

One summercms-io binary that embeds the Nuxt landing page and the SummerCMS docs. It serves them on PostgreSQL 15 with indexable, cache-correct responses. The framework gained PG15 docs and a docs header link back to the site. The release build is proven from a (scratch) v0.1.0 tag, every landing link and terminal command is checked against the built site, and DEPLOY.md covers rome.

Performance

  • Duration: 22 min (two executor sessions: tasks 1-2, then the tag checkpoint, then tasks 4-5)
  • Started: 2026-10-01T14:00:22Z
  • Completed: 2026-10-01T14:22:03Z
  • Tasks: 5 (4 auto/tracer, 1 decision checkpoint)
  • Files modified: 10 in summercms.go, 26 in sm-summercmsio-app plus plugin (excluding generated build output)

Accomplishments

  • Site plugin golem15.summercms (module git.golem15.com/golem15/sm-summercmsio-plugin). It embeds public/ with all: and serves / and /docs from in-memory trees with these behaviours:
    • strong ETags with 304 revalidation;
    • immutable caching for _nuxt/ (except builds/) and _fonts/, and no-cache for everything else;
    • a 301 from extension-less docs URLs to .html;
    • the tree's own 404 page, and dot-segment paths refused;
    • no robots-blocking header, no CSP and no admin.
  • App sm-summercmsio-app is a go.work workspace with the plugin and site as submodules, config without secrets, summer build generated sources, and these scripts:
    • scripts/build.sh, with release (default, from the tag) and dev modes;
    • scripts/smoke.sh, which runs postgres:15, migrate, serve and HTTP assertions, including the D-46 docs header link;
    • scripts/check-deploy.sh.
  • Framework (summercms.go):
    • The PG15 suites pass, and README.md and docs/setup/installation.md say "PostgreSQL 15 or newer".
    • docs/site.yaml accepts the site_url and site_label keys, and docs:build and docs:serve take --site-url and --site-label. Both are validated: javascript: and //host are rejected.
  • Checks:
    • TestLandingLinks checks 30 distinct links through the real surf.Assemble handler, including all ten D-44 docs targets and /docs.
    • TestTerminalCommandsInPage is the D-40 drift guard.
    • TestDocsHeaderSiteLink checks the docs header link back to /.
    • TestExternalLinks runs at cutover.
    • TestTerminalCommands runs the six page commands from a fresh shell to handled=true.
  • DEPLOY.md covers the launch checklist, server setup, build, upload, release, cutover, verification and rollback. It comes with the nginx config (HTTP→HTTPS, a 443 www→apex block, gzip, /backend denied, GET/HEAD only, proxy to 127.0.0.1:8095), the supervisor program, an env template and a copy of the live "Under construction" page.

Task Commits

Task Commit Repo Message
1 (tracer) 0ab96ed sm-summercmsio-plugin feat: serve the embedded site at / and the docs at /docs
1 (tracer) e0b9b7c sm-summercmsio-app feat: wire the summercms.io app with build and smoke scripts
2A 7936234 summercms.go docs: require PostgreSQL 15 or newer (verified on postgres:15)
2B a494375 summercms.go feat(docsite): optional site_url and site_label link back to the main site
3 — — decision checkpoint, resolved defer-tag (no commit)
4 7774763 sm-summercmsio-plugin test: verify the landing links and terminal commands against the built site
4 699785a sm-summercmsio-app feat: release builds from the v0.1.0 tag and the terminal command check
5 fe77fa7 sm-summercmsio-plugin docs: describe the site plugin
5 77eef5d sm-summercmsio-app docs: add DEPLOY.md with nginx, supervisor and rollback configs

Each app commit carries the updated plugin gitlink. Nothing was pushed anywhere, and no commit has a co-author trailer.

D-42 tag decision: defer-tag

  • What the user chose: "defer-tag" at the blocking-human checkpoint (Task 3).
  • summercms.go is untouched: git tag -l v0.1.0 in summercms.go prints nothing, and nothing was pushed.
  • How the release path was proven: against the scratch clone /tmp/claude-1000/-media-nvme-dev-golem15-summercms-io-summercms-summercms-go/b0d2a3f5-592c-4ca7-8d0c-8f89dbb2cfac/scratchpad/fw-release. That clone is a git clone of the local summercms.go with a local annotated v0.1.0 tag at a494375 (the Task 2 docsite commit). The build ran as SUMMERCMS_FRAMEWORK=<clone> scripts/build.sh.
  • Release build output: build: bin/summercms-io (release v0.1.0) ready. go version -m bin/summercms-io shows dep git.golem15.com/golem15/summercms v0.1.0 => /tmp/…/fw (devel), so the compiled framework is the tag export.
  • Without the tag: plain scripts/build.sh against the real checkout stops with build: tag v0.1.0 not found in …/summercms.go; run 'scripts/build.sh dev' or create the tag (DEPLOY.md).
  • Launch checklist step 3 in DEPLOY.md:
    1. Review the commit.
    2. git tag -a v0.1.0 -m "SummerCMS Alpha 0.1" <reviewed-sha>.
    3. git push origin master.
    4. git push origin v0.1.0.
    5. Confirm with git ls-remote --tags origin v0.1.0.

Verification Evidence

D-25, PostgreSQL 15 (previous session).

  • Setup: HEAD export, with postgres:16-alpine replaced by postgres:15 in 9 test files, run as go test -count=1 -p 4 -v.
  • Result: EXIT=0, and all 11 containers were postgres:15 (log: scratchpad/pg15.log).
  • Packages, every line ok, no FAIL:
    • modules: lagoon 53.2s, lagoon/attach 66.9s, cabana 65.6s, beachcomber 44.4s, beachcomber/typesense 0.2s, lighthouse 22.2s, lighthouse/centrifugo 0.1s, bouncer 8.0s, conga 19.7s;
    • docs/examples: blog 5.5s, blog/console, blog/controllers, blog/models, blog/updates.

Task 1 tracer gate (previous session). scripts/build.sh dev and scripts/smoke.sh ("smoke: ok") passed, go vet was clean in the app and the plugin, and TestStaticSmoke passed. All Task 1 acceptance criteria passed.

Task 2.

  • go vet ./... is clean.
  • The six named tests pass: TestDocsTree, TestDocsBuildRealTree, TestToolCommandNames, TestParseSite, TestSiteLink and TestDocsBuildSiteFlags.
  • scripts/check-phase11.1.sh --docs and --forbidden pass.
  • Re-run at the end of this session: go vet ./... && go test ./internal/docsite ./cmd/summer -count=1 ok, and both phase11.1 checks passed.

Task 4.

  • Release build: see the D-42 section above.
  • scripts/smoke.sh → smoke: ok, with the new D-46 /docs/ site-link assertions.
  • SUMMERCMS_REQUIRE_BUILD=1 go test ./... in the plugin: PASS for TestLandingLinks, TestTerminalCommandsInPage, TestDocsHeaderSiteLink and TestStaticSmoke; TestExternalLinks SKIP (it is gated).
  • SUMMERCMS_TERMINAL_CHECK=1 SUMMERCMS_CLONE_URL=…/summercms.go go test -run TestTerminalCommands: PASS. The output included built hello in 1.93s, golem15.greeter active and … handled=true.
  • grep -c 'class="site-link" href="/"' public/docs/index.html = 1.
  • go vet ./... and go test ./... -count=1 pass in the app (gated tests skip).
  • file bin/summercms-io: ELF 64-bit x86-64, statically linked.

Task 5.

  • scripts/check-deploy.sh → check-deploy: ok. It runs nginx -t on nginx 1.30.4 with a temp self-signed certificate, temp DH parameters and loopback ports. The only output is deprecation warnings for listen … http2, which is kept for rome's assumed nginx 1.22, per A3.
  • All acceptance greps match. git ls-files lists no .env.

Files Created/Modified

summercms.go (Task 2, previous session)

  • README.md and docs/setup/installation.md: PostgreSQL 15 or newer.
  • internal/docsite/{load,docsite,emit}.go, theme/templates/header.html, theme/assets/site.css: the site_url/site_label link.
  • cmd/summer/docs.go: the flags. docs/console/utilities.md: the docs, with a neutral acme.example example.
  • Tests in load_test.go, theme_test.go and cmd/summer/docs_test.go.

sm-summercmsio-plugin

  • plugin.go, static.go, smoke_test.go, go.mod/go.sum, .gitignore, public/README.md (Task 1).
  • links_test.go (Task 4) and README.md (Task 5).

sm-summercmsio-app

  • Workspace, config, generated sources and submodules (Task 1).
  • scripts/build.sh: release mode. scripts/smoke.sh: D-46 assertion. terminal_check_test.go (Task 4).
  • DEPLOY.md, README.md, deploy/**, scripts/check-deploy.sh (Task 5).

Decisions Made

  • D-42: defer-tag (user decision). See the section above.
  • Clone override in terminalScript. A non-empty SUMMERCMS_CLONE_URL becomes git clone <override> summercms, with the directory taken from path.Base of the page URL. The page's next command, cd summercms, then works for a local override such as …/summercms.go. With no override, the commands run verbatim.
  • check-deploy.sh test copy. It rewrites the listen ports to 127.0.0.1:18480 / [::1]:18480 and 127.0.0.1:18443 / [::1]:18443 (overridable), because nginx -t binds the listeners and a non-root check cannot bind 80 or 443. The committed config keeps 80 and 443.
  • DEPLOY.md choices:
    • bin/ and config/ are root-owned, so the service cannot rewrite its own binary; storage/ is owned by summercms.
    • Uploads use --rsync-path="sudo rsync".
    • The supervisor program is installed on the first release, because autostart before the binary exists would put the program into FATAL.
    • The key comes from ./bin/summercms-io key:generate, which prints ✓ <key>.
  • TestLandingLinks scope. It also checks src links, _payload.json?<uuid> and the og:image (https://summercms.io/og-image.png mapped to /og-image.png), not only hrefs.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] The terminal check's clone override changed the clone directory

  • Found during: Task 4.
  • Issue: git clone /…/summercms.go clones into summercms.go/, so the page's next command, cd summercms, failed (cd: summercms: No such file or directory).
  • Fix: with an override, terminalScript passes the page URL's directory name explicitly (git clone <override> summercms). The verbatim run (no override) is unchanged.
  • Files modified: sm-summercmsio-app/terminal_check_test.go.
  • Verification: TestTerminalCommands PASS with handled=true.
  • Committed in: 699785a.

2. [Rule 3 - Blocking] nginx -t binds the listen ports

  • Found during: Task 5.
  • Issue: As a normal user, nginx -t failed with bind() to 0.0.0.0:80 failed (13: Permission denied).
  • Fix: check-deploy.sh rewrites the listen directives in its temp copy to unprivileged loopback ports, and fails if any 80/443 listen remains unreplaced. The DH parameters are generated with openssl dhparam -dsaparam instead of dropping the ssl_dhparam line.
  • Files modified: sm-summercmsio-app/scripts/check-deploy.sh.
  • Verification: check-deploy: ok. Earlier, a failing run showed the script surfaces nginx errors.
  • Committed in: 77eef5d.

3. [Rule 1 - Bug, previous session] checkSiteURL also rejects backslashes

  • Issue: browsers treat /\host as //host, which would make a protocol-relative link to another host.
  • Fix: backslashes are rejected, and --site-label is validated as one non-empty line. The override logic lives in load() in load.go.
  • Committed in: a494375.

4. [Rule 3 - Blocking, previous session] summer plugin:add wrote an absolute path into the go.work use list

  • Fix: rewritten to ./plugins/golem15/summercms with go work edit. go mod tidy before the first summer build dropped the requires: they were restored, the build ran, the module was tidied, and toolchain go1.27.0 was re-added.
  • Follow-up: the framework quirks are logged in deferred-items.md.
  • Committed in: e0b9b7c.

5. [Acceptance-check note, previous session] Task 2 byte-identical check

  • Issue: a literal diff -r pre post cannot be empty, because the content of utilities.md changed and the .site-link rules live in the shared assets/site.css.
  • Isolated check: the pre-change docs source was built with the new binary. Only assets/site.css differs, by 17 additive lines. All 72 HTML pages, the .md copies, llms*.txt and search-index.json are byte-identical. An unset build has 0 site-link in index.html and 404.html.

6. [Acceptance-check note] The location ^~ /backend grep

  • Issue: the plan's literal grep -n 'location ^~ /backend' cannot match, because GNU BRE treats the mid-pattern ^ as an anchor.
  • Check: grep -nF 'location ^~ /backend' matches line 50 of deploy/nginx/summercms.io.conf.

Total deviations: 4 auto-fixed (2 bugs, 2 blocking) and 2 acceptance-check notes. Impact on plan: All fixes were needed for the checks to run as specified. There was no scope creep, and no new Go module or npm dependency.

Issues Encountered

  • HEAD on master. The executor's protected-branch assertion reports master as protected. This project commits directly on master (branching_strategy none, use_worktrees false), and the sequential orchestrator dispatch said to use normal commits on the main working tree, as the previous session did for 7936234 and a494375. Commits were made on master, and none were pushed.
  • Generated build output. bin/summercms-io and plugins/golem15/summercms/public/{site,docs} now hold a release build from the scratch tag. These paths are gitignored.

Known Stubs

None. The gated tests (TestExternalLinks, and TestTerminalCommands without an override) are designed to run at cutover, not stubs.

Threat Flags

None beyond the plan's threat model. T-11.2-04/05/06/07/08/09/11/12/14 are mitigated as planned. T-11.2-13 (the tag) is deferred to the user at cutover.

User Setup Required

These are the cutover steps from the DEPLOY.md launch checklist, all for the user:

  1. Push sm-summercmsio-plugin, vue-summercmsio-app, then sm-summercmsio-app. The origins are already set.
  2. Make golem15/summercms public (D-38).
  3. Create v0.1.0 of git.golem15.com/golem15/summercms at the reviewed commit, push it, and push summercms.go master (D-42, deferred).
  4. Run TestExternalLinks and the verbatim TestTerminalCommands.
  5. On rome: check that port 8095 is free, do the one-time setup, save the existing summercms.io server block into deploy/rollback/nginx-under-construction.conf, then deploy and cut over.

Next Phase Readiness

  • Plan 11.2-03 (unit test coverage) can build on the stable interfaces: Plugin, newHandlers, tree, newTree, contentType, siteImmutable, redirectTo, pageLinks, resolve, requireBuild, terminalScript, terminalEnv, loadTerminal, checkSiteURL and siteLabel.
  • The cutover is blocked only on the user steps above.

Self-Check: PASSED

  • Files exist: plugin.go, static.go, links_test.go and README.md in the plugin; build.sh, smoke.sh, check-deploy.sh, terminal_check_test.go, DEPLOY.md, README.md, the deploy/nginx and deploy/supervisor configs, deploy/env.example and deploy/rollback/under-construction/{index.html,logo.png} in the app.
  • Commits found: 0ab96ed, 7774763 and fe77fa7 (plugin); e0b9b7c, 699785a and 77eef5d (app); 7936234 and a494375 (summercms.go).
  • No v0.1.0 tag in summercms.go, and nothing pushed.

Phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin Completed: 2026-10-01