Skip to content

finding: sys_notification_subscription's index comment says role:/team: resolve against sys_permission_set/sys_position; RecipientResolver reads sys_member/sys_team_member #9722

Description

@os-elon

Found while verifying the notification principal set for a docs card (objectstack-ai/objectos#109). Filed, not fixed — the two readings below have very different blast radii and I cannot separate them from source alone.

packages/services/service-messaging/src/objects/notification-subscription.object.ts:75-78 carries this note justifying the (topic, principal) per-organization unique index:

`principal` names are per-organization: `role:x` and `team:x`
resolve against `sys_permission_set` / `sys_position` rows that
#8461 and #8556 already scoped per organization, so
`role:sales_manager` denoted a DIFFERENT principal in each
organization while colliding on one installation-wide key.

The resolver disagrees on both halves. packages/services/service-messaging/src/recipient-resolver.ts documents and implements:

'role:name' → sys_member where role = name (tenant-scoped)
'team:id' → sys_team_member where team_id = id

via MEMBER_OBJECT = 'sys_member' / resolveRole and TEAM_MEMBER_OBJECT = 'sys_team_member' / resolveTeam. The test pins it: recipient-resolver.test.ts:105 is named "expands role: via sys_member (tenant-scoped) and de-dups" and asserts the query where equals { role: 'admin', organization_id: 'org_1' }.

The comment's own worked example is a further signal that it is not describing this code path: role:sales_manager is not a reachable sys_member.role value — mapMembershipRole in packages/spec/src/identity/eval-user.zod.ts normalizes owner / admin / member and nothing else — whereas sales_manageris the canonical example position name in packages/spec/src/identity/position.zod.ts.

The two readings

  1. The comment is simply wrong prose on a correct index, and should be corrected to name sys_member / sys_team_member. Cheap fix, no behaviour change.
  2. The comment records intended behaviour — role: resolving against positions — that RecipientResolver never implemented. Then the tenancy argument the index rests on is about a graph the code does not read, and the real question is which of the two should move.

The per-organization unique index is not in question under either reading: sys_member and sys_team_member rows are tenant-scoped too, so the index is right regardless of which object backs the selector.

Worth resolving because this comment is load-bearing for exactly the reader who comes looking: it is the most detailed prose in the repo about what a notification principal denotes, and it is what a docs or spec agent verifying the principal set will find first.

Verified on origin/main (ed4ca59).

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions