diff --git a/.planning/phases/14.2.1-translate-plugin/14.2.1-CONTEXT.md b/.planning/phases/14.2.1-translate-plugin/14.2.1-CONTEXT.md new file mode 100644 index 0000000..10c4fba --- /dev/null +++ b/.planning/phases/14.2.1-translate-plugin/14.2.1-CONTEXT.md @@ -0,0 +1,142 @@ +# Phase 14.2.1: Translate plugin - Context + +**Gathered:** 2026-10-06 +**Status:** Ready for planning + + +## Phase Boundary + +`Golem15.Translate` is ported to Go as `sm-translate-plugin` so Journal (Phase 15) and later blogs can keep translatable fields. This phase ships the lean core Journal needs: the Locale model and its admin screen, the TranslatableModel behavior, cabana ML field types, and PHP's request-locale resolution. + +The proof host is `sm-grzybyfunkcjonalne-app`, booted with `sm-user-plugin` + `sm-translate-plugin` (Journal mounts in Phase 15). Proof: the app boots, Locales admin works, and a fixture model can save and read `en`+`pl` through ML fields and `WithLocale`. + +`summercms.go` is touched for the ML field types (`mltext`, `mlmarkdown`) and any Translator/request-locale seam the plugin cannot own alone. + +Out of scope: the Messages catalogue and `manage_messages` admin, locale-picker / hreflang / suggestion-banner CMS components, AI and theme console commands, message import/export, and any edit to `wn-translate-plugin`. Those wait for a later translate follow-up if a site needs them. + + + + +## Implementation Decisions + +### PHP source of truth +- **D-01:** The port contract is `github.com/golem15com/wn-translate-plugin` at git SHA `725d547` (2026-08-26), the pin currently checked out in Płytarium. That tree is newer than grzybyfunkcjonalne.pl (`6e0c146`). — **Reversibility:** costly — Locale tables, attribute storage and Translator behaviour are copied from this tree. +- **D-02:** Freeze at `725d547`. Planner and researcher must not silently pick a newer `master`. — **Reversibility:** reversible — a later discussion can re-pin. +- **D-03:** Downstream agents read the frozen source from `/media/nvme/dev/golem15/fonoteka/plugins/golem15/translate` (already at that SHA). Do not clone a second copy unless that path is missing or the SHA does not match. +- **D-04:** This phase does not edit or PR `wn-translate-plugin`. An urgent PHP upgrade on a live Winter site before that site migrates is allowed as operations. If it lands before plan or execute, re-pin the SHA rather than mixing trees. + +### What ships this phase +- **D-05:** Lean only: Locale model + Locales admin (`golem15.translate.manage_locales`), and a TranslatableModel behavior Journal can implement. Messages catalogue, locale-picker components, AI/theme commands and message import/export are deferred. — **Reversibility:** reversible — later plans can add the rest without rewriting Locale. +- **D-06:** Cabana gains ML field types (`mltext`, `mlmarkdown`) matching PHP's `ml*` widgets, so a Journal form stays one screen with a locale switch. Phase 15's markdown field (15 D-11) and these ML types must compose (`mlmarkdown` is markdown plus locale). — **Reversibility:** costly — new field types in the typed admin schema and generated TS types. +- **D-07:** Port PHP `Translator` locale resolution: URL prefix / session / cookie / default. Do not reduce this phase to `Accept-Language` or a query param only. — **Reversibility:** costly — public blog URLs and the Journal API will depend on this order. +- **D-08:** The plugin seeds English and Polish locales, matching Journal 15 D-06. + +### Model API +- **D-09:** A Go model declares translatable attributes with a `Translatable() []string` (or equivalent tagged list) the way PHP uses `$translatable`. Journal copies the PHP Post/Category lists. — **Reversibility:** costly — every translatable model in shared plugins will call this. +- **D-10:** Storage uses the same tables and columns as this PHP pin (`golem15_translate_*` attributes/indexes/locales — researcher confirms exact names from the frozen updates). No JSON-column alternative. — **Reversibility:** one-way — Winter import and Journal cutover expect those tables. +- **D-11:** A missing translation falls back to the default locale, as PHP does. +- **D-12:** The exported API other plugins call this phase is the minimum: `WithLocale` on queries, get/set an attribute in a locale, and current locale from Translator. Do not 1:1 every PHP `TranslatableModel` / `Translator` method name unless research shows Journal needs it. + +### Where it boots +- **D-13:** The host is `sm-grzybyfunkcjonalne-app` (same as Phase 15 D-18). This phase mounts `sm-user-plugin` + `sm-translate-plugin`; Journal is added in Phase 15. — **Reversibility:** one-way — submodule layout lands in that app. +- **D-14:** Local app checkout: `/media/nvme/dev/golem15/summercms.io/summercms/sm-grzybyfunkcjonalne-app/` (sibling of `summercms.go`). +- **D-15:** The user creates the public Gitea remote `git@git.golem15.com:golem15/sm-translate-plugin.git` as a manual plan step (it does not exist yet). Module path `git.golem15.com/golem15/sm-translate-plugin`, package `translate`, plugin ID `golem15.translate`. +- **D-16:** Local plugin checkout: `/media/nvme/dev/golem15/summercms.io/summercms/sm-translate-plugin/` (sibling of `summercms.go`). The host mounts it as a submodule at `plugins/golem15/translate` with a `go.work` replace. +- **D-17:** Phase proof: the app boots with user+translate; Locales admin works; a fixture model can save and read `en`+`pl` through ML fields and `WithLocale`. + +### Claude's Discretion +- Exact `golem15_translate_*` table/column names and migration IDs, taken from the frozen PHP updates (D-10), not invented. +- ML field YAML type names and SPA locale-switcher chrome, provided they follow cabana fail-loud rules and update the module README, `docs/`, admin OpenAPI, generated TS types and committed `dist/` in the same change. +- How `mlmarkdown` shares implementation with Phase 15's markdown field (land the shared primitive here if Journal is otherwise blocked). +- Plan count and split, subject to the plan-count checkpoint and "unit tests are the last plan". +- Run the security-review agent: locale switching on public requests and admin ML writes are authorization-adjacent. + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +### Roadmap and prior decisions +- `.planning/ROADMAP.md` § Phase 14.2.1 — stub goal; this file is the real scope +- `.planning/ROADMAP.md` § Phase 15 — Journal depends on this plugin +- `.planning/phases/15-journal-plugin/15-CONTEXT.md` — D-09/D-10 (Journal requires translate; translatable title/slug/content), D-06 (`en`+`pl`), D-11 (markdown field), D-18–D-21 (grzyby host) +- `.planning/notes/core-plugins-own-repos.md` — `sm--plugin` layout, module path, submodule mount +- `.planning/phases/12.1-user-plugin-admin-screens/12.1-CONTEXT.md` — YAML admin screens, framework-first field types +- `.planning/phases/04-cli-scaffolding-i18n-and-mail/04-CONTEXT.md` — phrasebook; this plugin is model-level translation, not UI strings +- `.planning/phases/09-backend-admin-authentication-and-schema-pipeline/09-CONTEXT.md` — typed schema, fail-loud YAML + +### PHP contract (read-only) — `/media/nvme/dev/golem15/fonoteka/plugins/golem15/translate` at SHA `725d547` +- `Plugin.php` — permissions (`manage_locales`, `manage_messages`), navigation, components (components are out of scope) +- `models/Locale.php`, `models/Attribute.php`, `models/Message.php` (Message is out of scope except as a table that must not be broken) +- `behaviors/` — TranslatableModel (or equivalent) and how `$translatable` is applied +- `classes/Translator.php` — URL prefix / session / cookie / default (D-07) +- `controllers/Locales.php` and YAML under `controllers/locales/`, `models/locale/` +- `formwidgets/` — PHP `ml*` widgets that D-06 replaces +- `updates/` — Locale / attributes / indexes table names and migration IDs (D-10) +- `README.md`, `UPGRADE.md` +- `tests/` — use as landmines; lean scope means not every PHP test maps 1:1 + +### Go references +- `../fonoteka.go/plugins/golem15/user/` — mount pattern for the sibling plugin +- `modules/cabana/` — field registry; `mltext` / `mlmarkdown` land here +- `modules/phrasebook/` — UI strings; do not conflate with model translations +- `docs/backend/forms.md`, `docs/backend/admin-controllers.md` + +### Remotes +- `git@git.golem15.com:golem15/sm-translate-plugin.git` — **does not exist yet**; user creates it, public +- `git@git.golem15.com:golem15/sm-grzybyfunkcjonalne-app.git` — empty proof host, already created +- `git@git.golem15.com:golem15/sm-journal-plugin.git` — public, empty; mounted in Phase 15 + + + + +## Existing Code Insights + +### Reusable Assets +- `sm-user-plugin` mount pattern: `summer.yaml`, submodule at `plugins/golem15/user`, `go.work` replace, `pact.HasAdminControllers`. +- cabana field registry (Phase 12.1 `permissioneditor`, Phase 12.2 `fileupload` / `datepicker`): the pattern for `mltext` / `mlmarkdown`. +- phrasebook `en`/`pl` for plugin UI strings — separate from Locale rows and attribute translations. +- `sm-grzybyfunkcjonalne-app` remote already exists; local directory may not. + +### Established Patterns +- Shared plugins: module `git.golem15.com/golem15/sm--plugin`, package plain (`translate`), plugin ID `golem15.translate`. +- Unknown YAML keys fail boot. Framework field types update README, `docs/`, OpenAPI, TS types and `boardwalk` `dist/` in the same change. +- PHP originals are not changed. Migrations are append-only. Unit tests are the last plan. + +### Integration Points +- New repo `sm-translate-plugin`: Locale model, updates, Locales admin, Translatable behavior, Translator. +- `summercms.go` cabana + admin SPA: `mltext`, `mlmarkdown`, locale switcher on forms. +- `sm-grzybyfunkcjonalne-app`: first mount of user + translate. +- Phase 15 Journal: `Requires` translate; Post/Category `Translatable()` lists; `mltext`/`mlmarkdown` on YAML forms. + + + + +## Specific Ideas + +- Same freeze/read-only/re-pin rules as Journal, applied to Translate. +- "Start fast" on `sm-grzybyfunkcjonalne-app` still holds; this phase boots that app without Journal. +- Messages, CMS locale components and AI/theme commands were explicitly left out so Journal is not blocked. + + + + +## Deferred Ideas + +- Messages catalogue and `golem15.translate.manage_messages` admin. +- Locale picker, alternate hreflang, and locale-suggestion-banner CMS components (Phase 16 views may need a successor). +- AI/theme console commands and message import/export. +- Remaining PHP locales beyond the `en`+`pl` seed (D-08 is seed rows, not phrasebook files). +- Phase 15 should list **Depends on: Phase 14.2.1** in ROADMAP (`$gsd-phase --edit 15`); not done in this discussion. + +### Reviewed Todos (not folded) +- `sitemap-plugin-port.md`, `mailblocker-with-mailing.md`, and the other keyword matches from `todo.match-phase 14.2.1`: unrelated housekeeping. + + + +--- + +*Phase: 14.2.1-translate-plugin* +*Context gathered: 2026-10-06* diff --git a/.planning/phases/14.2.1-translate-plugin/14.2.1-DISCUSSION-LOG.md b/.planning/phases/14.2.1-translate-plugin/14.2.1-DISCUSSION-LOG.md new file mode 100644 index 0000000..f8c2f18 --- /dev/null +++ b/.planning/phases/14.2.1-translate-plugin/14.2.1-DISCUSSION-LOG.md @@ -0,0 +1,78 @@ +# Phase 14.2.1: Translate plugin - 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-10-06 +**Phase:** 14.2.1-translate-plugin +**Areas discussed:** PHP source of truth, what ships, model API, where it boots + +--- + +## PHP source of truth + +| Option | Description | Selected | +|--------|-------------|----------| +| Fonoteka pin `725d547` | Newest Płytarium checkout | ✓ | +| grzyby pin `6e0c146` | Older ancestor | | +| GitHub master at plan time | Moving target | | + +**User's choice:** Freeze at `725d547`, read from the Fonoteka path. Read-only with the same urgent-upgrade re-pin rule as Journal. + +--- + +## What ships + +| Option | Description | Selected | +|--------|-------------|----------| +| Lean: Locale + TranslatableModel | Unblocks Journal | ✓ | +| Lean plus Messages catalogue | Extra admin | | +| Full PHP surface | Picker, AI/theme, import/export | | + +**User's choice:** Lean. Cabana `mltext`/`mlmarkdown`. PHP Translator resolution (URL prefix / session / cookie / default). Seed `en`+`pl`. + +--- + +## Model API + +| Option | Description | Selected | +|--------|-------------|----------| +| `Translatable() []string` like PHP `$translatable` | Journal copies the PHP list | ✓ | +| YAML-only; models unaware | | | +| Same Winter attribute tables | Cutover-compatible | ✓ | +| JSON column per field | Breaks Winter import | | +| Fallback to default locale | PHP behaviour | ✓ | +| Minimum API (`WithLocale`, get/set, Translator) | Enough for Journal | ✓ | +| 1:1 every PHP method | | | + +**User's choice:** PHP-like declaration, same tables, PHP fallback, minimum exported API. + +--- + +## Where it boots + +| Option | Description | Selected | +|--------|-------------|----------| +| Same `sm-grzybyfunkcjonalne-app` | Matches Phase 15 D-21 | ✓ | +| In-module testhost | | | +| User creates `sm-translate-plugin` remote | Does not exist yet | ✓ | +| Sibling checkout | `summercms/sm-translate-plugin/` | ✓ | +| Proof: Locales admin + fixture en+pl | | ✓ | +| Plugin tests only | | | + +**User's choice:** Grzyby host with user+translate. User creates the public remote. Proof is boot + Locales admin + fixture model. + +--- + +## Claude's Discretion + +- Exact table/column names from frozen PHP updates +- ML field identifiers and docs/OpenAPI/`dist/` bundling +- How `mlmarkdown` shares work with Phase 15 markdown +- Plan count and split +- Security-review agent + +## Deferred Ideas + +- Messages admin, CMS locale components, AI/theme commands +- ROADMAP Phase 15 depends_on should include 14.2.1 (`$gsd-phase --edit 15`)