--- phase: 14.2.1-translate-plugin plan: 06 subsystem: i18n tags: [translate, cabana, mltext, mlmarkdown, fillChild, RelationService, WR-02, hydrateMLRecord] requires: - phase: 14.2.1-05 provides: "hydrateMLRecord, TranslatedExact, FORM_ENABLED_LOCALES, TestMLHydration" provides: - "RelationService.writer Lookup matching CRUDService" - "CreateChild/UpdateChild liftMLValues before fillChild and applyMLTranslations after PK" - "ShowChild/CreateChild/UpdateChild hydrateMLRecord on child records" - "TestRelationChildMLNestedSave plus phase gate FRAMEWORK_REQUIRE/HIGH_THREATS T-14.2.1-19..23" affects: [15-journal-plugin] actuals: tokens: 9023 tasks: 3 commits: 3 plan_head_before: f4bea33faf55b6f52faa021ccb51592bcafd20fb plan_head_after: ef0301631448e7e287c4f68ea3f16eb7ae2004c1 plugin_repo_head_after: b3e0e26069af6a1c05cccdf2aae045fec6d0346d host_repo_head_after: 1860ad68c5d1de1c077c8accd17bba0cd94e40a9 tech-stack: added: [] patterns: - "Relation-child create/update lift nested ML maps before fillChild, apply after child PK, then hydrate like CRUDService.save" - "RelationService.writer is unexported Lookup[TranslationWriter]; cabana never imports sm-translate-plugin" key-files: created: - modules/cabana/relation_child_ml_test.go modified: - modules/cabana/relation.go - modules/cabana/relation_child.go - modules/cabana/http.go - modules/cabana/ml_smoke_test.go - modules/cabana/README.md - docs/backend/forms.md - admin/tests/form/MLFields.test.ts - admin/tests/form/MarkdownField.test.ts - scripts/check-phase14.2.1.sh - .planning/phases/14.2.1-translate-plugin/14.2.1-SECURITY-REVIEW.md key-decisions: - "RelationService.writer stays unexported; relations() Lookup matches crud(); no new HTTP route" - "CreateChild/UpdateChild call liftMLValues on cr.child before fillChild and applyMLTranslations after PK; ShowChild hydrates with op update" patterns-established: - "Child records reuse CRUD ML helpers (liftMLValues, applyMLTranslations, hydrateMLRecord) inside the existing child transaction" - "Host gitlink must implement cabana.TranslationWriter including TranslatedExact; plugin go.mod must not carry a sibling-relative sm-user-plugin replace" requirements-completed: [D-06, D-17] coverage: - id: D1 description: "CreateChild lifts nested mltext maps before fillChild, writes default locale on the host column and Polish through TranslationWriter after PK, hydrates the create response, and rejects undeclared locale de with 422 and no row" requirement: D-17 verification: - kind: unit ref: "modules/cabana#TestRelationChildMLNestedSave" status: pass human_judgment: false - id: D2 description: "UpdateChild persists nested ML maps the same way as CreateChild; ShowChild returns hydrated locale maps for declared ML fields" requirement: D-06 verification: - kind: unit ref: "modules/cabana#TestRelationChildMLNestedSave" status: pass - kind: other ref: "go test ./cmd/summer -run TestDocsTree && go run ./cmd/summer docs:build --check" status: pass human_judgment: false - id: D3 description: "Phase gate requires TestMLHydration and TestRelationChildMLNestedSave; HIGH_THREATS include T-14.2.1-19,22,23; ALL_THREATS also 20,21; SPA sends empty pl and one selector per ML field" requirement: D-06 verification: - kind: unit ref: "modules/cabana#TestMLHydration" status: pass - kind: unit ref: "modules/cabana#TestMLNestedSave" status: pass - kind: unit ref: "modules/cabana#TestRelationChildMLNestedSave" status: pass - kind: automated_ui ref: "admin/tests/form/MLFields.test.ts" status: pass - kind: other ref: "bash scripts/check-phase14.2.1.sh --all" status: pass human_judgment: false - id: D4 description: "Administrator UAT of Locales plus a Journal-shaped mltext form (one selector per ML field, synchronized switch, save/reload both locales)" verification: [] human_judgment: true rationale: "Plan-level human-check is harvested at phase verification, not mid-flight" duration: 41min completed: 2026-10-06 status: complete --- # Phase 14.2.1 Plan 06: Lift ML maps on relation-child saves Summary **Relation-child create/update/show persist and hydrate nested locale maps the same way host CRUD does, closing WR-02** ## Performance - **Duration:** 41 min - **Started:** 2026-10-06T13:48:28Z - **Completed:** 2026-10-06T14:29:04Z - **Tasks:** 3 - **Files modified:** 15 ## Accomplishments - `RelationService.writer` is the same optional `TranslationWriter` Lookup `crud()` uses. `relations()` assigns it; no new HTTP route. - `CreateChild` / `UpdateChild` call `liftMLValues` on `cr.child` before `fillChild`, `applyMLTranslations` after the child has a PK, then `hydrateMLRecord` after `projectFullRecord`. Undeclared locale `de` is 422 and inserts no row. - `ShowChild` hydrates declared ML fields after `projectFullRecord` on `cr.childForm()`. Writer is unexported; cabana never imports sm-translate-plugin. - `TestRelationChildMLNestedSave` covers create, update, show, and `de` 422. Phase gate `FRAMEWORK_REQUIRE` includes `TestMLHydration` and `TestRelationChildMLNestedSave`; `T-14.2.1-19..23` are mapped. `bash scripts/check-phase14.2.1.sh --all` printed `Phase 14.2.1 gate passed`. ## Task Commits Each task was committed atomically: 1. **Task 1: Lift one nested mltext map through CreateChild** — `88a3a88` (feat) summercms.go 2. **Task 2: Mirror lift/apply/hydrate on UpdateChild and ShowChild** — `4e37d87` (feat) summercms.go 3. **Task 3: Named unit/Vitest/cabana tests and the phase gate** — `ef03016` (test) summercms.go Sibling repos (Rule 3, not in the plan file list): - `b535b2a` (test) sm-translate-plugin — expect hydrated ML maps on fixture create - `b3e0e26` (fix) sm-translate-plugin — keep user-plugin replace out of submodule go.mod - `1860ad6` (fix) sm-grzybyfunkcjonalne-app — point translate gitlink at TranslatedExact adapter **Plan metadata:** this SUMMARY commit Nothing was pushed. WR-01/WR-03, new migrations, new HTTP routes, and npm pins were not touched. Plan 05 CR-01 hydration was consumed, not redone. ## Files Created/Modified - `modules/cabana/relation.go` — unexported `writer TranslationWriter` - `modules/cabana/http.go` — `relations()` Lookup `[TranslationWriter]` matching `crud()` - `modules/cabana/relation_child.go` — lift/apply/hydrate on CreateChild, UpdateChild, ShowChild - `modules/cabana/relation_child_ml_test.go` — `TestRelationChildMLNestedSave` (create, update, show, de 422) - `modules/cabana/ml_smoke_test.go` — `recordingWriter` PK guard used by the child test - `modules/cabana/README.md`, `docs/backend/forms.md` — child records share the CRUD ML path - `admin/tests/form/MLFields.test.ts` — empty-pl payload and one selector per ML field - `admin/tests/form/MarkdownField.test.ts` — provide `FORM_ENABLED_LOCALES` for mlmarkdown composition - `scripts/check-phase14.2.1.sh` — `TestMLHydration`, `TestRelationChildMLNestedSave`, threats 19–23 - `.planning/phases/14.2.1-translate-plugin/14.2.1-SECURITY-REVIEW.md` — `T-14.2.1-19`..`23`, `threats_total: 24` - `../sm-translate-plugin/integration_test.go` — decode hydrated title/slug/body maps - `../sm-translate-plugin/go.mod`, `go.work` — user-plugin replace lives in plugin go.work - `../sm-grzybyfunkcjonalne-app` gitlink `plugins/golem15/translate` → `b3e0e26` ## Decisions Made - `RelationService.writer` stays unexported. Document behaviour on `TranslationWriter` / child records, not a new exported type. - Child Show hydrates with op `"update"` like CRUD Show. - Plugin sibling-relative `sm-user-plugin` replace belongs in plugin `go.work`, not `go.mod`, so the proof-host workspace `use ./plugins/golem15/user` does not conflict. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 3 - Blocking] Plugin TestTranslateEndToEnd after Plan 05 hydration** - **Found during:** Task 3 verify - **Issue:** Fixture create now returns locale maps; the plugin test still decoded `title`/`slug`/`body` as strings. - **Fix:** Decode those fields as `map[string]string`. - **Files modified:** `../sm-translate-plugin/integration_test.go` - **Verification:** `TestTranslateEndToEnd` PASS - **Committed in:** `b535b2a` **2. [Rule 3 - Blocking] Host compile missing TranslatedExact plus go.mod replace conflict** - **Found during:** Task 3 `go -C ../sm-grzybyfunkcjonalne-app vet` - **Issue:** Host gitlink was Plan 03 `a3553b9` (no `TranslatedExact`). Checking out later plugin SHAs pulled a sibling-relative `sm-user-plugin` replace that conflicted with host `use ./plugins/golem15/user`. - **Fix:** Move that replace into plugin `go.work`; point the host gitlink at `b3e0e26`. - **Files modified:** `../sm-translate-plugin/go.mod`, `../sm-translate-plugin/go.work`, host gitlink - **Verification:** `TestBootUserTranslate` PASS; gate `--host` PASS - **Committed in:** `b3e0e26`, `1860ad6` **3. [Rule 3 - Blocking] MarkdownField mlmarkdown composition after FORM_ENABLED_LOCALES** - **Found during:** Task 3 `bash scripts/check-phase14.2.1.sh --all` (`--admin`) - **Issue:** `MarkdownField.test.ts` mounted `MLMarkdownField` without `FORM_ENABLED_LOCALES`, so locale `pl` never activated and preview stayed English `# Hi`. - **Fix:** Provide `FORM_ENABLED_LOCALES` `['en','pl']` on that mount. - **Files modified:** `admin/tests/form/MarkdownField.test.ts` - **Verification:** admin Vitest 59/59 PASS; `--all` printed `Phase 14.2.1 gate passed` - **Committed in:** `ef03016` **4. [Rule 3 - Blocking] Vitest `--run` path vs include glob** - **Found during:** Task 3 verify - **Issue:** Plan path `admin/tests/form/MLFields.test.ts` finds no files; include is `tests/**` under `admin/`. - **Fix:** Ran `tests/form/MLFields.test.ts` (same as Plan 05). - **Files modified:** none (verify command only) - **Verification:** 11/11 PASS - **Committed in:** n/a --- **Total deviations:** 4 auto-fixed (all Rule 3 blocking tests) **Impact on plan:** Required for the last-plan gate. No WR-01/WR-03. No new migration, HTTP route, or npm pin (T-14.2.1-SC). Threat IDs 01–18 were not reused. ## Issues Encountered - `mlJournal` needed `Entries []mlEntry \`gorm:"-"\`` so the hasMany relation compiled (Task 1, in `88a3a88`). - `requirements.mark-complete D-06 D-17` is expected `not_found`: those IDs are CONTEXT.md decisions, not REQUIREMENTS.md rows. They remain on this SUMMARY's `requirements-completed`. - Unrelated dirty `.planning/` files from other sessions were left unstaged. - Plan-level `` is harvested at phase verification, not mid-flight. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness Phase 14.2.1 production plans are complete: CR-01 and WR-02 are closed with named tests and `Phase 14.2.1 gate passed`. Ready for phase verification / UAT of the plan-level human-check. Journal (Phase 15) can keep translatable fields including relation-child ML writes. --- *Phase: 14.2.1-translate-plugin* *Completed: 2026-10-06* ## Self-Check: PASSED