208 lines
7.7 KiB
Markdown
208 lines
7.7 KiB
Markdown
# Phase 4: CLI scaffolding, i18n and mail - Discussion Log
|
|
|
|
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
|
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
|
|
|
**Date:** 2026-09-17 / 2026-09-18
|
|
**Phase:** 04-cli-scaffolding-i18n-and-mail
|
|
**Areas discussed:** Lang file & plural syntax, Mail template format, Scaffold stub behavior, Mail drivers & test send
|
|
|
|
---
|
|
|
|
## Lang file & plural syntax
|
|
|
|
### Plural message form
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| CLDR category map | `{one, few, many, other}` YAML map; 1:1 onto go-i18n; covers Polish's 4th category | |
|
|
| Laravel pipe strings | `":count wpis\|:count wpisy\|:count wpisów"` plus `{0}`/`[2,*]` ranges, per summer-phrasebook | |
|
|
| Both accepted | Map = CLDR categories; string with `\|` = Laravel-style | ✓ |
|
|
|
|
**Notes:** Fonoteka PHP lang files contain no pipe plurals; the pipe form is for line-by-line ports of other WinterCMS plugins.
|
|
|
|
### Parameter placeholders
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| `:name` | WinterCMS/Laravel style with `:Name`/`:NAME` variants; phrasebook substitutes, go-i18n only selects plural category | ✓ |
|
|
| `{{.Name}}` go-i18n native | Every ported string rewritten | |
|
|
| You decide | | |
|
|
|
|
### Missing key
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Fallback chain, then raw key | requested → parent → fallback → key string; logged once in dev/test | ✓ |
|
|
| Fallback chain, then error | `(string, error)` or panic in dev | |
|
|
| You decide | | |
|
|
|
|
### Default locale
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Config-driven, en default | `app.locale` / `app.fallback_locale`, framework default `en` | ✓ |
|
|
| Config-driven, pl default | | |
|
|
| You decide | | |
|
|
|
|
---
|
|
|
|
## Mail template format
|
|
|
|
### File shape
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| YAML front matter + body | `---` header parsed with goccy/go-yaml | |
|
|
| Keep WinterCMS INI + `==` | Exactly the PHP file shape; small hand-written header parser | ✓ |
|
|
| Go registration + body-only files | Subject/layout declared in Go | |
|
|
|
|
### Body format
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Markdown, add a renderer | html/template for vars, then Markdown → HTML; raw Markdown is the text part; one new dependency (goldmark expected) | ✓ |
|
|
| Plain HTML only | Zero new deps; six templates hand-converted; no text part | |
|
|
| HTML now, Markdown later | Body-renderer seam, decide at Phase 12/13 | |
|
|
|
|
### Per-locale suffix
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Suffix lookup with fallback | postcard tries `name-<locale>` then `name` | |
|
|
| Caller picks the full name | Exactly like PHP; no locale logic in postcard | ✓ |
|
|
| You decide | | |
|
|
|
|
### Layouts
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Registered by short name, HTML + text | Mirrors `registerMailLayouts` and the three-section PHP layout; framework ships `default` | ✓ |
|
|
| HTML wrapper only | Drops the text section | |
|
|
| You decide | | |
|
|
|
|
---
|
|
|
|
## Scaffold stub behavior
|
|
|
|
### Wiring
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Auto-wire via generated registry file | Per-plugin `registry.gen.go`; plugin.go never rewritten | ✓ |
|
|
| Edit plugin.go in place | go/ast insertion | |
|
|
| Write file, print the line to add | | |
|
|
|
|
### make:model output
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Model + migration, flag to skip | `--no-migration` | ✓ |
|
|
| Model only | | |
|
|
| Model + migration + fields.yaml/columns.yaml | No consumer until Phase 9 | |
|
|
|
|
### make:admin-controller before Phase 9
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Minimal interface + placeholder YAML | Tiny pact interface, WinterCMS-shaped YAML placeholders | ✓ |
|
|
| Go stub only, no YAML | | |
|
|
| You decide | | |
|
|
|
|
### Stub templates
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Embedded text/template .tmpl files | `internal/build/stubs`, go/format, compile test in examples/hello | ✓ |
|
|
| Keep Go string builders | | |
|
|
| You decide | | |
|
|
|
|
### make:job before River
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Framework job interface, River-shaped | `Kind()` on args, `Work(ctx, args) error`; no River import | ✓ |
|
|
| Import River now | River enters go.mod six phases early | |
|
|
| You decide | | |
|
|
|
|
### File layout inside a plugin
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Flat, one package, name suffixes | fonoteka.go Phase 3 style; no intra-plugin imports | |
|
|
| WinterCMS-style subpackages | models/, controllers/, console/, jobs/, updates/ as packages | ✓ (via /gsd-explore) |
|
|
| Flat Go, WinterCMS dirs for non-Go assets | | |
|
|
|
|
**User's choice:** Deferred to `/gsd-explore` ("This is a difficult one"), then decided: WinterCMS-style subpackages because agents converting golem15/jz/pxpx plugins lean on the PHP **directory**, not the class graph. Added rule: `models/` is a leaf package (user agreed "first option seems the best"), casts move in, service-calling hooks become callbacks from `classes/`/`plugin.go`, enforced by `summer build`. Evidence: Fonoteka has 40 `classes→models` edges and exactly 1 `models→classes` edge (a cast and a hook in `Album.php`). Recorded in `.planning/notes/plugin-layout-winter-directories.md`.
|
|
|
|
### Coexistence with hand-written plugins
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Opt-in, merged by plugin.go | Hand-written plugins call generated accessors; fonoteka.go untouched | ✓ |
|
|
| Migrate existing plugins in this phase | Touches fonoteka.go, out of phase scope | |
|
|
| You decide | | |
|
|
|
|
### Argument shape
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| `make:model <vendor.plugin> <Name>` | Plugin ID inferred from plugin.go when inside a plugin dir | ✓ |
|
|
| Always explicit, no inference | | |
|
|
| You decide | | |
|
|
|
|
---
|
|
|
|
## Mail drivers & test send
|
|
|
|
### Drivers
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| smtp + log + memory | go-mail, logger, in-memory slice | ✓ |
|
|
| smtp + memory only | | |
|
|
| smtp only | | |
|
|
|
|
### Test send
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Unit test + SMTP integration test | memory driver unit test plus Mailpit container test gated like testcontainers Postgres | ✓ |
|
|
| Unit test with memory driver only | | |
|
|
| `summer mail:test` command | Manual verification | |
|
|
|
|
### Send API
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Mailer service on the container | `postcard.Mailer.Send(ctx, Message{Template, To, Vars})` | ✓ |
|
|
| Typed message per template | | |
|
|
| You decide | | |
|
|
|
|
### Failures
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Error returned, unknown template fails at boot | Consistent with Phase 1 D-10 | ✓ |
|
|
| Error returned, lazy template check | | |
|
|
| You decide | | |
|
|
|
|
---
|
|
|
|
## Claude's Discretion
|
|
|
|
- phrasebook API surface, aliases/overrides, request-context seam for Phase 7
|
|
- Markdown renderer pick (research) and escaping of substituted values
|
|
- registry.gen.go naming and accessor names; how the models-leaf check locates packages
|
|
- What make:plugin scaffolds beyond Phase 1's minimum in the new layout
|
|
- make:command naming under the plugin's colon prefix
|
|
- Mailpit image and API access
|
|
- Plan count and split
|
|
|
|
## Deferred Ideas
|
|
|
|
- Restructure Phase 3 fonoteka.go plugins into the directory layout — Phase 5
|
|
- Validate the models-leaf rule on keios.eu / jz / pxpx plugins — todo `verify-models-leaf-rule`
|
|
- `summer mail:test` production smoke command
|
|
- Attachments on `postcard.Message`
|