From 1f204f5a6c2d2289d434105e0e22f94693ae5705 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Sun, 18 Oct 2026 14:55:11 +0200 Subject: [PATCH] docs(quick-261018-k9s): add the D-17 module in summercms.go --- .planning/STATE.md | 10 +- .../261018-k9s-PLAN.md | 217 ++++++++++++++++++ .../261018-k9s-SUMMARY.md | 126 ++++++++++ 3 files changed, 348 insertions(+), 5 deletions(-) create mode 100644 .planning/quick/261018-k9s-add-the-d-17-module-in-summercms-go/261018-k9s-PLAN.md create mode 100644 .planning/quick/261018-k9s-add-the-d-17-module-in-summercms-go/261018-k9s-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index fc164e5e..85a9805d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,10 +5,10 @@ current_phase: "19.1" current_phase_name: Chat satellites status: executing stopped_at: Completed 19.1-05-PLAN.md -last_updated: "2026-10-18T12:02:40.895Z" +last_updated: "2026-10-18T12:55:02.551Z" last_activity: 2026-10-18 -last_activity_desc: Phase 19.1 execution started -state_head: 782a6720bf2bcdce1dcaa3c1c9a98c69f1d44998 +last_activity_desc: "Completed quick task 261018-k9s: add the D-17 module in summercms.go (modules/breakwater); 19.1-02 forum plan unblocked" +state_head: 98bc6de8f57fda4bd3ccc1880ba22b9ce1d6582a progress: total_phases: 41 completed_phases: 33 @@ -32,7 +32,7 @@ See: .planning/PROJECT.md (updated 2026-10-12) Phase: 19.1 (Chat satellites) — EXECUTING Plan: 4 of 6 Status: Ready to execute -Last activity: 2026-10-18 — Phase 19.1 execution started +Last activity: 2026-10-18 — Completed quick task 261018-k9s: add the D-17 module in summercms.go (modules/breakwater); 19.1-02 forum plan unblocked Progress: [████████░░] 80% @@ -891,7 +891,6 @@ Recent decisions affecting current work: - Phase 8 UI gate: scripts/check-phase8-ui.mjs --final-gate's real Playwright browser matrix (32 UI-SPEC scenarios) is unimplemented (deliberate fatal() at check-phase8-ui.mjs:458, never authored by 08-05); stage_ui_harness must keep failing closed until a Playwright spec is authored. User approved Phase 8 closure on 2026-09-24 with this gap carried forward -- see 08-10-SUMMARY.md and .planning/phases/08-oauth2-1-authorization-server/deferred-items.md. - pending: push sm-user-plugin — 12.1-05 Task 3 condition (c) failed (`git ls-remote --tags origin v0.1.3` empty). Push summercms.go master and annotated tag v0.1.3 (local on df5cace) first, then the plugin, then fonoteka.go. Recorded in 12.1-05-SUMMARY.md. - 16.2-05 open UAT items: re-save HTML post 1 untouched (D-03 evidence vacuous), D-19 category edit check, root vhost install (frame-src), unpublish/delete test post 105 -- D-17 Render/Purify missing: 19.1-02 halted after the D-16 forum-policy note (2740ab2). modules/ has cabana.RenderMarkdown only. Run /msd-quick in summercms.go for a markdown module exporting Render(markdown) and Purify(html) with the named forum policy, then re-run /msd-execute-phase 19.1. Do not create sm-forum-plugin until that lands. ### Quick Tasks Completed @@ -935,6 +934,7 @@ Recent decisions affecting current work: | 36 | summercms.io copy Alpha 0.1 → 0.2 (vue b5d6dbb, app 6a13a87), deployed | 2026-10-15 | 853ea1f | — | | 261016-0fm | Admin minors for v0.2.1: theme switch, language switch, settings nav on form pages | 2026-10-16 | 66fd4ae | [261016-0fm-admin-minors-for-v0-2-1-theme-switch-lan](./quick/261016-0fm-admin-minors-for-v0-2-1-theme-switch-lan/) | | 261016-s8x | PaymentGateway: banner bottom margin and hide disabled sections from admin navigation | 2026-10-16 | ab9f385 | [261016-s8x-paymentgateway-banner-bottom-margin-and-](./quick/261016-s8x-paymentgateway-banner-bottom-margin-and-/) | +| 261018-k9s | add the D-17 module in summercms.go: modules/breakwater Render(markdown)/Purify(html) with the forum bluemonday policy | 2026-10-18 | 98bc6de | [261018-k9s-add-the-d-17-module-in-summercms-go](./quick/261018-k9s-add-the-d-17-module-in-summercms-go/) | ## Deferred Items diff --git a/.planning/quick/261018-k9s-add-the-d-17-module-in-summercms-go/261018-k9s-PLAN.md b/.planning/quick/261018-k9s-add-the-d-17-module-in-summercms-go/261018-k9s-PLAN.md new file mode 100644 index 00000000..f358a19d --- /dev/null +++ b/.planning/quick/261018-k9s-add-the-d-17-module-in-summercms-go/261018-k9s-PLAN.md @@ -0,0 +1,217 @@ +--- +phase: quick-261018-k9s +plan: 01 +quick_id: 261018-k9s +slug: add-the-d-17-module-in-summercms-go +type: execute +mode: quick +wave: 1 +depends_on: [] +files_modified: + - modules/breakwater/breakwater.go + - modules/breakwater/breakwater_test.go + - modules/breakwater/README.md + - README.md + - docs/architecture/introduction.md + - docs/services/markdown.md +autonomous: true +requirements: [D-16, D-17] +source: + - .planning/phases/19.1-chat-satellites/19.1-CONTEXT.md (D-16, D-17, D-18) + - .planning/notes/2026-10-18-bluemonday-forum-policy.md + - .planning/STATE.md blocker "D-17 Render/Purify missing" + +estimate: + tokens: 70000 + raw_tokens: 70000 + tasks: 2 + confidence: low + +must_haves: + truths: + - "A plugin can import one framework package and turn user Markdown into HTML that contains no script, iframe, img, event handler or javascript:/data: URL, while GFM tables, fenced code, strikethrough, links and autolinks render (D-16, D-17)." + - "The same package sanitizes already-HTML with the identical allow-list, so trainings-style stored HTML goes through the policy without a Markdown pass (D-17 purify callers)." + - "Blank or whitespace-only input renders and purifies to the empty string, as PHP MarkdownRenderer does." + - "Every link the policy keeps carries rel nofollow; every fully-qualified http/https link also carries target _blank (PHP HTML.Nofollow + HTML.TargetBlank)." + - "The 19.1-02 forum gate finds `func Render(` and `func Purify(` under modules/ outside cabana.RenderMarkdown, so forum execution unblocks." + - "go vet ./..., go test -short ./... and summer docs:build --check are green; go.mod and go.sum are unchanged." + artifacts: + - "modules/breakwater/breakwater.go — package breakwater: unexported goldmark GFM engine, unexported bluemonday forum policy, exported Render and Purify" + - "modules/breakwater/breakwater_test.go — PHP ForumMarkdownRenderTest corpus, links/autolinks, blank input, Purify allow-list" + - "modules/breakwater/README.md — standard module README structure" + - "README.md — module table row for breakwater" + - "docs/architecture/introduction.md — breakwater in the framework modules table" + - "docs/services/markdown.md — docs page: Markdown and HTML sanitizing for user content" + key_links: + - "Render = goldmark (extension.GFM, default safe renderer) -> Purify; Purify = forum policy Sanitize. Both return \"\" on blank input." + - "The policy is built once at package init and shared; bluemonday policies are read-only once built (seaglass precedent)." + - "Docs: every `breakwater.X` code span in README/docs must exist in the package (docs checker)." +--- + + +Add the D-17 framework module: `modules/breakwater`, a Markdown renderer and HTML sanitizer for user-submitted content, exporting `Render(markdown string) string` and `Purify(html string) string` with the named forum policy from the 2026-10-18 decision note (1:1 with the PHP `golem15/forum` HTMLPurifier config). It is the gate the forum plans (19.1-02) and later the Wavepath `purify()` callers consume; `cabana.RenderMarkdown` stays untouched as the admin preview gate and `seaglass` stays the admin rich-text policy. + +Purpose: 19.1-02 is halted on the STATE.md blocker "D-17 Render/Purify missing". Per D-17 the renderer is a framework module delivered by a quick task, not forum code. +Output: the `breakwater` package with tests, its README, a root README row, the introduction.md table row and a `docs/services/markdown.md` page; one commit. + + + +@~/.claude/msd-core/workflows/execute-plan.md +@~/.claude/msd-core/templates/summary.md + + + +@/media/nvme/dev/golem15/summercms.io/summercms/summercms.go/CLAUDE.md +@/media/nvme/dev/golem15/summercms.io/summercms/summercms.go/.planning/notes/2026-10-18-bluemonday-forum-policy.md +@/media/nvme/dev/golem15/summercms.io/summercms/summercms.go/.planning/notes/2026-10-11-framework-html-sanitizer-bluemonday.md +@/media/nvme/dev/golem15/summercms.io/summercms/summercms.go/modules/seaglass/seaglass.go +@/media/nvme/dev/golem15/summercms.io/summercms/summercms.go/modules/seaglass/README.md +@/media/nvme/dev/golem15/summercms.io/summercms/summercms.go/modules/cabana/field_markdown.go +@/media/nvme/dev/golem15/summercms.io/summercms/summercms.go/internal/docsite/render.go +@/media/nvme/dev/jz/wavepath.org/plugins/golem15/forum/classes/markdown/MarkdownRenderer.php +@/media/nvme/dev/jz/wavepath.org/plugins/golem15/forum/tests/security/ForumMarkdownRenderTest.php + +Facts verified at planning time (2026-10-18, live reads): +- `go.mod` already pins `github.com/microcosm-cc/bluemonday v1.0.27` (line 24) and `github.com/yuin/goldmark v1.8.6` (line 34). No dependency is added or bumped. +- `modules/` has no `breakwater`; the word appears nowhere in `modules/`, `docs/`, `README.md` or `.planning/notes/`. +- goldmark v1.8.6 default renderer (no unsafe option): raw HTML blocks and inline raw HTML are written as the comment `` (renderer/html/html.go:390,398,656); a link or autolink whose destination fails `IsDangerousURL` is written with an empty href (html.go:518,591,620). +- bluemonday v1.0.27: `AllowURLSchemes` turns on parseable-URL checking and leaves relative URLs disallowed; an `a` whose only attribute was dropped is not in the default no-attribute element set, so the tag is removed and its text kept (seaglass smoke test documents the same); `AddTargetBlankToFullyQualifiedLinks(true)` also adds `noopener` to `rel` (sanitize.go:717-752); comments are dropped unless allowed. +- 19.1-02-PLAN.md line 174-175 gate: `grep -n 'func Render(' modules --glob '*.go' | grep -v RenderMarkdown` and `grep -n 'func Purify(' modules --glob '*.go'` must each print a line. +- Docs checker (`internal/docsite/check_identifiers.go`): code spans of the form `pkg.Ident` / `pkg.Ident(...)` must name an identifier that exists in `modules/`; `src=` snippets and links/anchors are checked too. Module READMEs are ingested as API reference pages automatically. +- Root README module table is alphabetical (`bouncer` row precedes `cabana`); introduction.md "The framework modules" table has rows Plugins and the container / HTTP / Data / Admin / Services / Console and tooling / Utilities. +- `docs/services/*.md` front-matter: `title`, `description`, `section: services`, `order` (highest existing order is 160, frontend-and-ajax.md). + + + + + + Task 1: breakwater package — Render and Purify with the named forum policy, end-to-end under test + modules/breakwater/breakwater.go, modules/breakwater/breakwater_test.go + + - .planning/notes/2026-10-18-bluemonday-forum-policy.md (the locked policy: D-16/D-17) + - modules/seaglass/seaglass.go (package doc style, package-level policy var, bluemonday builder calls) + - modules/cabana/field_markdown.go (package-level goldmark engine precedent; do not modify this file) + - internal/docsite/render.go lines 26-31 (in-tree `goldmark.New(goldmark.WithExtensions(extension.GFM), ...)` form) + - /media/nvme/dev/jz/wavepath.org/plugins/golem15/forum/classes/markdown/MarkdownRenderer.php and tests/security/ForumMarkdownRenderTest.php (reference behavior and corpus) + + `grep -n 'bluemonday v1.0.27\|goldmark v1.8.6' go.mod` prints two lines and `ls modules/breakwater` fails (the package does not exist yet). + + Test file `modules/breakwater/breakwater_test.go`, package `breakwater_test`, importing `git.golem15.com/golem15/summercms/modules/breakwater`; table-driven where natural; `strings.ToLower` the output before negative checks as the PHP test does: + - TestRenderXSS (the PHP `test_xss_sanitization` corpus verbatim: "\n\n[x](javascript:alert(1))\n\n\n\n"): the lowercased output does not contain `alert(1)")` and `Render("text bold more")` contain no `alert(1)`, no `onclick`, no `
")` returns a string that contains `

plain

` and none of `` marker. The package imports neither seaglass nor cabana nor `golang.org/x/net/html`; `cabana.RenderMarkdown` and `modules/seaglass` are untouched (`git diff --exit-code HEAD~1 -- modules/cabana modules/seaglass` is empty). + +## Gate output + +- `go vet ./...` — exit 0, no output. +- `go test -short ./...` — exit 0 (all `ok` / `no test files`; the only failures seen during the task were the docs-tree tests before the `src=` fix below, resolved). +- `go install ./cmd/summer && summer docs:build --check` — `docs:build: no problems found` (run as `$(go env GOPATH)/bin/summer`; `~/go/bin` is not on this shell's PATH). +- `git diff --exit-code go.mod go.sum` — clean; bluemonday v1.0.27 and goldmark v1.8.6 were already pinned. +- `go test -short -count=1 ./modules/breakwater -v` — PASS: TestRenderXSS, TestRenderStripsRawHTML, TestRenderGFM, TestRenderLinks, TestBlankInput, TestPurifyAllowList, TestConcurrentUse, ExampleRender, ExamplePurify (0.012s). +- `git show --stat HEAD` lists the plan's six files plus `modules/breakwater/example_test.go` (7 files, 430 insertions, 1 deletion — the rewritten introduction.md row). + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] docs/ Go fences must carry `src=`; added `modules/breakwater/example_test.go`** +- **Found during:** Task 2, gate `go test -short ./...` (cmd/summer docs-tree tests) and `summer docs:build --check`: `docs/services/markdown.md:13: snippet: go code block has no src= reference`. +- **Issue:** The plan assumed the docs checker only verifies `src=` snippets, so it prescribed a plain fenced Go block on the docs page. `internal/docsite/check_policy.go` rejects every Go fence in a `docs/` page without `src=` (module READMEs are exempt; the README's plain block passed). +- **Fix:** Following the in-tree convention (`docs/services/events.md` → `modules/festival/example_test.go#ExampleBus_Fire`), added `modules/breakwater/example_test.go` with `ExampleRender` and `ExamplePurify` (their `// Output:` lines make them real tests of the documented output), split the docs page block into two `src=modules/breakwater/example_test.go#Example…` blocks and ran `summer docs:sync`. The checker was not weakened. +- **Files modified:** modules/breakwater/example_test.go (new), docs/services/markdown.md. +- **Commit:** 98bc6de (the plan's single commit; it therefore has seven files, not six). + +**2. [Rule 1 - Bug] Corrected a docs claim about inline raw HTML** +- **Found during:** Task 2, before the gates. +- **Issue:** The first draft of `docs/services/markdown.md` said `text bold more` renders to `

text more

`. goldmark replaces each raw tag with an omitted-HTML comment but keeps the inner text, so the real output is `

text bold more

` (verified with a throwaway test, since removed). +- **Fix:** Docs text corrected to the real output. The plan's own test (`TestRenderStripsRawHTML`) only asserts `text` and `more` survive, which is still true. +- **Files modified:** docs/services/markdown.md. +- **Commit:** 98bc6de. + +## Tracer feedback gate + +Task 1 is `type="tracer"`; interactive mode with `human_verify_mode` at its default (`end-of-phase`) and an automated-only ``: re-ran the verify (vet, module tests, go.mod clean, no `WithUnsafe`/`UGCPolicy`/seaglass/cabana imports, both gate greps) — passed, so execution continued to Task 2 without a checkpoint. + +## Known Stubs + +None. Both exported functions are fully wired; no placeholder values. + +## Threat Flags + +None beyond the plan's register. T-k9s-01/02/04 are pinned by TestRenderXSS, TestRenderStripsRawHTML, TestRenderLinks and TestPurifyAllowList as the plan assigned. No new network endpoint, auth path, file access or schema surface was introduced. + +## Not done by the executor (orchestrator's part) + +- Docs commit of this SUMMARY and the quick-task PLAN (`.planning/quick/261018-k9s-…` left untracked on purpose). +- STATE.md: blocker "D-17 Render/Purify missing" to be cleared; ROADMAP.md untouched (quick task). +- Nothing pushed, nothing tagged. + +## Self-Check: PASSED + +All five created files exist on disk; commit 98bc6de is in the log; `modules/cabana/field_markdown.go` and `modules/seaglass/` are byte-identical to HEAD.