DECISION — which built-in role holds each of the eight member-facing keys, and what a workspace member with no project membership keeps
The question. For each of the eight member-facing keys, which built-in role holds it — admin, member, viewer — and does the ACCESS LEVEL subtract it the way project:administer is subtracted, or does it need its own branch? Every answer takes a capability away from somebody who has it today, because the shipped answer for all eight is "anyone signed in". It is settled ONCE, here, against the mirrors and the shipped access model — never per-route while wiring, where each card would answer it alone and the eight answers would not agree.
The deliverable is docs/decisions/member-facing-permissions.md, in the shape of the existing records in docs/decisions/ — the question, the options considered, the decision per key with its evidence, and what it gates.
The recommendation this card records, and the evidence behind each row
Rung 1 is the mirror; rung 2 is this codebase. The recommendation is what the evidence supports, not what is least disruptive.
| Key | Role set | Rung-1 evidence | Who loses what |
|---|---|---|---|
sprint:manage | admin + member | Plane: Admin and Contributor create and manage cycles; Commenter and Guest cannot. Jira team-managed: Member. Jira company-managed makes Manage Sprints a SEPARATE permission typically held by Scrum Masters — narrowing it further is exactly what custom roles will allow, on top of this default | a viewer loses starting, completing and re-ranking |
report:view | admin + member + viewer | Jira has NO separate report permission: Browse Projects is what governs reports and other aggregated reads. Plane's analytics follow project visibility | nobody who can browse; a non-member on a private project already cannot browse |
saved_filter:manage | admin + member | a saved filter is authored content with a star and a subscription — creating one is a write, and Jira treats filter creation as a logged-in user capability while sharing is separately governed | a viewer loses authoring and subscribing, keeps reading results |
import:run | admin only | Plane: only workspace admins can perform imports, to maintain governance. Linear: you must be a Linear Admin to run an import | a member loses running an import — the largest single revocation in this story |
work_item:delete | admin only | Jira's default permission scheme grants Delete Issues to the Administrators project role | a member loses archiving and the delete cascade, keeps editing every field |
work_item:triage | admin + member | Plane: Admin and Contributor accept / decline / snooze an intake item; Guest and Commenter may only SUBMIT | a viewer loses accepting and declining, keeps reading the queue |
ai:plan | admin + member | Motir's own ai:configure already put the AI SETTINGS behind admin; the mirrors put AI configuration at admin and usage at member. A planning job spends the workspace's credits, so it is a write with a bill | a viewer loses running the planner |
ai:view_plan | admin + member | approving a generated plan MATERIALIZES work items — it is a create, not a read, and Motir already gates the review surface's writes with assertCanEdit | a viewer loses approve / decline / proposal edit |
The two structural sub-questions, which are the ones a per-route answer would get wrong
- The implicit workspace member.
IMPLICIT_WORKSPACE_MEMBER_PERMISSIONSinlib/permissions/builtinRoles.tsis what a workspace member holds on a project they hold NO membership in — todayproject:browse,work_item:edit,comment:add,attachment:create. The recommendation is that it gainsreport:viewand NOTHING else. A stranger to the project may read its charts because they may already read its work items; they may not spend the workspace's AI credits, run an importer, restructure a sprint or delete a subtree on a project nobody put them on. This is the one placeai:planatmemberis not the whole answer, and it is invisible from any single route. levelGrantsbranches.lib/permissions/resolve.tsnames onlywork_item:edit/comment:add/attachment:create; every other key takes the default arm, which is what made the administrative split provably behaviour-neutral. The recommendation is to add NO new branch. Each of the eight then behaves exactly asproject:administerdoes per level — kept onopen/public, kept onlimited, and onprivategated on project membership alone — and the whole capability question stays in the ROLE sets, where it is readable in one place. A key that genuinely needs a per-level rule would be a second policy axis, and the file says so in its own comment.
Acceptance criteria
docs/decisions/member-facing-permissions.mdexists, follows the shape of a sibling record indocs/decisions/, and states for EACH of the eight keys: the decision, the mirror evidence with its source, and the capability that is revoked on the day it lands.- It answers both structural sub-questions explicitly — the implicit-workspace-member set, and whether
levelGrantsgains a branch — with the reasoning, not just the verdict. - It names, per key, the actor who can no longer perform the operation, so the revocation list can be read without opening any other file.
- It records the ONE loosening in the story (
/api/ai/coding-convention/*, admin-gated today and mapped toai:plan) as a decision in its own right: either the inventory mapping is corrected or the widening is intended, and the document says which and why. - Every claim about the shipped model cites the file it was read from on
origin/main—lib/permissions/builtinRoles.ts,lib/permissions/resolve.ts,lib/services/projectAccessService.ts— and no claim about a mirror is made without its source link. - The record names the cards it gates, so a reader arriving at it from a wiring card can see what it decided for them.
Context refs
lib/permissions/builtinRoles.ts—ROLE_GATED_PERMISSIONS,BUILTIN_ROLE_PERMISSIONS,IMPLICIT_WORKSPACE_MEMBER_PERMISSIONS.lib/permissions/resolve.ts—resolvePermissionsand thelevelGrantstable this decision chooses not to touch.docs/decisions/permission-inventory.md— the operation-to-key mapping being decided over.docs/decisions/public-projects.md— the sibling record's shape, and the level-gated grants a role can neither hold nor withhold.- The permission MODEL and the administrative split — the two stories whose answers this one must not contradict.
ai:configure— the already-decided admin half of the AI domain.- Mirror sources to cite: Plane member roles, Plane permissions matrix, Plane's Jira importer, Linear's importer, Jira company-managed permissions, Jira sprint permissions.