Skip to content

feat(policy): add the observer role below member - #2543

Open
Makisuo wants to merge 1 commit into
mainfrom
observer/policy
Open

Makisuo wants to merge 1 commit into
mainfrom
observer/policy

feat(policy): add the observer role below member

bdcd2bc
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review completed Nov 12, 2026 in 1m 40s

Confidence 6/10 · 1 issue to address

🟡 Confidence 6/10 · needs attention. Held at 6 because a correctness warning is open.
1 warning −1 · tests covered · risk high −1

Adds observer below member, makes an unknown role string read as observer instead of member, and rewords the member denial. The policy ladder itself is sound; the Members page's existing role reader reports an observer as a member.

  • observer joins Role and RANK below member under key org:observer
  • roleOf answers observer for any role string it does not know
  • highestRole keeps the member floor only for an empty role set
  • holdersPhrase for a member permission names admins, engineers and members

Before merge

  • Manual · Create the org:observer role in the Clerk development and production instances, or roleOf will never see the key

Findings

🟠 Warning · F1 · An observer member renders as Member in the Members page and cannot be promoted to member

correctness · packages/domain/src/policy/roles.ts:46-54

roleOf("org:observer") now answers observer, but observerRole stays out of assignableRoles, so entryOf (apps/web/src/page/settings/kit/org-membership.ts:13) finds no entry and falls back to the member one: the row reads "Member" with org:member preselected for someone who holds no grants. Picking Member then equals toOrgRole(member.role), so members/update.ts:585 drops it as a no-op.

Let `org-membership` resolve the observer entry (label "Observer") while keeping it out of `ROLE_ENTRIES`, so the row is not reported as a member and a Member pick still saves.
What was checked
  • No production caller passes bare role names to Policy.can; sessions, keys and device credentials carry Clerk keys (packages/auth/src/index.ts:780)
  • holdersPhrase is only fed denial.required from permissions.ts, never a session's own role, so the reworded message is correct
  • The empty-set member floor is tested (policy.test.ts:29) and the diff adds no untraced production work

bdcd2bc · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

Annotations

Check warning on line 54 in packages/domain/src/policy/roles.ts

See this annotation in the file changed.

@maple-review-bot maple-review-bot / Maple / review

correctness: An `observer` member renders as Member in the Members page and cannot be promoted to member

`roleOf("org:observer")` now answers `observer`, but `observerRole` stays out of `assignableRoles`, so `entryOf` (`apps/web/src/page/settings/kit/org-membership.ts:13`) finds no entry and falls back to the member one: the row reads "Member" with `org:member` preselected for someone who holds no grants. Picking Member then equals `toOrgRole(member.role)`, so `members/update.ts:585` drops it as a no-op.