Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 24 additions & 0 deletions .changeset/notification-subscription-expansion-not-wired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
---
"@objectstack/service-messaging": patch
---

docs(service-messaging): mark the `sys_notification_subscription` expansion not-yet-wired and align the `principal` description with the resolver (#9807)

The object header described a live routing control that does not exist: "where a
producer emits with `audience: 'subscribers'` … the resolver expands the topic's
subscriptions into recipients". Nothing implements that. `AudienceSpec`
(`messaging-service.ts`) has no `'subscribers'` member, `EmitInput.audience` is
required, and `RecipientResolver` has no branch that reads
`sys_notification_subscription` — the literal `'subscribers'` occurs exactly once
in the repo, in that sentence. Every delivery today comes from the explicit
`audience` a producer passes to `emit()`, so the Setup "Notification
Subscriptions" grid is admin-authored data, not a live routing control. The
header now says so, per the maintainer ruling on #9807 (annotate now; the
ADR-0030 Layer-3 expansion stays future work, deferred on measured zero pull).

The `principal` field description shipped a four-form list (`'role:x'` |
`'team:x'` | `'user:id'` | bare user id) narrower than what
`RecipientResolver.resolveOne()` accepts for the same string shape; it now also
names `'owner_of:object:id'` and the email form (matched against `sys_user`).
This is a user-visible string: it ships into `dist/` and into the generated `en`
translation bundle as the field's help text in the Setup grid.
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,11 +6,18 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
* `sys_notification_subscription` — who is subscribed to a topic (ADR-0030
* Layer 3).
*
* Declares standing interest in a `topic` by a `principal` (`role:x`, `team:x`,
* `user:id`, or a bare user id). Where a producer emits with `audience:
* 'subscribers'` (or no explicit audience), the resolver expands the topic's
* subscriptions into recipients — the opt-in counterpart to the explicit
* audience most producers pass today.
* Declares standing interest in a `topic` by a `principal` (see that field for
* the accepted selector forms).
*
* ⚠️ [#9807] The subscription→recipient expansion is **NOT WIRED in this
* repo**: `AudienceSpec` (`messaging-service.ts`) has no `'subscribers'`
* member, `EmitInput.audience` is REQUIRED, and no `RecipientResolver` branch
* expands a topic's subscriptions — so nothing here reads these rows at
* runtime. Every delivery today comes from the explicit `audience` a producer
* passes to `emit()`, which makes the Setup "Notification Subscriptions" grid
* admin-authored data, NOT a live routing control. The ADR-0030 Layer-3
* expansion is future work, deferred on measured zero pull; ADR-0012/0030
* describe that target state, not today's behaviour.
*
* Distinct from `sys_notification_preference`: a subscription says "include me
* for this topic"; a preference says "but mute it on this channel".
Expand DownExpand Up@@ -45,7 +52,13 @@ export const NotificationSubscription = ObjectSchema.create({
label: 'Principal',
required: true,
searchable: true,
description: "Subscriber selector: 'role:x' | 'team:x' | 'user:id' | bare user id.",
// [#9807] Kept in step with what `RecipientResolver.resolveOne()` really
// accepts for a string spec, so this does not under-describe the day the
// expansion above is wired: an email-shaped value is matched against
// `sys_user` (kept verbatim when no user matches), and anything otherwise
// unrecognized falls through as a bare user id.
description:
"Subscriber selector: 'role:x' | 'team:x' | 'user:id' | 'owner_of:object:id' | an email | a bare user id.",
}),

enabled: Field.boolean({
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -238,7 +238,7 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
},
principal: {
label: "Principal",
help: "Subscriber selector: 'role:x' | 'team:x' | 'user:id' | bare user id."
help: "Subscriber selector: 'role:x' | 'team:x' | 'user:id' | 'owner_of:object:id' | an email | a bare user id."
},
enabled: {
label: "Enabled",
Expand Down
Loading