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 e026730
Browse filesBrowse the repository at this point in the historyBrowse files
feat(service-messaging,spec,cli)!: `sys_notification_subscription` retires — no reader ever expanded a subscription into recipients (ADR-0030 L3, amended)
**BREAKING**, shipped as `minor` under the repo's launch-window convention for breaking changes. It goes into 18.x because its ADR-0087 semantic entry, `sys-notification-subscription-retired`, carries the narrowing.
15
+
16
+
ADR-0030 described `sys_notification_subscription` as Layer-3 audience expansion: "who is subscribed to a topic". That expansion was never wired. Every producer names its recipients explicitly (`EmitInput.audience` is required: a flow notification node's recipients, an approval's approvers, a mention's users), no audience member asks for subscribers, and nothing reads the table's rows. So an administrator could author subscriptions in Setup that made nobody receive anything. The maintainer ruled to retire the object rather than build the expansion. The ruling supersedes the earlier decision to keep it with a code comment saying it was not wired, and ADR-0030 is amended to match.
17
+
18
+
**What moves for consumers, FROM → TO.**
19
+
20
+
-**The object.** FROM `import { NotificationSubscription } from '@objectstack/service-messaging'` TO nothing: delete the import. `MessagingServicePlugin` neither registers nor provisions the object any more, so the generic data door no longer serves it.
21
+
-**Setup navigation.** FROM a Notification Subscriptions item (`nav_notification_subscriptions`) in the Setup app's Configuration group TO no item. Notification Preferences and Notification Templates stay. The item's labels leave the platform app translations, and the object's keys leave service-messaging's generated object bundles.
22
+
-**The name.**`PLATFORM_OBJECTS_BY_PACKAGE['service-messaging']` (`@objectstack/spec/system`) lists six objects, and `isPlatformProvidedObjectName` answers `false` for `sys_notification_subscription`. A stack that names it (a lookup target, a flow trigger, a permission entry, a navigation item) is now flagged as naming an unknown object. Fix: remove the reference. To control who gets a notification, name the audience on the producer. To mute a topic for a user, write a `sys_notification_preference` row, which is unchanged.
23
+
-**`os migrate organization-ownership` (`@objectstack/cli`).** The inventory row for `sys_notification_subscription` moves FROM fate 3 (`attribution`: organization-less rows went to the Default Organization under `single`) TO fate 4 (`report`: rows listed by id, never attributed, never deleted). The table is exempt from NOT NULL, because no declaration anchors tenancy on its column any more. The row stays, so a database that still holds the table plans instead of being refused as carrying an uninventoried platform table. The plan's `inventoryDigest` changes: a plan file written by an earlier CLI no longer matches, and `--apply --plan-file` refuses it with `plan-digest-mismatch`. Re-run `os migrate organization-ownership --out PLAN`, review it, and apply that.
24
+
25
+
**Existing databases.** The table is left in place untouched: no backfill, no reaper, no migrate command. Schema sync is additive and never drops a table, so a database an earlier release provisioned keeps `sys_notification_subscription` and any rows an administrator wrote, and nothing reads them. Export any row worth keeping. Dropping the table is the operator's call, by hand: `DROP TABLE sys_notification_subscription`.
**Status**: Accepted (2026-06-01) — **P0–P3b2 shipped** (P3b-2 digest collapse landed); cross-repo objectui cut-over remains. See [§ Implementation status & remaining work](#implementation-status--remaining-work). · **Amended** (2026-09-20, #16194 — P0's idempotent data migration `migrateSysNotificationToEvent` is **retired**: it had zero production callers and no way to be run, and both ways of giving it one — an `os migrate` sub-command and a boot-time invoker — were refused. Pre-ADR-0030 `sys_notification` rows are therefore **not** carried by the platform on this line. ⛔ Nothing else moves: single ingress, the layered object model and the remaining objectui cut-over read exactly as accepted.)
3
+
**Status**: Accepted (2026-06-01) — **P0–P3b2 shipped** (P3b-2 digest collapse landed); cross-repo objectui cut-over remains. See [§ Implementation status & remaining work](#implementation-status--remaining-work). · **Amended** (2026-09-20, #16194 — P0's idempotent data migration `migrateSysNotificationToEvent` is **retired**: it had zero production callers and no way to be run, and both ways of giving it one — an `os migrate` sub-command and a boot-time invoker — were refused. Pre-ADR-0030 `sys_notification` rows are therefore **not** carried by the platform on this line. ⛔ Nothing else moves: single ingress, the layered object model and the remaining objectui cut-over read exactly as accepted.) · **Amended** (2026-10-20, maintainer ruling D on #24770, recorded 6293376907 — the L3 `sys_notification_subscription` object is **retired**: the subscription→recipient expansion was never wired, every producer names an explicit audience (`EmitInput.audience` is required), and the Setup grid let an administrator author subscriptions that made nobody receive anything. The ruling supersedes #9807's keep-with-a-comment decision. The object, its registration, its Setup nav item and its translations leave; existing tables are left in place untouched (ADR-0087 semantic entry `sys-notification-subscription-retired`). L3 is now preference-only; a topic-subscription capability, if real demand appears, returns as a new decision with a who-may-subscribe-whom permission model. ⛔ Nothing else moves.)
**Related**: [ADR-0019 — Approval as a Flow Node](./0019-approval-as-flow-node.md), [ADR-0022 — Connectors vs Messaging Channels](./0022-connectors-vs-messaging-channels.md)
| L3 Resolve + Preference |`sys_notification_preference`*(`sys_notification_subscription` retired 2026-10-20 — see the status line)*| audience resolution of the producer's explicit audience (`role:` / `owner_of:record` / `team:` / explicit ids; **email→id resolved here**), user×topic×channel toggles, quiet-hours, digest; mandatory topics bypass. ~~Topic-subscription audience expansion~~ — never wired, retired with its object.|
32
32
| L4 Delivery (outbox) |`sys_notification_delivery`| one row per (event × recipient × channel); state machine `pending→sent→failed/dead`; retry/backoff/dedup; the durable spine |
33
33
| L5 Materialize + Receipt |`sys_inbox_message` (in-app), `sys_email`, `sys_user_device` (push), connector dispatch (webhook/Slack); `sys_notification_receipt`| each channel renders delivery into a consumable artifact; **the bell reads `sys_inbox_message`**; read/clicked/dismissed live in `sys_notification_receipt`|
34
34
| Cross-cutting |`sys_notification_template` (topic×channel×locale) | rendering, with generic fallback to `title`/`body`|
@@ -119,9 +119,12 @@ configurable daily send-hour (windows flush at local midnight / Monday 00:00).
119
119
`connector_action` Slack path already works today).
120
120
-**`defineTopic()`** declarative topic catalog (Studio discoverability for topics /
121
121
templates / preferences) — the low-code backbone.
122
-
-**Subscription-driven fan-out**: expand a topic's `sys_notification_subscription`
122
+
-~~**Subscription-driven fan-out**: expand a topic's `sys_notification_subscription`
123
123
principals when a producer emits without an explicit audience (object exists;
124
-
expansion not wired).
124
+
expansion not wired).~~**Withdrawn (2026-10-20, ruling D on #24770)** — the object
125
+
is retired instead (status line): zero pull, every producer names its audience,
126
+
and the expansion would need a permission model nobody asked for. Existing
127
+
tables are left in place untouched, read by nothing.
125
128
-**MJML** compilation for email (P3a treats `mjml` format as raw HTML).
126
129
- Quiet-hours **tz fallback** to a `sys_user` timezone field (currently
{object: 'sys_notification',census: P1,fate: 'attribution',citation: `${D7} (the notification family is recipient-anchored); the source record carries the organization`,anchors: [subject('source_object','source_id')]},
504
520
{object: 'sys_notification_delivery',census: P1,fate: 'attribution',citation: `${C7_FATE_3}; the delivery's notification carries the organization`,anchors: [parent('notification_id','sys_notification')]},
505
521
{object: 'sys_notification_receipt',census: P1,fate: 'attribution',citation: `${D7} (recipient-anchored); the receipt's notification carries the organization`,anchors: [parent('notification_id','sys_notification')]},
0 commit comments