You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit bcdb1db
Browse filesBrowse the repository at this point in the historyBrowse files
fix(spec,messaging): a push device's locale is described as stored, not as the language pushes render in (#30544)
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wg5yARdbrWsd674TB3515nv2)_
domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
## 维护者速读
- 改了什么:推送设备 `locale` 的三处描述(设备 schema、设备注册请求、`sys_user_device.locale`
字段及其四种语言的帮助文字)改成如实说法:它只随设备保存,推送不按它渲染,推送用收件人的语言(用户语言,其次部署默认)。
-
为什么改:原描述承诺"推送按设备语言渲染",实际没有任何代码这样做;手机设成日文、账号设成英文的用户收到的是英文推送。契约说了做不到的事(ADR-0049:要么兑现要么删掉),席位选了最小的方案
1。
- 风险与回滚:只改描述文字和一个测试,运行行为零变化,调用方发什么都照常收;回滚即 revert 本 PR。
- 先看:`packages/spec/src/system/push-device.zod.ts`(locale
一行)、`packages/spec/src/api/protocol.zod.ts`(RegisterDeviceRequest.locale
一行)、`packages/services/service-messaging/src/keyed-notification-text.integration.test.ts`(末尾新
describe)。
- 两分钟验证:`pnpm --filter @objectstack/service-messaging exec vitest run
src/keyed-notification-text.integration.test.ts`,预期 20/20 通过,其中 3
条是新钉的"推送按用户语言而非设备语言"。
- 需要你做的:无,席位落地。
Fixes#29818
Clause-②: no
Metadata-protocol: none
## What changes
- `UserDeviceSchema.locale`
(`packages/spec/src/system/push-device.zod.ts`): was "Device locale …
that pushes to this device are rendered in". Now: stored for the device,
as the registration named it; no push renders by it; a push arrives in
the recipient's language (`sys_user.locale`, then the deployment
default).
- `RegisterDeviceRequestSchema.locale`
(`packages/spec/src/api/protocol.zod.ts`): the same promise in the
request contract a client sends, corrected the same way. **Scope
extension approved by the seat (14:22Z):** the dispatch named one spec
description; this twin carries the identical wording and defect, so it
rides in this PR.
- `sys_user_device.locale` field description
(`packages/services/service-messaging/src/objects/user-device.object.ts`)
says the same; the messaging i18n bundle's `help` for that field is
regenerated in `en` (`check-i18n-bundles --write`) and its hand-written
`zh-CN` / `ja-JP` / `es-ES` translations are rewritten to the new
meaning (they otherwise kept translating the old promise). Declared to
and approved by the seat.
- Regenerated: `content/docs/references/api/protocol.mdx`,
`content/docs/references/system/push-device.mdx` (`gen:docs`). No other
spec artifact moved (`check:generated` 14/14 current).
- Changeset: `patch` for `@objectstack/spec` and
`@objectstack/service-messaging` (`Metadata-protocol: none`).
- Not done (option 2 of the card, a widening): no `push` row in
`TEMPLATE_CHANNELS`, no per-device rendering.
## Tests
A new top-level `describe` in
`keyed-notification-text.integration.test.ts` (the shared keyed-text
harness: real `ObjectQL` over `SqlDriver`/better-sqlite3, the kernel's
in-memory i18n, the real `MessagingService`), adding the real push
channel over the real device registry (`sys_user_device`) on a
push-service double that records each token's title and body. Each
recipient's phone is registered with a locale that is not the
recipient's: the `zh-CN` user's phone in `en`, the `en` user's phone in
`zh-CN`, the no-locale user's phone in `ja-JP`.
- the device row keeps the locale it was registered with;
- inline fan-out: the `zh-CN` user's phone gets the `zh-CN` sentence,
the `en` user's phone the English one, and the no-locale user's phone
the deployment default's (`zh-CN`), not `ja-JP`;
- durable path (SQL outbox + dispatcher): the same, rendered at
delivery.
Measured on `main` (fa9c0aa) before any description change: the test
was green, so the card's premise holds (pushes render by the recipient,
never the device).
## Acceptance notes
- The push relay's `text` member
(`packages/spec/src/api/push-relay.zod.ts:78`) also says the line is
shown "in the device locale". No producer of it lives in this repository
(the relay is cloud's), so nothing here was measured against it;
untouched, carrier: cloud.
## Verification
All readings at `e541b3721` (head; `origin/main` was still `fa9c0aa2`,
so the merge was a no-op).
<details><summary>Tests</summary>
- `pnpm --filter @objectstack/service-messaging test`: Test Files 66
passed (66), Tests 815 passed (815); `typecheck` exit 0 (lock VERDICT
command-exit 0).
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2`: Test
Files 905 passed (905), Tests 27161 passed, 1 todo (lock VERDICT
command-exit 0).
- The new pins alone: `keyed-notification-text.integration.test.ts`
20/20, first green on `main`'s code before any description edit (commit
`8ab75e9f`).
</details>
<details><summary>Derived gates</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 107 families; each run with its exit
recorded before any pipe; `--ran`: 107 accounted, 106 run, 1
NOT-MEASURED.
- NOT MEASURED: `pnpm check:dual-build-cjs-loads` (exit 3, PREREQUISITE
NOT MET: needs every package's `dist/`; left to CI).
- First runs of `check-plugin-teardown-shape --self-test` and
`check:skill-examples` exited 3 (shallow history; `@objectstack/client`
and `client-react` not built); both re-run green after the prerequisite
(`git fetch` of the fixture commit; building those two packages).
- `pnpm --filter @objectstack/spec check:generated`: all 14 artifacts
current. `node scripts/check-i18n-bundles.mjs --write`: exit 0, moved
only `en.objects.generated.ts`.
- `pnpm lint`: exit 0.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CZqHFs5GUFFGRbLgxy)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
fix(spec,messaging): a push device's `locale` is described as what it is — stored for the device, and no push renders by it
7
+
8
+
Clause-②: no
9
+
Metadata-protocol: none
10
+
11
+
-**`@objectstack/spec`**: `UserDeviceSchema.locale` and `RegisterDeviceRequestSchema.locale` no longer say pushes to the device are rendered in its locale. They now say the value is stored for the device and that a push arrives in the recipient's language: `sys_user.locale`, then the deployment default. Description text only; nothing a caller sends is accepted or refused differently.
12
+
-**`@objectstack/service-messaging`**: the `sys_user_device.locale` field description says the same, in all four shipped languages. A test now pins it end to end: with the real push channel over the real device registry, a phone registered with a locale other than its user's receives the push in the user's language, on the inline fan-out and on the durable outbox path alike.
Copy file name to clipboardExpand all lines: content/docs/references/api/protocol.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2777,7 +2777,7 @@ Register or refresh a push device for the signed-in user
2777
2777
|**name**|`string`| optional | Device friendly name |
2778
2778
|**vendor**|`Enum<'apns' \| 'fcm'>`| optional | Push vendor the token belongs to: apns or fcm. Absent, the server derives it from platform: ios means apns, android means fcm. web has no default vendor: a web registration names apns or fcm, and one without vendor is refused |
2779
2779
|**appId**|`string`| optional | App the token was issued to: the iOS bundle ID or the Android application ID |
2780
-
|**locale**|`string`| optional | Device locale (BCP 47, for example zh-CN) that pushes to this device are rendered in|
2780
+
|**locale**|`string`| optional | Device locale (BCP 47, for example zh-CN). Stored on the device row; no push renders by it: a push arrives in the recipient's language (sys_user.locale, then the deployment default)|
2781
2781
|**origin**|`string`| optional |[PLANNED — not yet stored] The origin the Console was loaded from (window.location.origin, for example https://crm.example.com): the address the mobile shell saved for this server. The Console writes it; the server stores it on the device row and every push to the device carries it back, so a shell with several saved servers opens the push on the one that sent it. http or https, a lowercase host and an optional port; a path, another scheme or a bare host is refused |
Copy file name to clipboardExpand all lines: content/docs/references/system/push-device.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -36,7 +36,7 @@ A push device of one user: one row per app install
36
36
|**token**|`string`| ✅ | Vendor push token. Held by one live device at a time: a second user's registration moves it |
37
37
|**platform**|`Enum<'ios' \| 'android' \| 'web'>`| ✅ | Device platform, as the registration named it |
38
38
|**appId**|`string`| optional | App the token was issued to: the iOS bundle ID or the Android application ID |
39
-
|**locale**|`string`| optional | Device locale (BCP 47, for example zh-CN) that pushes to this device are rendered in|
39
+
|**locale**|`string`| optional | Device locale (BCP 47, for example zh-CN), as the registration named it. Stored for the device; no push renders by it: a push arrives in the recipient's language (sys_user.locale, then the deployment default)|
40
40
|**name**|`string`| optional | Device friendly name, as the registration named it |
41
41
|**origin**|`string`| optional |[PLANNED — not yet stored] The origin the Console was loaded from, as the registration named it (for example https://crm.example.com). The device registry keeps it on the row and the push channel copies it into every push to this device, where the mobile shell picks the saved server by it. Absent on a device registered without one |
42
42
|**enabled**|`boolean`| ✅ | False once the vendor reported the token dead; nothing is delivered to a disabled device |
help: "Device locale that pushes to this device are rendered in."
463
+
help: "Device locale, as the registration named it. Stored for the device; no push renders by it: a push arrives in the recipient's language (their user locale, then the deployment default)."
help: "Configuración regional del dispositivo en la que se muestran las notificaciones push enviadas a él."
463
+
help: "Configuración regional del dispositivo, tal como la indicó el registro. Se guarda para el dispositivo; ninguna notificación push se muestra según ella: llega en el idioma del destinatario (su configuración regional de usuario y, si no, la predeterminada de la implantación)."
0 commit comments