Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-2262Done

`projectAccessService.getPermissions` — resolve an actor's permission set for a project in one round trip, and its DTO

Give the rest of the product one way to ask "what may this actor do in this project?" and get a permission SET back, instead of asking a different capability method per domain. projectAccessService already carries five such methods — getCapabilities, getCommentCapabilities, getAttachmentCapabilities, getWatcherCapabilities, getSettingsCapabilities — each spending its own resolveInputs round trip to answer three or four booleans. This card adds the general read those specialise.

The shape

  • getPermissions(projectId, ctx, tx?) on lib/services/projectAccessService.ts — one resolveInputs call, then resolvePermissions, returning the actor's set. tx stays optional exactly as every sibling method has it, so a caller already inside a transaction shares the snapshot and the RLS workspace GUC.
  • getRoleCatalog(projectId, ctx, tx?) — the read the settings page renders: the built-in roles with their permission sets, plus the catalog's domain grouping. It is a project-scoped read (not a static import) because the custom-roles story makes the answer project-dependent, and a page that reads a constant today would need its data source replaced then.
  • A serialisable DTO in lib/dto/permissions.ts — the set as a sorted array of keys, plus the roles and domains, in the mapper-layer shape the repo's other DTOs follow (lib/mappers/). A Set does not cross the server/client boundary; an array that is sorted crosses it deterministically, which matters for render stability.
  • The five existing capability methods keep working unchanged. They are called from ~40 places and this card does not re-point them; where each already computes its booleans from the same inputs, it may compute them through hasPermission internally, but its return shape is untouched.

Acceptance criteria

  • projectAccessService.getPermissions and getRoleCatalog exist, take the optional tx in the same position as their siblings, and throw ProjectNotFoundError — never a 403-shaped error — for a project id in another workspace, preserving the shipped no-existence-leak posture.
  • getPermissions for an actor with no workspace membership on a non-public project returns the empty set, and for a workspace owner returns the full catalog.
  • The DTO in lib/dto/permissions.ts is JSON-serialisable, and its permission array is sorted by catalog order, not by insertion.
  • An integration test drives a real Postgres row set — a project with an actual ProjectMembership at each of the three roles — through getPermissions and asserts the resolved sets, rather than mocking resolveInputs.
  • The five existing capability methods' return shapes are unchanged, asserted by their existing tests continuing to pass untouched.
  • Per-file coverage on every new and changed module meets the ≥90% floor.

Context refs

  • lib/services/projectAccessService.tsresolveInputs, the five capability methods this generalises, and the optional-tx convention.
  • lib/dto/projectMembers.ts and lib/mappers/projectMemberMappers.ts — the DTO + mapper layering this follows.
  • lib/projects/errors.tsProjectNotFoundError, the cross-workspace posture.
  • The resolution cardresolvePermissions, which this wraps with IO.