22 KiB
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 |
|
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.
Key Link Verification
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.
Recommended Fix Plans
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)