docs(14.2.1): create phase plan
This commit is contained in:
242
.planning/phases/14.2.1-translate-plugin/14.2.1-03-PLAN.md
Normal file
242
.planning/phases/14.2.1-translate-plugin/14.2.1-03-PLAN.md
Normal file
@@ -0,0 +1,242 @@
|
||||
---
|
||||
phase: 14.2.1-translate-plugin
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on: ["14.2.1-02"]
|
||||
files_modified:
|
||||
- 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
|
||||
autonomous: false
|
||||
requirements: [D-06, D-09, D-12, D-13, D-14, D-15, D-16, D-17]
|
||||
must_haves:
|
||||
truths:
|
||||
- "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."
|
||||
artifacts:
|
||||
- path: "modules/cabana/field_ml.go"
|
||||
provides: "ML nested-value lift and TranslationWriter bridge"
|
||||
contains: "TranslationWriter"
|
||||
- path: "admin/src/components/form/fields/MLMarkdownField.vue"
|
||||
provides: "locale-aware markdown source editor"
|
||||
contains: "modelValue"
|
||||
- path: "docs/backend/forms.md"
|
||||
provides: "verified field-type documentation"
|
||||
contains: "mlmarkdown"
|
||||
- path: "../sm-grzybyfunkcjonalne-app/summer.yaml"
|
||||
provides: "compiled user+translate plugin list"
|
||||
contains: "golem15.translate"
|
||||
- path: "../sm-grzybyfunkcjonalne-app/boot_test.go"
|
||||
provides: "proof-host boot smoke"
|
||||
contains: "TestBootUserTranslate"
|
||||
key_links:
|
||||
- from: "modules/cabana/crud.go"
|
||||
to: "modules/cabana/field_ml.go"
|
||||
via: "ML values are lifted before ProjectWritableFields"
|
||||
pattern: "liftMLValues"
|
||||
- from: "../sm-translate-plugin/classes/admin_writer.go"
|
||||
to: "../sm-translate-plugin/classes/translatable.go"
|
||||
via: "published cabana TranslationWriter delegates each locale to SetTranslated"
|
||||
pattern: "SetTranslated"
|
||||
- from: "../sm-grzybyfunkcjonalne-app/summer.yaml"
|
||||
to: "../sm-grzybyfunkcjonalne-app/plugins.gen.go"
|
||||
via: "summer build generates blank imports for both submodules"
|
||||
pattern: "sm-translate-plugin"
|
||||
---
|
||||
|
||||
<objective>
|
||||
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.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@~/.codex/gsd-core/workflows/execute-plan.md
|
||||
@~/.codex/gsd-core/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<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
|
||||
</context>
|
||||
|
||||
## 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`.
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="tracer">
|
||||
<name>Task 1: Submit one mltext map through cabana and persist default plus Polish values</name>
|
||||
<files>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</files>
|
||||
<read_first>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</read_first>
|
||||
<action>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.</action>
|
||||
<verify>
|
||||
<automated>go test ./modules/cabana -count=1 -v -run '^(TestMLNestedSaveSmoke)$' && npm --prefix admin test -- --run admin/tests/form/MLFields.test.ts</automated>
|
||||
<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>
|
||||
</verify>
|
||||
<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>
|
||||
<done>A real cabana save carries one multilingual title from Vue-shaped JSON through projection and transactional persistence without dropping or broadening the write.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:decision" gate="blocking-human">
|
||||
<name>Task 2: Confirm the one-way proof-host submodule layout</name>
|
||||
<files>../sm-grzybyfunkcjonalne-app/.gitmodules, ../sm-grzybyfunkcjonalne-app/go.work, ../sm-grzybyfunkcjonalne-app/go.mod, ../sm-grzybyfunkcjonalne-app/summer.yaml</files>
|
||||
<read_first>.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</read_first>
|
||||
<action>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.</action>
|
||||
<decision>Which durable proof-host layout should receive the translate gitlink?</decision>
|
||||
<context>Changing submodule paths after downstream clones and Phase 15 Journal mounts requires coordinated gitlink, workspace, replace, and deployment changes.</context>
|
||||
<options>
|
||||
<option id="grzyby-submodules">
|
||||
<name>Use sm-grzybyfunkcjonalne-app with plugins/golem15/user and plugins/golem15/translate</name>
|
||||
<pros>Matches locked D-13/D-16 and the established sm-bm-app pattern.</pros>
|
||||
<cons>The submodule paths become durable host layout.</cons>
|
||||
</option>
|
||||
<option id="fonoteka-host">
|
||||
<name>Use fonoteka.go as the proof host</name>
|
||||
<pros>Already has a large application harness.</pros>
|
||||
<cons>Contradicts D-13 and couples the shared plugin to the wrong app.</cons>
|
||||
</option>
|
||||
<option id="vendored-copy">
|
||||
<name>Copy plugin source into the host</name>
|
||||
<pros>No gitlinks.</pros>
|
||||
<cons>Contradicts compiled core-plugin repository ownership and creates a fork.</cons>
|
||||
</option>
|
||||
</options>
|
||||
<verify>
|
||||
<automated>git -C ../sm-grzybyfunkcjonalne-app rev-parse --is-inside-work-tree && git -C ../sm-grzybyfunkcjonalne-app remote get-url origin</automated>
|
||||
<fails_when>Non-zero exit or origin is not the existing sm-grzybyfunkcjonalne-app remote.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- The selection is recorded in the summary.
|
||||
- Task 3 proceeds only with `grzyby-submodules`; another selection stops as a D-13/D-16 conflict.
|
||||
</acceptance_criteria>
|
||||
<resume-signal>Select `grzyby-submodules` to implement the locked host, or an alternative to stop.</resume-signal>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Complete docs/OpenAPI/TS/dist and boot the user+translate proof host</name>
|
||||
<reversibility rating="one-way">The committed user/translate gitlink paths become the Phase 15 host layout; moving them later requires coordinated submodule and workspace migration.</reversibility>
|
||||
<precondition>Task 2 selected `grzyby-submodules`, and the Plan 02 translate commit is reachable from its Gitea remote.</precondition>
|
||||
<files>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</files>
|
||||
<read_first>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)</read_first>
|
||||
<action>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.</action>
|
||||
<verify>
|
||||
<automated>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)$'</automated>
|
||||
<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>
|
||||
</verify>
|
||||
<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>
|
||||
<done>The framework ships typed multilingual controls and the intended host boots user+translate from durable submodules with generated admin artifacts in sync.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<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>
|
||||
|
||||
<verification>
|
||||
Run the Task 3 combined gate. Confirm `git status --short` in all three repositories contains only intended tracked source/generated artifacts and gitlink updates.
|
||||
</verification>
|
||||
|
||||
<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>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/14.2.1-translate-plugin/14.2.1-03-SUMMARY.md` when done.
|
||||
</output>
|
||||
Reference in New Issue
Block a user