Skip to content

feat(members): offer the observer role on invite and role change - #2549

Open
Makisuo wants to merge 1 commit into
observer/agentsfrom
observer/members
Open

Makisuo wants to merge 1 commit into
observer/agentsfrom
observer/members

feat(members): offer the observer role on invite and role change

cfd9725
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review succeeded Nov 12, 2026 in 1m 34s

Confidence 10/10 · No issues found

🟢 Confidence 10/10 · safe to merge. Only an info note on a positional lookup; the role list, picker and prompt text are pinned by new tests.
1 note · tests covered · risk medium

Moves the observer role into Policy.assignableRoles so the Members page offers it in the invite picker, the row picker and the role-change prompt, and makes an unknown Clerk role key display as Observer. Safe to merge once the Clerk role exists in production.

  • observer joins assignableRoles lowest first; clerkRoles is gone
  • toOrgRole now maps an unknown key to org:observer
  • Invite default stays org:member

Before merge

  • Manual · Create org:observer (name "Observer", no system permissions, in the default role set) on the production Clerk instance
  • Manual · Confirm production has no Clerk role keys beyond admin, member and engineer: any other key now reads, and resends, as observer

Findings

🔵 Note · F1 · memberEntry is looked up by array position, not by role

maintainability · apps/web/src/page/settings/kit/org-membership.ts:11

Policy.assignableRoles[1] hard-codes member as second in a list the same module defines as rank-ordered; this change already shifted every role down one. A role inserted between observer and member would make a root membership read as that role, and its row picker would then write it.

`const memberEntry = Arr.findFirst(Policy.assignableRoles, (entry) => entry.role === "member")`, and return `Option.getOrThrow(memberEntry)` from `entryOf`'s fallback.
What was checked
  • roleOf reads known keys as before and still falls back to observer (roles.ts:56)
  • Invite default is the literal org:member at update.ts:259 and :543, not orgRoleKeys[0]
  • Schema.Literals(orgRoleKeys) accepts a picked org:observer (model.ts:15, update.ts:580)

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

Annotations

Check notice on line 11 in apps/web/src/page/settings/kit/org-membership.ts

See this annotation in the file changed.

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

maintainability: `memberEntry` is looked up by array position, not by role

`Policy.assignableRoles[1]` hard-codes member as second in a list the same module defines as rank-ordered; this change already shifted every role down one. A role inserted between observer and member would make a `root` membership read as that role, and its row picker would then write it.