docs(04-03): complete postcard mail plan

Tasks completed: 3/3
- Render and send one registered template through memory
- Register plugin layouts and publish the mailer at Boot
- Add config-selected log and SMTP delivery

SUMMARY: .planning/phases/04-cli-scaffolding-i18n-and-mail/04-03-SUMMARY.md
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-09-18 14:03:11 +02:00
parent 1b7245661d
commit 3b476cbf55

View File

@@ -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/<dot-path>.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 <plugin ID>` 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*