MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-351

6.4.2 Schema — role enum + `ProjectMembership` + `project.accessLevel` (migration-aware)

Done
Description

Estimate: 30m · Depends on: 1.2

The data model for project gating, in ONE migration. (a) Formalize the role set — a MemberRole enum { owner, admin, member, viewer }; keep WorkspaceMembership.role (migrate the existing string column: ownerowner, everything else→member). (b) A ProjectMembership { userId, projectId, role: MemberRole } join (unique [userId, projectId], RLS-forced + tenant-scoped like WorkspaceMembership; cascade on project/user delete). (c) A project.accessLevel enum { open, limited, private } @default(open) — existing projects backfill to open so no one is locked out.

What this does NOT do: the enforcement gate (6.4.3), the management API (6.4.4), any UI (6.4.5/6.4.6), or the seed (6.4.7). It also does not build a full permission-scheme matrix — project roles + access level are the model; the browse/edit policy is computed in 6.4.3.

Acceptance criteria

  • MemberRole enum + ProjectMembership + project.accessLevel @default(open) added in one Prisma migration; prisma migrate dev applies cleanly on a fresh DB and is idempotent.
  • Existing WorkspaceMembership.role values migrate into the enum (owner→owner, else→member); existing projects backfill to open (no lockout); ProjectMembership is RLS-forced + tenant-scoped (mirror WorkspaceMembership).
  • prisma generate types the new model/enums; a vitest (real Postgres) asserts a project defaults to open and a ProjectMembership round-trips under RLS.

Context refs

  • prisma/schema.prismaWorkspaceMembership (Story 1.2, the role string + RLS pattern to mirror) + Project
  • motir-core/CLAUDE.md — one migration, application-seeded data, RLS-forced tenant tables