Files
summercms/.planning/phases/14.2.1-translate-plugin/14.2.1-03-PLAN.md
2026-10-06 11:22:45 +02:00

19 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
14.2.1-translate-plugin 03 execute 3
14.2.1-02
modules/cabana/form_schema.go
modules/cabana/crud.go
modules/cabana/field_ml.go
modules/cabana/field_markdown.go
modules/cabana/ml_smoke_test.go
modules/cabana/README.md
docs/backend/forms.md
docs/backend/admin-controllers.md
admin/src/components/form/registry.ts
admin/src/components/form/formState.ts
admin/src/components/form/fields/MarkdownField.vue
admin/src/components/form/fields/MLTextField.vue
admin/src/components/form/fields/MLMarkdownField.vue
admin/tests/form/registry.test.ts
admin/tests/form/MLFields.test.ts
admin/openapi/admin.json
admin/src/api/schema.d.ts
modules/boardwalk/dist/
../sm-translate-plugin/classes/admin_writer.go
../sm-translate-plugin/plugin.go
../sm-grzybyfunkcjonalne-app/go.mod
../sm-grzybyfunkcjonalne-app/go.work
../sm-grzybyfunkcjonalne-app/summer.yaml
../sm-grzybyfunkcjonalne-app/.gitmodules
../sm-grzybyfunkcjonalne-app/main.go
../sm-grzybyfunkcjonalne-app/plugins.gen.go
../sm-grzybyfunkcjonalne-app/boot_test.go
false
D-06
D-09
D-12
D-13
D-14
D-15
D-16
D-17
truths artifacts key_links
D-06: cabana accepts `markdown`, `mltext`, and `mlmarkdown`; `mlmarkdown` composes the same markdown primitive with locale switching.
Nested locale maps are lifted before `ProjectWritableFields` drops maps; default locale fills the host field and non-default locales reach the translate writer inside the host save transaction.
The SPA exposes one locale selector per ML field, switches all ML controls together, supports copy-from-locale, and sends every locale as `Record<string,string>`.
Markdown preview uses goldmark without unsafe HTML and cannot execute translated raw HTML/script/event-handler/javascript content.
Cabana README, forms/admin docs, OpenAPI, generated TS types, and committed `modules/boardwalk/dist/` are regenerated in the same change.
D-13/D-14/D-15/D-16/D-17: `sm-grzybyfunkcjonalne-app` mounts user and translate as submodules, uses go.work/local replaces, and boots both compiled plugins.
path provides contains
modules/cabana/field_ml.go ML nested-value lift and TranslationWriter bridge TranslationWriter
path provides contains
admin/src/components/form/fields/MLMarkdownField.vue locale-aware markdown source editor modelValue
path provides contains
docs/backend/forms.md verified field-type documentation mlmarkdown
path provides contains
../sm-grzybyfunkcjonalne-app/summer.yaml compiled user+translate plugin list golem15.translate
path provides contains
../sm-grzybyfunkcjonalne-app/boot_test.go proof-host boot smoke TestBootUserTranslate
from to via pattern
modules/cabana/crud.go modules/cabana/field_ml.go ML values are lifted before ProjectWritableFields liftMLValues
from to via pattern
../sm-translate-plugin/classes/admin_writer.go ../sm-translate-plugin/classes/translatable.go published cabana TranslationWriter delegates each locale to SetTranslated SetTranslated
from to via pattern
../sm-grzybyfunkcjonalne-app/summer.yaml ../sm-grzybyfunkcjonalne-app/plugins.gen.go summer build generates blank imports for both submodules sm-translate-plugin
Add cabana's markdown and multilingual fields end to end, preserve nested locale writes, regenerate every public/generated artifact, and boot the user+translate proof host.

Purpose: prove a Journal-shaped form can submit localized text/markdown through the generic admin stack without a Node extension or dropped nested JSON. Output: framework schema/save/UI/docs/OpenAPI/dist changes, translate writer adapter, and the D-13 proof-host layout.

<execution_context> @/.codex/gsd-core/workflows/execute-plan.md @/.codex/gsd-core/templates/summary.md </execution_context>

@.planning/phases/14.2.1-translate-plugin/14.2.1-02-SUMMARY.md @.planning/phases/14.2.1-translate-plugin/14.2.1-CONTEXT.md @.planning/phases/14.2.1-translate-plugin/14.2.1-RESEARCH.md @.planning/phases/14.2.1-translate-plugin/14.2.1-PATTERNS.md @modules/cabana/form_schema.go @modules/cabana/crud.go @modules/cabana/field_permission.go @admin/src/components/form/registry.ts @admin/src/components/form/formState.ts @../sm-bm-app/summer.yaml @../sm-bm-app/go.work

Spec-less probe fallback

The phase has no mapped requirement IDs, so no speculative requirement probes are generated. D-06/D-09/D-12/D-13/D-14/D-15/D-16/D-17 and RESEARCH's cabana/host validation rows are the acceptance source.

Artifacts this phase produces

  • Cabana field types markdown, mltext, mlmarkdown, TranslationWriter, nested-map lifting, and safe RenderMarkdown.
  • Vue MarkdownField, MLTextField, MLMarkdownField, synchronized selector/copy behavior, and registry/form-state support.
  • Updated cabana README and backend docs; regenerated admin/openapi/admin.json, admin/src/api/schema.d.ts, and modules/boardwalk/dist/.
  • Translate plugin's cabana writer adapter published at Boot.
  • Proof host source layout with user and translate submodules, local workspace/replaces, generated plugin list, and TestBootUserTranslate.
Task 1: Submit one mltext map through cabana and persist default plus Polish values modules/cabana/form_schema.go, modules/cabana/crud.go, modules/cabana/field_ml.go, modules/cabana/field_markdown.go, modules/cabana/ml_smoke_test.go, admin/src/components/form/registry.ts, admin/src/components/form/formState.ts, admin/src/components/form/fields/MarkdownField.vue, admin/src/components/form/fields/MLTextField.vue, admin/src/components/form/fields/MLMarkdownField.vue, admin/tests/form/MLFields.test.ts, ../sm-translate-plugin/classes/admin_writer.go, ../sm-translate-plugin/plugin.go modules/cabana/form_schema.go (formFieldTypes and compileFieldNode), modules/cabana/crud.go (save, ProjectWritableFields, BindWritableFields, scalarFormField), modules/cabana/field_permission.go (liftPermissionValues), modules/postcard/templates.go (goldmark safe path), admin/src/components/form/registry.ts, admin/src/components/form/formState.ts, admin/src/components/form/fields/TextField.vue, admin/src/components/form/fields/TextareaField.vue, /media/nvme/dev/golem15/fonoteka/plugins/golem15/translate/traits/MLControl.php, /media/nvme/dev/golem15/fonoteka/plugins/golem15/translate/traits/mlcontrol/partials/_locale_selector.htm, ../sm-translate-plugin/classes/translatable.go Register exactly `markdown`, `mltext`, and `mlmarkdown` as fail-loud schema types without adding unnecessary YAML keys. Define a framework-owned cabana `TranslationWriter` service contract so cabana never imports the translate plugin; implement and publish its adapter from sm-translate-plugin.

In CRUD save, identify declared ML fields and lift each map[locale]text before ProjectWritableFields evaluates nested values. Reject non-string values, undeclared locales, and extra field names with validation errors. Fill only the default-locale scalar into the host model, then call the writer for non-default values inside the same lagoon/cabana transaction and after controller permission/query scoping has succeeded. Never expose a public translate-write endpoint.

Implement RenderMarkdown using the already-pinned goldmark without unsafe HTML; reject unsafe output patterns consistently with postcard. MarkdownField edits source and may preview only sanitized output. Build ML components around a Record<string,string> value: active locale editor, selector, copy-from-locale, and a shared form event so changing one selector changes all ML controls. mlmarkdown composes MarkdownField, not a duplicate parser/editor. Ensure formState sends nested ML records.

Create one focused Go smoke fixture using the Plan 02 writer adapter and a cabana save body {title:{en,pl}}. It must prove the nested map survives projection, English reaches the host field, and Polish reaches attribute JSON. Add a focused SPA smoke asserting registry resolution and emitted nested values; full edge/coverage tests remain Plan 04. go test ./modules/cabana -count=1 -v -run '^(TestMLNestedSaveSmoke)$' && npm --prefix admin test -- --run admin/tests/form/MLFields.test.ts <fails_when>Non-zero exit; Go output contains "--- FAIL", "--- SKIP", or "no tests to run", lacks "--- PASS: TestMLNestedSaveSmoke", or Vitest reports no test files/tests or any failed test.</fails_when> <acceptance_criteria> - A compiled schema accepts all three exact field types and still rejects unknown types. - ML map lifting occurs before ProjectWritableFields; generic nested maps remain rejected/dropped by existing rules. - Default locale fills the host scalar and is not duplicated in attributes; Polish persists through TranslationWriter. - Writer execution occurs only after host-controller authorization/scoping and inside the save transaction. - Raw script/iframe/event-handler/javascript markdown cannot become executable preview HTML. - SPA payload contains all locale keys, and mlmarkdown reuses MarkdownField. </acceptance_criteria> A real cabana save carries one multilingual title from Vue-shaped JSON through projection and transactional persistence without dropping or broadening the write.

Task 2: Confirm the one-way proof-host submodule layout ../sm-grzybyfunkcjonalne-app/.gitmodules, ../sm-grzybyfunkcjonalne-app/go.work, ../sm-grzybyfunkcjonalne-app/go.mod, ../sm-grzybyfunkcjonalne-app/summer.yaml .planning/phases/14.2.1-translate-plugin/14.2.1-CONTEXT.md (D-13 through D-17), .planning/phases/14.2.1-translate-plugin/14.2.1-PATTERNS.md (Proof host), ../sm-bm-app/.gitmodules, ../sm-bm-app/go.work, ../sm-bm-app/go.mod, ../sm-bm-app/summer.yaml Record the D-13 host layout before adding gitlinks. The selected contract is the existing `sm-grzybyfunkcjonalne-app` repository with `plugins/golem15/user` and `plugins/golem15/translate` submodules plus go.work and local replace entries, following sm-bm-app. Journal is deliberately absent until Phase 15. Which durable proof-host layout should receive the translate gitlink? Changing submodule paths after downstream clones and Phase 15 Journal mounts requires coordinated gitlink, workspace, replace, and deployment changes. Use sm-grzybyfunkcjonalne-app with plugins/golem15/user and plugins/golem15/translate Matches locked D-13/D-16 and the established sm-bm-app pattern. The submodule paths become durable host layout. Use fonoteka.go as the proof host Already has a large application harness. Contradicts D-13 and couples the shared plugin to the wrong app. Copy plugin source into the host No gitlinks. Contradicts compiled core-plugin repository ownership and creates a fork. git -C ../sm-grzybyfunkcjonalne-app rev-parse --is-inside-work-tree && git -C ../sm-grzybyfunkcjonalne-app remote get-url origin Non-zero exit or origin is not the existing sm-grzybyfunkcjonalne-app remote. - The selection is recorded in the summary. - Task 3 proceeds only with `grzyby-submodules`; another selection stops as a D-13/D-16 conflict. Select `grzyby-submodules` to implement the locked host, or an alternative to stop. Task 3: Complete docs/OpenAPI/TS/dist and boot the user+translate proof host The committed user/translate gitlink paths become the Phase 15 host layout; moving them later requires coordinated submodule and workspace migration. Task 2 selected `grzyby-submodules`, and the Plan 02 translate commit is reachable from its Gitea remote. modules/cabana/README.md, docs/backend/forms.md, docs/backend/admin-controllers.md, admin/tests/form/registry.test.ts, admin/tests/form/MLFields.test.ts, admin/openapi/admin.json, admin/src/api/schema.d.ts, modules/boardwalk/dist/, ../sm-grzybyfunkcjonalne-app/go.mod, ../sm-grzybyfunkcjonalne-app/go.work, ../sm-grzybyfunkcjonalne-app/summer.yaml, ../sm-grzybyfunkcjonalne-app/.gitmodules, ../sm-grzybyfunkcjonalne-app/main.go, ../sm-grzybyfunkcjonalne-app/plugins.gen.go, ../sm-grzybyfunkcjonalne-app/boot_test.go modules/cabana/README.md, docs/backend/forms.md, docs/backend/admin-controllers.md, modules/cabana/openapi_conformance_test.go, admin/package.json, admin/tests/form/registry.test.ts, ../sm-bm-app/go.mod, ../sm-bm-app/go.work, ../sm-bm-app/summer.yaml, ../sm-bm-app/.gitmodules, ../sm-bm-app/main.go, ../sm-bm-app/plugins.gen.go, .planning/phases/14.2.1-translate-plugin/14.2.1-RESEARCH.md (Docs/OpenAPI/TS/dist and Proof-host layout) Update cabana README and backend forms documentation for `markdown`, `mltext`, `mlmarkdown`, nested locale payloads, TranslationWriter, safe markdown, and fail-loud behavior. Update admin-controller docs only where activation/save semantics changed. All examples use neutral blog/acme names. Remove the stale claim that markdown is unavailable.

Regenerate admin OpenAPI from the established command, regenerate TypeScript schema with npm --prefix admin run gen:api, run the admin build, and copy the resulting Vite output to committed modules/boardwalk/dist/ using the existing boardwalk build workflow. Do not hand-edit generated JSON, d.ts, or dist assets. Extend registry tests for all three field types.

In the already-cloned D-14 host, use the sm-bm-app layout: module git.golem15.com/golem15/sm-grzybyfunkcjonalne-app, framework replace ../summercms.go, submodules at plugins/golem15/user and plugins/golem15/translate, workspace uses and host replaces for each, and summer.yaml entries for only golem15.user and golem15.translate. Add submodules from their Gitea remotes, never copy runtime mirrors. Generate main.go/plugins.gen.go with summer build. Add TestBootUserTranslate that activates the real two plugins and verifies the Locales admin controller is registered; the fixture save/read proof remains the plugin integration smoke from Plans 02/03 rather than a third production plugin. go test ./cmd/summer -count=1 -run 'TestDocsTree' && go run ./cmd/summer docs:build --check && npm --prefix admin run typecheck && npm --prefix admin test -- --run admin/tests/form/registry.test.ts admin/tests/form/MLFields.test.ts && npm --prefix admin run build && go -C ../sm-grzybyfunkcjonalne-app vet ./... && go -C ../sm-grzybyfunkcjonalne-app test ./... -count=1 -v -run '^(TestBootUserTranslate)$' <fails_when>Non-zero exit; docs checker reports stale identifiers/links/forbidden names; Vitest reports no tests or failures; build omits index.html/assets; host output contains "--- FAIL", "--- SKIP", or "no tests to run", or lacks "--- PASS: TestBootUserTranslate".</fails_when> <acceptance_criteria> - README/docs identifiers resolve and contain no consuming-application name. - OpenAPI, schema.d.ts, and boardwalk dist are regenerated and tracked; no generated file is hand-edited. - Registry resolves markdown/mltext/mlmarkdown and unknown ml types still fail boot. - Host .gitmodules, go.work, go.mod, and summer.yaml use exact D-16 paths/module IDs. - Generated plugin list imports sm-user-plugin and sm-translate-plugin; Journal is absent. - Host smoke activates both plugins and sees the Locales admin controller. </acceptance_criteria> The framework ships typed multilingual controls and the intended host boots user+translate from durable submodules with generated admin artifacts in sync.

<threat_model>

Trust Boundaries

Boundary Description
Admin browser → cabana CRUD Untrusted nested multilingual JSON crosses authenticated controller boundary
Translated markdown → preview DOM Stored content can carry active HTML payloads
Cabana → plugin TranslationWriter Framework delegates scoped writes to an optional plugin service
Host gitlinks → compiled binary Remote plugin commits become trusted build inputs

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-14.2.1-09 Tampering ML nested request high mitigate Lift only declared ML fields; require string locale map; validate locales; execute after host authorization
T-14.2.1-10 Elevation of Privilege TranslationWriter high mitigate No standalone endpoint; writer runs inside permissioned/scoped host save transaction
T-14.2.1-11 Tampering mass assignment high mitigate Default scalar only enters writable host field; locale map never reaches generic Fill
T-14.2.1-12 Tampering markdown preview high mitigate Goldmark safe mode plus unsafe output rejection and CSP-compatible rendering
T-14.2.1-13 Tampering host submodule provenance medium mitigate Exact Gitea remotes and committed gitlinks; generated plugin imports
T-14.2.1-SC Tampering npm packages high mitigate No package installation or version change; existing exact pins only

ASVS L1: all high threats are mitigated; Plan 04 adds fail-when-broken evidence. </threat_model>

Run the Task 3 combined gate. Confirm `git status --short` in all three repositories contains only intended tracked source/generated artifacts and gitlink updates.

<success_criteria>

  • ML nested saves persist both locales without widening mass assignment.
  • Markdown and multilingual controls compile, test, and appear in generated API/types/dist.
  • Docs checks pass with application-neutral examples.
  • Proof host boots real user+translate plugins and exposes Locales admin. </success_criteria>
Create `.planning/phases/14.2.1-translate-plugin/14.2.1-03-SUMMARY.md` when done.