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

`repository:manage` + `repository:manage_access` — the project's repository SET, and who may clone it (self-connect stays open)

A project has a repository SET — as many repos as its architecture needs, not one — and deciding what is in it is administrative. repository:manage gates the set: add a row, remove it, move it to another project, take it over, establish a real repo behind a proposed row. repository:manage_access gates the separate question of who on the team may CLONE the code.

Today /api/projects/[key]/repositories, .../[rowId]/move, .../[rowId]/takeover and .../establish reach their services with workspace membership alone, and .../[rowId] is on assertCanEdit — so a plain member can currently detach a project's repository. The code-access routes are ungated in the same way.

⚠️ Two boundaries this card must not cross.

  1. Self-connect stays open to a non-admin. lib/settings/projectSettingsNav.ts records the reason on the code-access entry: connecting your OWN GitHub identity is the one action nobody can take on your behalf (project-repository-set ADR §3 Q3), which is why that page is browse-gated. repository:manage_access gates granting or revoking access for other people; it must not gate a member connecting themselves. Read projectRepoAccessService method by method and say, in the PR, which side each one is on.
  2. A provisioning callback is not an actor. .../[rowId]/state reaches projectRepoSetService.attachRealizedRepo. If that path is driven by a job or a provider callback rather than a person, it is a no-gate row in the inventory (the serviceAuth family), not a repository:manage one. Determine which before gating it — putting a project permission in front of a machine caller breaks provisioning silently, and it fails in the environment where nobody is watching.

Acceptance criteria

  • Enumerate first: run the guard on the branch for both keys, and git grep -ln "projectRepo" -- lib/services to reach every service in the family. The PR body carries operation / gate today / gate after for every row in the inventory's repository section, including the ones this card decides NOT to move.
  • Every repository-SET write — add / remove / move / takeover / establish, wherever they live across projectRepoSetService, projectRepoEstablishService, projectRepoTakeoverService and their siblings — asserts repository:manage through projectAccessService.assertPermission, threading tx where the method already runs inside one.
  • Granting or revoking ANOTHER member's code access asserts repository:manage_access. A member connecting their own identity does not, and a test asserts that directly.
  • Reads — the repositories view, the establish view, the code-access table — keep a browse-level gate. The Repositories and Code access nav entries stay browse-gated; a member still SEES where the code lives.
  • .../[rowId]/state is classified by who calls it, and its inventory row records the answer with the evidence.
  • Both keys flip to enforcement: 'enforced' in lib/permissions/catalog.ts. (repository:connect is retired separately; if that card has not landed, do not touch its key here.)
  • The inventory's repository rows carry corrected gate-today / gate-after cells and move newexisting for the ones this card wires.
  • Tests: an admin adds, moves, takes over and detaches a repository row and grants a teammate code access; a project member is refused 403 on each; a member can still view the set and still connect their own identity; a non-browser gets 404.
  • The guard's pending pin is re-derived by running the suite on the branch.
  • pnpm test green; pnpm lint and the prettier check pass repo-wide.

Context refs

  • lib/services/projectRepoSetService.ts · projectRepoEstablishService.ts · projectRepoTakeoverService.ts · projectRepoAccessService.ts · projectRepoPinService.ts · projectRepoProvisioningService.ts · projectRepoProposalService.ts · projectRepoRoomService.ts — the family; the enumeration decides which of them this card actually touches.
  • app/api/projects/[key]/repositories/** — the set routes plus access and access/team.
  • lib/settings/projectSettingsNav.ts — the repositories and code-access entries and the ADR reasoning recorded in their comments (read only; this card does not edit the nav).
  • docs/decisions/project-repository-set.md — §3 Q3 (self-connect) and the repo-SET model.
  • docs/decisions/permission-inventory.md — the repository section; reasons R21, R22.
  • The seam this calls · the repository:connect retirement.