Repository navigation
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.
observerjoinsassignableRoleslowest first;clerkRolesis gonetoOrgRolenow maps an unknown key toorg: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
roleOfreads known keys as before and still falls back to observer (roles.ts:56)- Invite default is the literal
org:memberatupdate.ts:259and:543, notorgRoleKeys[0] Schema.Literals(orgRoleKeys)accepts a pickedorg: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
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.