Record WR-02 close-out, D-06/D-17 coverage, and last-plan gate evidence for phase verification. Co-authored-by: Cursor <cursoragent@cursor.com>
228 lines
11 KiB
Markdown
228 lines
11 KiB
Markdown
---
|
||
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 `<human-check>` 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
|