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-2349Done

The eight keys enter the built-in role sets — and the 64-row parity table records exactly what each one takes away

The seam every wiring card in this story stands on, and the twin of assertPermission's card one story over. projectAccessService.assertPermission(projectId, ctx, key, tx?) already ships, so no new gate mechanism is needed — but a key that is in the catalog and in NO role set resolves to nobody, so a wiring card that lands before this one refuses every actor including the project admin. This card puts the eight keys into the sets the decision assigns them to, and does nothing else.

Adding a key to a role set is inert until a gate consults it — the twelve administrative keys sat in ROLE_GATED_PERMISSIONS for the whole of the split while their domains were wired one at a time. So this card changes no behaviour by itself, and its whole value is that the capability change becomes VISIBLE in one place: the parity truth table.

The shape

  • All eight join ROLE_GATED_PERMISSIONS — a key outside it is not role-holdable at all, and the workspace-manager always-pass rail resolves to exactly that array.
  • Each joins the BUILTIN_ROLE_PERMISSIONS sets the decision assigns: admin takes all eight; member takes the ones the decision puts at member; viewer takes only what the decision puts at viewer.
  • IMPLICIT_WORKSPACE_MEMBER_PERMISSIONS takes exactly what the decision says and nothing more — it is a separate constant on purpose, and reusing member's set here is the mistake the file's own comment warns about.
  • levelGrants in lib/permissions/resolve.ts is NOT touched unless the decision explicitly calls for a branch. The file states why: naming a key there breaks the per-level equivalence the parity table proves.
  • enforcement in lib/permissions/catalog.ts stays planned for all eight. It flips to enforced on the card that actually wires that key's operations — the seam that lets naming and wiring land separately, and the reason tests/permissions/catalog.test.ts can pin the count honestly.

The parity table is the deliverable, not a chore

tests/permissions/accessParity.test.ts writes out all 64 combinations of access level × workspace role × project role by hand, deliberately not derived. For the administrative split it proved the twelve keys resolved identically to project:administer for every actor. Here it proves the opposite kind of thing, and that is the point: each row that changes is a capability somebody loses, and the diff of this file is the complete, reviewable list of them.

Acceptance criteria

  • ROLE_GATED_PERMISSIONS holds all eight keys, and BUILTIN_ROLE_PERMISSIONS / IMPLICIT_WORKSPACE_MEMBER_PERMISSIONS hold exactly the subsets docs/decisions/member-facing-permissions.md assigns — the file is the source, and any divergence from it is a bug in this card, not a judgement call.
  • The 64-row table in tests/permissions/accessParity.test.ts is updated by hand, row by row, and every row whose expectation changes carries a comment naming the key and the actor whose capability moved.
  • A test asserts the workspace-manager rail holds all eight on every access level (the existing ROLE_GATED_PERMISSIONS loop covers this once they are in the array — confirm it does rather than assuming).
  • A test asserts a project viewer does NOT hold the keys the decision withholds, and that IMPLICIT_WORKSPACE_MEMBER_PERMISSIONS grew by exactly the keys the decision names.
  • lib/permissions/catalog.ts is unchanged apart from comments: PLANNED_PERMISSIONS still returns all eight, and ENFORCED_PERMISSIONS still has the length catalog.test.ts pins, because no gate consults a new key in this PR.
  • The comment block in builtinRoles.ts that reads "Today that leaves MOTIR-2291's eight member-facing keys outside" is corrected to describe what this card did.
  • pnpm test tests/permissions/ is green.

Context refs

  • lib/permissions/builtinRoles.ts — the three sets this card edits, and the comment that documents the twelve-key precedent.
  • lib/permissions/resolve.tslevelGrants, and its warning about adding a branch.
  • tests/permissions/accessParity.test.ts — the 64-row table, hand-written on purpose.
  • tests/permissions/catalog.test.ts — the ENFORCED_PERMISSIONS length pin this card must NOT move.
  • assertPermission and the twelve administrative keys — the shipped precedent for this exact move.
  • The decision — the assignment this card transcribes.