Files
summercms/.planning/phases/04-cli-scaffolding-i18n-and-mail/04-VERIFICATION.md
2026-09-18 14:23:56 +02:00

22 KiB
Raw Blame History

phase, verified, status, score, overrides_applied, human_verification, decision_coverage
phase verified status score overrides_applied human_verification decision_coverage
04-cli-scaffolding-i18n-and-mail 2026-09-18T12:22:19Z passed 3/3 must-haves verified 0
honored total not_honored
21 21

Phase 4: CLI scaffolding, i18n and mail Verification Report

Phase Goal: Scaffolding commands generate compiling stubs for every plugin artifact type, and plugins can register translated, CLDR-pluralized, namespaced strings and mail templates rendered through a pluggable driver interface. Verified: 2026-09-18T12:22:19Z Status: passed Re-verification: No — initial verification

Must-haves are the three ROADMAP success criteria (they override plan-level truths). Plan must_haves (D-01…D-21) were checked as supporting evidence. The ROADMAP goal is an infrastructure/tooling outcome, not a product UI; MVP developer-flow coverage is mapped from Plan 01's objective below.

04-REVIEW.md status is issues (4 warnings, 3 info, 0 critical). Those findings do not break must-haves or the phase goal and are recorded as non-blocking notes.

User Flow Coverage

User story (Plan 01 objective): «As a plugin developer, I want to generate compiling plugin artifacts, resolve translated strings, and send registered mail, so that I can port WinterCMS plugins into one SummerCMS binary.»

Step Expected Evidence Status
Run summer make:* for plugin, model, migration, command, job, admin-controller Stubs compile and pass go vet TestScaffoldAllArtifacts generates all six into a copied hello workspace then go build/go vet; TestMakeCommandsViaCLI drives public cobra commands ✓
Resolve vendor.plugin::group.key for pl and en Nested keys, CLDR plurals, :name substitution, fallback Hello LangFS YAML + TestHelloPluginResolvesLang; TestPluralSmoke CLDR one/few/many and Laravel pipes ✓
Register mail template/layout and send Dotted names, -en sibling, html/template + Goldmark, driver Send Hello HasMailTemplates; memory render asserts subject/HTML/text; TestSMTPMailpit go-mail receipt in Mailpit ✓
Outcome Phase gate green with Docker required This session: ./scripts/check-phase4.sh → phase4 check passed; forced -count=1 on scaffold/i18n/mail including Mailpit PASS 1.88s ✓

Goal Achievement

Observable Truths

# Truth Status Evidence
1 summer make:plugin, make:model, make:migration, make:command, make:job and make:admin-controller each generate stubs that compile and pass go vet ✓ VERIFIED cmd/summer/main.go registers all six; make:plugin → build.MakePlugin; the others → build.MakeModel/MakeMigration/MakeCommand/MakeJob/MakeAdminController. Stubs render from embedded internal/build/stubs/*.tmpl through go/format. TestScaffoldAllArtifacts (this session 6.196s) writes Album+Widget models (golem15_demo_albums, timestamps, --no-migration), a separate AddAlbumIndex migration, Purge command, Reindex job (Kind()/Work, no River), Albums admin controller + fields.yaml/columns.yaml, then go build ./... and go vet on the copied workspace and plugin. Registry bytes are stable; handwritten plugin.go is untouched. TestMakeCommandsViaCLI executes make:plugin/make:model --no-migration/make:job/make:command through cobra. TestModelsLeaf fails summer build naming plugin ID, file, and sibling import.
2 A translation key vendor.plugin::group.key resolves for pl and en, including a CLDR plural form, loaded from per-plugin per-locale YAML files with parameter substitution ✓ VERIFIED Hello embeds lang/en/lang.yaml and lang/pl/lang.yaml. Keys are golem15.hello::lang.greeting / lang.labels.pluginName. phrasebook.Activate walks HasLang after Register and publishes *phrasebook.Translator before Boot. Get/Choice use towel.Locale; go-i18n v2.6.1 selects only the CLDR category via IdentityParser. This session: go test ./phrasebook ./party -count=1 ok; go -C examples/hello test ./... -count=1 ok. TestHelloPluginResolvesLang: pl=Cześć, en=Hello, nested labels, pl-PL→pl, de→configured en, missing key returns the raw key. TestPluralSmoke: pl albums 1/2/5/22 → one/few/many/few with :count; en {0}/{1}/[2,*] pipes; :name/:Name/:NAME → hi alice Alice ALICE; {{.Count}} stays literal.
3 A plugin registers a mail template and layout by dotted name with the per-locale suffix convention, and it renders via html/template through a driver interface (SMTP via go-mail) in a test send ✓ VERIFIED Hello registers golem15.hello::mail.hello, golem15.hello::mail.hello-en, layout alias hello → golem15.hello::mail.layouts.hello. Send takes the full dotted name and does no locale lookup. Render: html/template on Markdown → Goldmark HTML (goldmark.New(), no html.WithUnsafe) → substituted Markdown as text. Drivers: memory, log, smtp (github.com/wneessen/go-mail DialAndSendWithContext) behind postcard.Driver. mail.driver + SUMMER_MAIL__DRIVER. This session: TestMailRenderSmoke subject Witaj Ada / Hello Ada, HTML <strong>Ada</strong>, text Witaj **Ada**.; TestSMTPMailpit started axllent/mailpit:v1.31.1, sent via go-mail TLS none, Mailpit HTTP API asserted To/subject/HTML/text — PASS 1.88s. Missing template/layout fails party: boot <plugin ID> with the name.

Score: 3/3 truths verified

Supporting plan truths (D-01…D-21: Winter directory shape, models-leaf check, registry.gen.go, gormigrate create-table, admin/job contracts, embedded templates, argument inference, HasLang load/flatten, both plural syntaxes, parameter case variants, fallback chain, en/en framework defaults, Winter mail headers, Goldmark safety, -en siblings, layout aliases, three drivers, Mailpit vs -short, backpack Mailer.Send, no-retry driver errors) all hold in code; they are not extra score rows.

Required Artifacts

gsd-sdk query verify.artifacts: 19/19 passed across the four plans.

Artifact Expected Status Details
internal/build/registry.go Deterministic per-plugin registry.gen.go ✓ VERIFIED Scan Code generated by summer make; atomic temp+rename; sorted models/migrations/commands/jobs/admin slices
internal/build/leaf.go models/ sibling-import enforcement ✓ VERIFIED checkModelsLeaf before go build; reports plugin ID, file, import
internal/build/stubs/plugin.tmpl Winter-shaped plugin root, routes, leaves, go.mod ✓ VERIFIED plugin.go/routes.go/leaf doc.go; embeds config/lang/views/mail; generated accessors
internal/build/stubs/artifacts.tmpl Model, migration, command, job, admin, YAML stubs ✓ VERIFIED GORM timestamps + TableName; gormigrate CREATE/DROP; pact.Job without River; fields.yaml/columns.yaml
internal/build/stubs/registry.tmpl Generated-registry template ✓ VERIFIED generatedModels/Migrations/Commands/Jobs/AdminControllers
cmd/summer/main.go Six make commands ✓ VERIFIED make:plugin plus five artifact commands; --no-migration bare flag
pact/capabilities.go Job, admin, lang, mail contracts ✓ VERIFIED Job/JobArgs, AdminController, HasLang, HasMailTemplates
phrasebook/loader.go Embedded YAML catalog load ✓ VERIFIED lang/<locale>/<group>.yaml; flatten; CLDR map vs nested map; goccy/go-yaml
phrasebook/translator.go Get/Choice, CLDR, pipes, fallback, params ✓ VERIFIED towel.Locale; per-locale i18n.Bundle; :name/:Name/:NAME; missing-key log once
party/registry.go HasLang + HasMailTemplates at Activate ✓ VERIFIED phrasebook.Activate then postcard.Activate after Register; BootPlugin before Boot
postcard/templates.go Winter template/layout parse and render ✓ VERIFIED INI + ==; html/template then Goldmark; layout .Content
postcard/mailer.go App-scoped Send(ctx, Message) ✓ VERIFIED Full dotted name; optional subject override; one wrap, no retry
postcard/drivers.go memory, log, SMTP behind Driver ✓ VERIFIED go-mail client; TLS mandatory/starttls/none; FailDriver
postcard/assets/default.htm Neutral framework layout ✓ VERIFIED Header + text wrapper + HTML content-body
internal/build/build_test.go Six-artifact compile/vet + leaf check ✓ VERIFIED TestScaffoldAllArtifacts, TestModelsLeaf
phrasebook/translator_test.go Catalog, CLDR, pipe, substitution, fallback ✓ VERIFIED TestTranslationSmoke, TestPluralSmoke, TestLocaleFallbackSmoke, TestCatalogBoundaries
postcard/mailpit_test.go Real SMTP receipt via Mailpit HTTP API ✓ VERIFIED This session PASS; -short skips only this test
scripts/check-phase4.sh Repeatable root/hello/Mailpit/race gate ✓ VERIFIED Refuses missing Docker; no -short; this session phase4 check passed
examples/hello/plugins/base/plugin.go HasLang + HasMailTemplates on the testbed plugin ✓ VERIFIED Embedded lang/ and views/mail/; hello + hello-en + hello layout

Artifacts: 19/19 verified

internal/build/artifact.go (Make* generators) and internal/build/scaffold.go (MakePlugin + embedded templates) are the shared scaffold entry the CLI calls; they are substantive and wired even though only registry.go/leaf.go/scaffold templates appear in plan 01 artifact paths.

gsd-sdk query verify.key-links reported 2/12 verified. Same false negative as Phases 1–3: the checker greps target filenames inside source; Go imports package paths. Manual wiring:

From To Via Status Details
cmd/summer/main.go internal/build/scaffold.go / artifact.go make commands call build.Make* ✓ WIRED MakePlugin in scaffold.go; MakeModel/Migration/Command/Job/AdminController in artifact.go (same package). TestMakeCommandsViaCLI executes cobra → those funcs.
internal/build/artifact.go internal/build/registry.go finishArtifact → refreshRegistry ✓ WIRED Every successful artifact write refreshes registry.gen.go then go mod tidy. Plan listed scaffold.go; refresh lives next to the Make* writers. MakePlugin seeds an empty registry from registry.tmpl.
internal/build/build.go internal/build/leaf.go checkModelsLeaf before go build ✓ WIRED App line 40 calls checkModelsLeaf then exec.CommandContext(..., "go", "build", ...).
examples/hello/plugins/base/plugin.go pact.HasLang LangFS embedded assets ✓ WIRED Compile-time assert _ pact.HasLang; //go:embed lang. gsd-sdk also verified this link.
party/registry.go phrasebook (loader.go via Activate) ordered HasLang scan before Boot ✓ WIRED phrasebook.Activate(app, ordered) after all Register; cat.Load(p.ID(), hl.LangFS()).
phrasebook/translator.go towel/context.go towel.Locale request-context seam ✓ WIRED localeFrom → towel.Locale(ctx); hello test uses towel.WithLocale.
examples/hello/plugins/base/plugin.go pact.HasMailTemplates embedded mail FS + names ✓ WIRED MailTemplatesFS/MailTemplates/MailLayouts; gsd-sdk verified this link.
party/registry.go postcard (mailer.go) postcard.Activate + BootPlugin then Publish[Mailer] ✓ WIRED Activate publishes Mailer; BootPlugin registers declared templates/layouts and fails named missing files.
postcard/mailer.go postcard/drivers.go driver.Send after render ✓ WIRED m.catalog.render then validateRendered then m.driver.Send.
scripts/check-phase4.sh postcard/mailpit_test.go full go test ./... without -short ✓ WIRED Script runs go test ./... (includes TestSMTPMailpit) after refusing missing Docker.
internal/build/build_test.go internal/build/scaffold.go copied hello app drives public Make/Build API ✓ WIRED copyHelloApp → MakePlugin/MakeModel/… → AddPlugin → App.
examples/hello/hello_test.go party/registry.go Activate publishes translator and mailer ✓ WIRED party.Activate then Lookup[*phrasebook.Translator] and Lookup[postcard.Mailer].

Wiring: 12/12 connections verified (manual)

Data-Flow Trace (Level 4)

Artifact Data Source Produces real data Status
Make* stubs Go sources + YAML Embedded text/template + go/format Yes — this session TestScaffoldAllArtifacts go build/go vet on generated plugin ✓ FLOWING
phrasebook.Translator vendor.plugin::group.key strings Per-plugin lang/<locale>/<group>.yaml Yes — hello embedded en/pl; CLDR few/many from go-i18n category selection + YAML maps ✓ FLOWING
postcard.Mailer.Send subject, HTML, text html/template + Goldmark + layout wrappers Yes — memory driver stores rendered parts; Mailpit HTTP API observed the SMTP payload ✓ FLOWING
SMTP driver RFC 5322 message go-mail DialAndSendWithContext Yes — this session Mailpit To=ada@example.test, subject Witaj Ada, HTML <strong>Ada</strong> ✓ FLOWING

Requirements Coverage

PLAN frontmatter IDs: CLI-02 (01, 04), I18N-01 (02, 04), I18N-03 (03, 04). REQUIREMENTS.md maps the same three to Phase 4. No Phase 4 orphans. Every ID is accounted for.

I18N-02 (per-request preferred_locale) is Phase 7. CLI-03 (migrate rollback) is Phase 5. Admin schema depth is Phase 9. River adaptation of pact.Job is Phase 11. Those are later-phase requirements, not Phase 4 gaps.

Requirement Source Plan Description Status Evidence
CLI-02 01, 04 Scaffolding commands generate a plugin, model, migration, command, job and admin controller with stubs that compile ✓ SATISFIED All six commands exist; generated plugin with every artifact type go build/go vet this session; CLI path proven for plugin/model/job/command
I18N-01 02, 04 Translation keys use vendor.plugin::group.key, load from per-plugin per-locale YAML, parameters and CLDR plurals (go-i18n) for pl and en ✓ SATISFIED Hello YAML + phrasebook Get/Choice; Polish CLDR map and English pipes; :name case variants; go-i18n used only for category labels
I18N-03 03, 04 Plugins register mail templates and layouts by dotted name with per-locale suffix, rendered with html/template, sent through a driver interface (SMTP via go-mail) ✓ SATISFIED hello / hello-en registration; memory + SMTP drivers; Mailpit receipt this session

Coverage: 3/3 requirements satisfied

Decision Coverage

All trackable CONTEXT.md decisions are honored by shipped artifacts. gsd-sdk query check.decision-coverage-verify: 21/21 honored, not_honored: []. Lang D-01–D-05, mail D-06–D-09 and D-18–D-21, scaffolding D-10–D-17.

Behavioral Verification

Check Result Detail
./scripts/check-phase4.sh ✓ This session: docker available; root go vet/go test ./...; hello vet/test; -race ./internal/build ./phrasebook ./postcard; printed phase4 check passed (~14s; some packages cached from the implementation session)
go test ./internal/build ./cmd/summer -run 'TestScaffold…|TestModelsLeaf|TestMakeCommandsViaCLI|TestToolCommandNames' -count=1 ✓ internal/build 6.196s; cmd/summer 0.262s
go test ./phrasebook ./party -count=1 ✓ phrasebook 0.007s; party 0.009s
go -C examples/hello test ./... -count=1 ✓ 0.595s including TestHelloPluginResolvesLang and TestHelloPluginSendsMail
go test ./postcard -count=1 ✓ 1.913s including Mailpit
go test ./postcard -run TestSMTPMailpit -count=1 -v ✓ Container axllent/mailpit:v1.31.1 created, SMTP send, HTTP API receipt, PASS 1.88s
go vet ./... and go -C examples/hello vet ./... ✓ Clean this session
go test ./postcard -run TestSMTPMailpit -short skip-only-Mailpit ℹ not re-run Code: if testing.Short() { t.Skip(...) } then t.Fatalf on testcontainers error — Docker absence fails the full test, matching D-19. Gate does not pass -short.

Anti-Patterns Found

No FIXME / XXX / TBD / HACK / coming soon / not implemented in internal/build, phrasebook, postcard, cmd/summer, pact, or hello plugin sources.

File Line Pattern Severity Impact
internal/build/leaf.go 22–80 Models leaf check is prefix-only, non-recursive, skips plugins outside the app root ⚠️ Warning Advisory WR-01 from 04-REVIEW.md. Happy-path sibling import in models/*.go is rejected (TestModelsLeaf). Nested packages / aliased replace paths are bypassable. Does not break SC1: generated stubs still compile and the documented check fires on the intended shape.
internal/build/registry.go 80–111 Concurrent make:* can lose registry entries (no flock) ⚠️ Warning Advisory WR-02. Single-writer TestScaffoldAllArtifacts is stable. Agent-parallel make:* is a real race, not a missing feature.
phrasebook/loader.go 243–263 Catalog keys keep directory spelling (pt_BR vs pt-BR) ⚠️ Warning Advisory WR-03. D-04 pl-PL→pl works because files live under pl (hello + tests). Winter pt_BR trees are not a Phase 4 success criterion.
postcard/templates.go 336–409 Vars can carry template.HTML; regex is not a sanitizer ⚠️ Warning Advisory WR-04. String Vars cannot drop raw <script> (TestMailRenderSmoke / TestMailRecipientsAndSafety). Typed HTML bypass is a later hardening item, not SC3.
postcard/drivers.go 163–165 SMTP errors wrapped without extra redaction ℹ️ Info IN-01. Dial-to-port-1 tests assert secretpass is absent.
phrasebook/loader.go 80–107 Extra files in LangFS fail boot ℹ️ Info IN-02. Fail-closed against traversal; editor backups in lang/ cannot boot.
postcard/templates.go 25, 406–408 data: banned in all rendered HTML ℹ️ Info IN-03. Conservative for v1; blocks inline data:image/*.
internal/build/artifact.go 177 Generated command Description: "TODO: describe " + cmdName ℹ️ Info Scaffold placeholder in compiling stubs (CLI-02), not unfinished production code.

Anti-patterns: 8 found (0 blockers, 4 warnings, 4 info)

Code-review warnings WR-01…WR-04 do not fail the scaffolding/i18n/mail goal. No security-review agent was required for this phase (no auth/crypto surface in the ROADMAP goal).

Test Quality Audit

Test File Linked Req Active Skipped Circular Assertion Level Verdict
internal/build/build_test.go CLI-02 yes no no Behavioral (go build/go vet of generated plugin; leaf-import error text) ✓
cmd/summer/main_test.go CLI-02 yes no no Behavioral (cobra help + make:* writes files) ✓
phrasebook/translator_test.go I18N-01 yes no no Value (pl/en strings, CLDR counts, pipe {0}, params) ✓
party/registry_test.go I18N-01, I18N-03 yes no no Behavioral (translator/mailer published before Boot; named boot errors) ✓
examples/hello/hello_test.go I18N-01, I18N-03 yes no no Value (Cześć/Hello/pl-PL/raw key) + Behavioral (Send hello and hello-en) ✓
postcard/mailer_test.go I18N-03 yes no no Value (subject/HTML/text) + Behavioral (memory/log/smtp/env, no retry) ✓
postcard/templates_test.go I18N-03 yes no no Value (recipients, escaped script, registration ownership) ✓
postcard/smtp_test.go I18N-03 yes no no Behavioral (TLS policy; errors omit credentials) ✓
postcard/mailpit_test.go I18N-03 yes -short only no Value (Mailpit To/subject/HTML/text after real SMTP) ✓

Disabled tests on requirements: 0 blockers (-short skip is the documented fast loop; sign-off is check-phase4.sh without -short). Circular patterns detected: 0. Expected mail subject/HTML/text are authored in the test (Witaj Ada, <strong>Ada</strong>), not captured from the SUT. CLDR expected strings are hand-written Polish/English forms, not dumped from go-i18n. Insufficient assertions: 0. Scaffold proof is compile/vet, not file-exists. Translation proof is exact strings. Mail proof is Mailpit HTTP body, not Send returning nil alone. Provenance: VALID — expected translation strings come from the YAML fixtures; expected mail parts come from the Winter-shaped templates the test registers. Mailpit is an independent SMTP observer.

Human Verification Required

N/A — Infrastructure/tooling phase with no product UI. Acceptance is compiling stubs, catalog lookup, and a Mailpit SMTP receipt. Production SMTP credentials stay in mail.* / SUMMER_MAIL__ (deployment config). VALIDATION.md: all phase behaviors have automated verification.

Gaps Summary

No gaps found. Phase goal achieved. All three ROADMAP success criteria hold in the codebase and were exercised this session (forced -count=1 scaffold/i18n/mail tests, live Mailpit receipt, scripts/check-phase4.sh). Advisory code-review warnings do not break must-haves.

I18N-02 (user preferred_locale per request) remains Phase 7 by design. pact.Job → River remains Phase 11. Admin fields.yaml pipeline remains Phase 9. Not Phase 4 gaps.

None — status is passed.

Verification Metadata

Verification approach: Goal-backward from ROADMAP success criteria Must-haves source: ROADMAP.md success criteria (override PLAN.md frontmatter) Automated checks: 19 artifacts passed, 12 key links passed (manual; gsd-sdk filename-grep false negatives excluded), 3/3 truths, 3/3 requirements, 21/21 decisions Human checks required: 0 Total verification time: ~12 min


Verified: 2026-09-18T12:22:19Z Verifier: the agent (subagent)