diff --git a/.planning/phases/04-cli-scaffolding-i18n-and-mail/04-03-SUMMARY.md b/.planning/phases/04-cli-scaffolding-i18n-and-mail/04-03-SUMMARY.md new file mode 100644 index 0000000..a7cfadc --- /dev/null +++ b/.planning/phases/04-cli-scaffolding-i18n-and-mail/04-03-SUMMARY.md @@ -0,0 +1,149 @@ +--- +phase: 04-cli-scaffolding-i18n-and-mail +plan: 03 +subsystem: mail +tags: [postcard, mail, goldmark, go-mail, smtp, wintercms] + +requires: + - phase: 04-cli-scaffolding-i18n-and-mail + provides: pact.HasMailTemplates, party.Activate Register-then-Boot order + - phase: 01-framework-kernel-foundation + provides: backpack.Publish/Lookup, compass mail.* and SUMMER_MAIL__ layering +provides: + - postcard Mailer with Send(ctx, Message) through memory, log, and SMTP drivers + - Winter-shaped template and layout parser (INI, ==, Markdown body) + - Goldmark HTML with unsafe HTML disabled and header/address validation + - hello plugin mail templates, -en sibling, and hello layout +affects: [04-04-tests, plugin-porting, I18N-03] + +tech-stack: + added: [github.com/yuin/goldmark@v1.8.6, github.com/wneessen/go-mail@v0.8.1] + patterns: + - html/template on Markdown then Goldmark HTML; substituted Markdown is the text part + - HasMailTemplates catalogs validate at Boot and publish Mailer before Boot + - mail.driver selects memory, log, or smtp with explicit TLS including opt-in NoTLS + +key-files: + created: + - postcard/templates.go + - postcard/mailer.go + - postcard/drivers.go + - postcard/assets/default.htm + - postcard/mailer_test.go + - examples/hello/plugins/base/views/mail/hello.htm + - examples/hello/plugins/base/views/mail/hello-en.htm + - examples/hello/plugins/base/views/mail/layouts/hello.htm + - examples/hello/config/mail.yaml + modified: + - party/registry.go + - party/registry_test.go + - examples/hello/plugins/base/plugin.go + - examples/hello/hello_test.go + - go.mod + - go.sum + +key-decisions: + - "Goldmark v1.8.6 is used without html.WithUnsafe; final HTML is rejected if script/iframe, event handlers, or javascript/vbscript/data schemes remain" + - "postcard.Mailer is published after Register and before Boot; each HasMailTemplates catalog is validated at that plugin's Boot transition" + - "mail.smtp.tls defaults to mandatory STARTTLS; none/notls is opt-in for Mailpit and is never inferred" + +patterns-established: + - "Dotted names vendor.plugin::mail.name map to views/mail/.htm; short layout aliases resolve only within the owning plugin" + - "Send takes the full dotted name including any -en suffix and performs no locale lookup" + - "Driver errors return to Send with one postcard wrap and no retry; SMTP errors must not include credentials or bodies" + +requirements-completed: [I18N-03] + +duration: 6min +completed: 2026-09-18 +--- + +# Phase 4 Plan 03: postcard mail Summary + +**Winter-shaped mail templates and layouts render safe Markdown/HTML and send through memory, log, or go-mail SMTP selected by `mail.*`** + +## Performance + +- **Duration:** 6 min +- **Started:** 2026-09-18T11:56:07Z +- **Completed:** 2026-09-18T12:02:31Z +- **Tasks:** 3 +- **Files modified:** 15 + +## Accomplishments + +- `postcard` parses INI headers, `==` separators, and Markdown bodies; `html/template` substitutes vars, Goldmark renders HTML, and the substituted Markdown is the text part +- Plugins register dotted template names and short layout aliases; missing files and unknown layouts fail `party: boot ` with the full name +- `postcard.Mailer` is app-scoped; hello sends `golem15.hello::mail.hello` and `hello-en` through backpack, using the plugin `hello` layout +- `mail.driver` selects memory, log, or smtp (`SUMMER_MAIL__` overrides); SMTP uses explicit TLS, address/CRLF checks, and context-aware go-mail send without retries + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Render and send one registered template through memory** - `b4fd833` (feat) +2. **Task 2: Register plugin layouts and publish the mailer at Boot** - `1e51597` (feat) +3. **Task 3: Add config-selected log and SMTP delivery** - `1b72456` (feat) + +**Plan metadata:** (this commit) + +## Files Created/Modified + +- `postcard/templates.go` - Winter parser, catalog registration, Goldmark render, HTML safety +- `postcard/mailer.go` - Message/Mailer, Activate, BootPlugin, config-selected driver +- `postcard/drivers.go` - Driver interface plus memory, log, SMTP, and FailDriver +- `postcard/assets/default.htm` - Neutral framework layout +- `postcard/mailer_test.go` - render, layout, and driver smokes +- `party/registry.go` - Publish mailer before Boot; validate catalogs at Boot +- `examples/hello/plugins/base/plugin.go` - HasMailTemplates with hello and hello-en +- `examples/hello/plugins/base/views/mail/` - Distinct default-locale and -en templates plus hello layout +- `examples/hello/config/mail.yaml` - memory driver and documented smtp/NoTLS shape +- `go.mod` / `go.sum` - goldmark v1.8.6 and go-mail v0.8.1 + +## Decisions Made + +- Goldmark stays on v1 (v2 is still a new line); unsafe HTML stays disabled and final HTML is re-checked +- Default layout is internal (`layout = default` or omitted); plugins cannot alias `default` +- TLS empty/mandatory maps to go-mail `TLSMandatory`; `none`/`notls` is explicit Mailpit opt-in; `starttls` is opportunistic only when named +- Mailpit real SMTP receipt stays Plan 04 (D-19); this plan uses an in-process FailDriver and a closed-port SMTP error + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 2 - Missing Critical] Three-section layout parser shipped with Task 1** +- **Found during:** Task 1 (embedded default layout) +- **Issue:** The neutral default layout is header + text wrapper + HTML wrapper, so memory-driver HTML/text wrapping could not wait for Task 2 +- **Fix:** Parse two-`==` layouts in `templates.go` in Task 1; Task 2 still added plugin aliases, party Boot validation, and the hello layout +- **Files modified:** `postcard/templates.go`, `postcard/assets/default.htm` +- **Verification:** `TestMailRenderSmoke` asserts `content-body` from the default layout +- **Committed in:** `b4fd833` (Task 1 commit) + +--- + +**Total deviations:** 1 auto-fixed (1 missing critical) +**Impact on plan:** No behavior divergence from D-06–D-09 or D-18–D-21. Layout parsing was required to honor the Task 1 default-layout artifact. + +## Issues Encountered + +None + +## User Setup Required + +None - no external service configuration required. Production SMTP credentials stay in `mail.*` / `SUMMER_MAIL__`; Mailpit is Plan 04. + +## Next Phase Readiness + +Ready for 04-04 (unit tests and Mailpit). Memory render, layout wrappers, named boot errors, log/SMTP driver contracts, and a failing-driver seam are in place. Do not treat `-short` as SMTP receipt. + +## Verification + +- `go test ./postcard -run 'TestMailRenderSmoke|TestMailLayoutSmoke|TestMailDriverSmoke' -short -count=1` — PASS +- `go -C examples/hello test ./...` — PASS +- `go vet ./... && go test ./...` — PASS + +## Self-Check: PASSED + +--- +*Phase: 04-cli-scaffolding-i18n-and-mail* +*Completed: 2026-09-18*