Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1658c4c
fix(i18n): serve the translation bundle without Studio's server-resol…
claude Oct 26, 2026
143499a
test(spec): pin the served translation face against the platform face…
claude Oct 26, 2026
e2fc71e
chore(spec): regenerate artifacts for the served translation face
claude Oct 26, 2026
84674c9
docs(i18n): the served bundle leaves the server-resolved metadataForm…
claude Oct 26, 2026
8b2ad27
chore(spec): count the served face's row in the dropped-refinements h…
claude Oct 26, 2026
2f4dfc0
fix(i18n)!: declare requestedLocale and carry the breaking dispositio…
claude Oct 26, 2026
81dc21e
Merge remote-tracking branch 'origin/main' into claude/project-thread…
claude Oct 26, 2026
4f5d2f0
chore(sdui): re-record the i18n registry digest after the docblock fix
claude Oct 26, 2026
80441cd
Merge remote-tracking branch 'origin/main' into claude/project-thread…
claude Oct 26, 2026
4c82d13
fix(i18n)!: declare the served-translations narrowing as Metadata-pro…
claude Oct 26, 2026
4808827
chore(sdui): re-record the i18n registry digest after merging main
claude Oct 26, 2026
f575345
Merge remote-tracking branch 'origin/main' into claude/project-thread…
claude Oct 26, 2026
ecf4c1e
chore(sdui): re-record the i18n registry digest after merging main
claude Oct 26, 2026
dca2e00
Merge remote-tracking branch 'origin/main' into claude/project-thread…
claude Oct 26, 2026
bf688dc
docs(spec): regenerate the reference index after merging main
claude Oct 26, 2026
2cd9696
chore(sdui): re-record the i18n registry digest after merging main
claude Oct 26, 2026
a25dc27
Merge remote-tracking branch 'origin/main' into claude/project-thread…
claude Oct 27, 2026
fec80c3
chore(sdui): re-record the i18n registry digest after merging main
claude Oct 27, 2026
41d06a4
Merge remote-tracking branch 'origin/main' into claude/project-thread…
claude Oct 27, 2026
392a610
chore(spec): regenerate the authorable surface and reference index af…
claude Oct 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .changeset/25785-served-translations-without-metadata-forms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@objectstack/spec': minor
'@objectstack/runtime': minor
---

**BREAKING** fix(i18n)!: the translation bundle a console downloads leaves out Studio's `metadataForms`

Clause-②: yes (narrowing)
Metadata-protocol: narrowed

`GET /i18n/translations/:locale` served the i18n service's whole locale tree, so every console open downloaded `metadataForms`, the translations of Studio's metadata-type authoring forms: 75 KB of the 221 KB raw zh-CN bundle on `examples/app-crm`. No browser reads that group from the bundle. Studio's forms already arrive translated, because `GET /meta` (and `/meta/types`) resolves `metadataForms.<type>` on the server, from the same i18n service, before it serves each form.

The route now serves every group except the ones the server resolves itself. The spec names them in `SERVER_RESOLVED_TRANSLATION_GROUPS`, which today holds only `metadataForms`. `GetTranslationsResponseSchema.translations` is now `ServedTranslationDataSchema`: the platform face without those groups, and closed, so a body that still carries one fails it. The runtime copies the tree and never edits the service's own one, which is the tree `/meta` translates from. The `ETag` still hashes the bytes served, so a warm reopen still gets `304`.

**Why this is breaking, and why `minor`.** A response body that carries `metadataForms` was accepted by the published `GetTranslationsResponseSchema` and now fails it, the exported `GetTranslationsResponse` type loses that member, and the route stops serving a group it served. The published protocol records the move: the authorable surface gains the served face's keys (`ServedTranslationData`) and `GetTranslationsResponse:requestedLocale`, and the response face a client parses against is narrower, so the kind is `narrowed` and both packages that carry it — the spec that declares the face and the runtime that serves it — ship `minor`. What an author writes into a translation bundle does not move.

**What to do.** Nothing, unless you read `metadataForms` from this route. Read it from `GET /meta/types` instead, which serves each form already translated into the requested locale, rather than from the bundle this route returns. Authoring is unchanged: `metadataForms` stays a group on every bundle face (`TranslationData`, `PlatformTranslationData`), in `i18n:extract` and in the i18n service.

Also on this route: `requestedLocale`, which the door has always served when it fell back to another locale (`zh` served as `zh-CN`), is now declared on `GetTranslationsResponseSchema`, optional. It was served undeclared, so no generated client could read it.

<!-- adr-0087: not-required (no-migration-prescription) The narrowed face is a RESPONSE, and no stored metadata holds one: no package source, stored row or object definition carries a `GetTranslationsResponse` body, so there is nothing for `objectstack migrate meta` to reach or rewrite. What an author writes into a translation bundle is unchanged — `metadataForms` stays declared on both authored bundle faces and in the i18n service, and the authorable-surface keys this diff moves are ADDITIONS (the served face's keys and `GetTranslationsResponse:requestedLocale`), none removed or renamed. The affected party is a code consumer reading this route's answer, whose channel is the compiler and the response schema, not the metadata ledger (ADR-0087 D8), and the remedy is a read from another door (`GET /meta/types`, which serves the same forms already translated), never a conversion between two metadata versions. The other categories are closed on facts: both packages publish (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); the diff touches `packages/spec/**` and a `*.zod.ts` contract face (not type-surface-only); and the narrowed declaration IS a Zod projection, `z.input<typeof GetTranslationsResponseSchema>` (not runtime-interface-only), with no export retired (not runtime-export-retired). -->
12 changes: 9 additions & 3 deletions content/docs/protocol/kernel/i18n-standard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,15 @@ through the one the renderer reads for that node:

Both run in `GET /api/v1/meta`, which serves every type's `form` and `schema`
already localised for the request's locale; a client deriving the schema itself
with `z.toJSONSchema` applies the second one to its own copy. Only `label`
crosses over to the schema (as `title`); `helpText` and `placeholder` stay on
the form.
with `z.toJSONSchema` applies the second one to its own copy, over a bundle it
carries itself. Only `label` crosses over to the schema (as `title`); `helpText`
and `placeholder` stay on the form.

Because the server resolves this group, the bundle a console downloads when it
opens — `GET /api/v1/i18n/translations/:locale` — leaves `metadataForms` out:
`SERVER_RESOLVED_TRANSLATION_GROUPS` names it, and the response's `translations`
is `ServedTranslationData`, the platform groups without it. Author it like any
other group; it stays in the i18n service, which is where `/meta` reads it.

**Naming an item-level property** therefore has three parts, none of which is
a locale-catalog entry alone: the English name is a `.meta({ title: 'Action URL' })`
Expand Down
2 changes: 1 addition & 1 deletion content/docs/references/api/protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1764,6 +1764,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **locale** | `string` | ✅ | Locale code |
| **requestedLocale** | `string` | optional | The locale the caller asked for, when the served locale is a fallback from it |
| **translations** | `{ objects?: Record<string, object>; picklists?: Record<string, object>; apps?: Record<string, object>; messages?: Record<string, string>; … }` | ✅ | Translation data |

### Nested Shape: `GetTranslationsResponse.translations`
Expand All @@ -1782,7 +1783,6 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
| **flows** | `Record<string, { label?: string; successMessage?: string; errorMessage?: string; screens?: Record<string, object>; … }>` | optional | Screen-flow translations keyed by flow name |
| **positions** | `Record<string, { label?: string; description?: string }>` | optional | Position translations keyed by position name |
| **permissions** | `Record<string, { label?: string; description?: string }>` | optional | Permission-set translations keyed by permission-set name |
| **metadataForms** | `Record<string, { label?: string; description?: string; sections?: Record<string, object>; fields?: Record<string, object> }>` | optional | Translations for metadata-type configuration forms keyed by metadata type |
| **settingsCommon** | `{ sourceLabels?: object }` | optional | Cross-namespace Settings UI strings |
| **settings** | `Record<string, { title?: string; description?: string; groups?: Record<string, object>; keys?: Record<string, object>; … }>` | optional | Settings manifest translations keyed by namespace |

Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Protocol reference — every schema by module
navTitle: Protocol Reference
description: Every schema published by @objectstack/spec — 1548 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1549 schemas across 14 protocol modules
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand Down Expand Up @@ -32,9 +32,9 @@ counts are sums of the rows they head. Regenerate with
| [Security Protocol](/docs/references/security) | 5 | 34 | Permission sets, row-level security, sharing rules, tenancy posture. |
| [Shared Protocol](/docs/references/shared) | 10 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
| [System Protocol](/docs/references/system) | 37 | 286 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [System Protocol](/docs/references/system) | 37 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [UI Protocol](/docs/references/ui) | 16 | 171 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **200** | **1548** | 14 protocol modules |
| **Total** | **200** | **1549** | 14 protocol modules |

---

Expand Down Expand Up @@ -319,7 +319,7 @@ Studio designer metadata — the authoring surfaces for the protocols above.

## System Protocol

**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **37 pages, 286 schemas**
**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **37 pages, 287 schemas**

The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance.

Expand Down Expand Up @@ -360,7 +360,7 @@ The runtime environment — logging, jobs, cache, metrics, notifications, i18n a
| [`supplier-security.zod.ts`](/docs/references/system/supplier-security) | `SupplierAssessmentStatus`, `SupplierRiskLevel`, `SupplierSecurityAssessment`, `SupplierSecurityPolicy`, `SupplierSecurityRequirement` |
| [`tenant.zod.ts`](/docs/references/system/tenant) | `DatabaseLevelIsolationStrategy`, `DatabaseProvider`, `QuotaEnforcementResult`, `RowLevelIsolationStrategy`, `SchemaLevelIsolationStrategy`, `Tenant`, `TenantConnectionConfig`, `TenantIsolationConfig`, `TenantIsolationLevel`, `TenantQuota`, `TenantSecurityPolicy`, `TenantUsage` |
| [`tracing.zod.ts`](/docs/references/system/tracing) | `OpenTelemetryCompatibility`, `OtelExporterType`, `SamplingDecision`, `SamplingStrategyType`, `Span`, `SpanAttributeValue`, `SpanAttributes`, `SpanEvent`, `SpanKind`, `SpanLink`, `SpanStatus`, `TraceContext`, `TraceContextPropagation`, `TraceFlags`, `TracePropagationFormat`, `TraceSamplingConfig`, `TraceState`, `TracingConfig` |
| [`translation.zod.ts`](/docs/references/system/translation) | `ActionResultDialogTranslation`, `CoverageBreakdownEntry`, `FieldTranslation`, `Locale`, `ObjectTranslationData`, `PlatformTranslationBundle`, `PlatformTranslationData`, `TranslationBundle`, `TranslationConfig`, `TranslationCoverageResult`, `TranslationData`, `TranslationDiffItem`, `TranslationDiffStatus`, `TranslationItem` |
| [`translation.zod.ts`](/docs/references/system/translation) | `ActionResultDialogTranslation`, `CoverageBreakdownEntry`, `FieldTranslation`, `Locale`, `ObjectTranslationData`, `PlatformTranslationBundle`, `PlatformTranslationData`, `ServedTranslationData`, `TranslationBundle`, `TranslationConfig`, `TranslationCoverageResult`, `TranslationData`, `TranslationDiffItem`, `TranslationDiffStatus`, `TranslationItem` |
| [`worker.zod.ts`](/docs/references/system/worker) | `BatchProgress`, `QueueConfig`, `Task`, `TaskExecutionResult`, `TaskPriority`, `TaskRetryPolicy`, `TaskStatus`, `WorkerStats` |

---
Expand Down
Loading
Loading