@@ -184,6 +184,39 @@ for this ADR is the `setup` app and first-party capability contributions;
184184generalizing app-extension to arbitrary apps is a follow-up. References inside
185185contributions follow ADR-0028's naming model (` sys.audit_log ` , etc.).
186186
187+ ### D8 — An object's i18n resources migrate with its ownership
188+
189+ A kernel object is more than its schema: it has ** localized labels, field help,
190+ and list-view names** . Today these live in ` platform-objects ` as generated
191+ bundles (` src/apps/translations/*.objects.generated.ts ` , produced by
192+ ` os i18n extract ` against ` scripts/i18n-extract.config.ts ` , loaded at runtime by
193+ the ` platform-objects ` plugin). The generated entries are keyed by ** object
194+ name** (` sys_webhook: {...} ` ) and loaded globally, so they keep working at
195+ runtime regardless of which package owns the object — but their ** source of
196+ truth** stays wrongly attached to the monolith.
197+
198+ Therefore object migration must carry the i18n resources, not just the schema:
199+
200+ - The owning plugin becomes the ** source of truth** for its objects'
201+ translations — it owns the extract config entry and ships the generated
202+ bundle(s), and contributes them at runtime (e.g. via ` i18n.loadTranslations `
203+ or ` manifest.translations ` ), exactly as ` platform-objects ` does today.
204+ - When an object leaves ` platform-objects ` , it is removed from
205+ ` scripts/i18n-extract.config.ts ` ; ** regenerating before the plugin owns its
206+ extraction would silently drop locales** — so the plugin-side i18n extraction
207+ must land in the same step (or the object stays in the extract set
208+ transitionally, explicitly tracked).
209+ - A plugin that currently has ** no i18n infrastructure** (e.g. ` plugin-webhooks ` ,
210+ whose ` sys_webhook_delivery ` ships inline labels only) must gain one as part of
211+ taking ownership of a localized object — this is real, recurring work in every
212+ K2 domain move and must be budgeted, not assumed free.
213+
214+ Pilot note: the first pilot (webhooks) moves the schema and removes the object
215+ from the monolith extract config, but ** defers building plugin-side i18n
216+ extraction** — the existing generated entries remain valid at runtime
217+ (object-name-keyed), and the plugin-owned i18n bundle is the explicit next sub-task
218+ before any regeneration.
219+
187220---
188221
189222## Migration plan (template-transparent, independently shippable)
@@ -211,10 +244,14 @@ the ADR-0028 naming flip.
211244 webhooks), relocate the ` *.object.ts ` definitions into the owning plugin and
212245 switch its manifest from "declare ` namespace:'sys' ` " to actual ` own ` , and move
213246 that domain's ` setup ` nav entries out of the base shell into the plugin as
214- navigation contributions (D7). Keep a ` platform-objects ` re-export facade so
215- importers don't break mid-migration.
247+ navigation contributions (D7). ** Migrate the object's i18n resources with it**
248+ (see D8). Keep a ` platform-objects ` re-export facade so importers don't break
249+ mid-migration (where the dependency direction forbids a facade — e.g. a leaf
250+ plugin ` platform-objects ` already depends on — do a clean move instead and
251+ update the importers).
216252 * Exit per domain:* that domain's objects resolve to the new owner; its setup
217- menu entries render via its own contribution; its tests green; cross-domain
253+ menu entries render via its own contribution; its translations load from the
254+ owning plugin (no localization regression); its tests green; cross-domain
218255 lookups to the hub still resolve.
219256- ** K3 — Boundary enforcement.** Flip the app-cannot-define-kernel check
220257 warn→error. Classify the scattered ` ai ` /` mail ` /` branding ` /` prefs ` /` feat ` /… —
0 commit comments