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

The nine rows no project permission can govern — the importer OAuth legs, the pre-auth idea draft, and the canvas layout

Nine operations the inventory maps to import:run or ai:plan resolve no project, so no project permission can ever govern them. This is the repository:connect retirement one story over: a key pointed at an operation that never names a project is the lie the catalog's opening rule forbids, and a count that includes those rows sends every card underneath looking for gates that cannot exist. So this card runs first and gives each row a DECIDED disposition, before anything downstream is sized against it.

Read on origin/main (44e55eff), one file each:

RowsWhat the code doesDisposition
/api/import/{jira,linear,plane}/oauth/{start,callback} (6)resolveWorkspaceContext(req) and nothing else — the 3LO round trip binds the actor's stored provider credential to a WORKSPACE; app/api/import/jira/oauth/start/route.ts never mentions a projectworkspace-scoped / R3, exactly as MOTIR-2294 decided for the GitHub and GitLab legs
/api/idea-draft, /api/idea-draft/[id]/claim (2)the PUBLIC cross-origin pre-auth receiver — origin-allowlisted, per-IP rate-limited, "deliberately no getSession() call" in its own header; the claim leg binds a draft to the signing-in user, still with no projectuser-scoped (the claim) / no-gate (the anonymous POST), each argued in the Reasons section
/api/canvas-layout (1)getActiveProject() then a per-user, per-project node arrangement — the actor's own view state inside a project they already have open, not a planning act that spends anythingre-map to project:browse and ADD the assert, so it stops being ungated without pretending it is an AI operation

Nothing here weakens a gate: eight rows are re-decided as operations a project role was never the right axis for, and the ninth gains a gate it does not have today. The importer's project-scoped operations stay import:run and are wired by their own card; attaching an imported project's work is not what the OAuth leg does.

Acceptance criteria

  • docs/decisions/permission-inventory.md carries the new decision for each of the nine rows, and each cites a Reason in the Reasons section — a new reason is added where none of the existing ones fits, rather than stretching one.
  • The six importer OAuth rows read the same decision + reason as the GitHub / GitLab legs MOTIR-2294 re-decided, and the document says why the two sets are the same shape.
  • canvasLayoutService.getLayout and savePositions each assert projectAccessService.assertPermission(projectId, ctx, 'project:browse'), and a unit test drives a non-browsing actor into the typed refusal.
  • The import:run and ai:plan rows the inventory still maps are re-counted in the document's own totals, and the counts in tests/permissions/noUngovernedOperation.test.ts are re-derived by RUNNING it on the branch, not by arithmetic on this card — the pinned PENDING figure falls by exactly the rows this card moves plus the one it gates.
  • pnpm test tests/permissions/ is green, and the guard's "every pending operation names the permission that will govern it" assertion still holds for every remaining row.
  • No permission key is added, removed or re-labelled in lib/permissions/catalog.ts.

Context refs

  • docs/decisions/permission-inventory.md — the rows and the Reasons section this card edits.
  • tests/permissions/noUngovernedOperation.test.ts — the pinned PENDING count and the PERMANENTLY_UNGATED decision set.
  • app/api/import/jira/oauth/start/route.ts, app/api/import/jira/oauth/callback/route.ts — the workspace-context resolution to read before writing the disposition.
  • app/api/idea-draft/route.ts — the pre-auth receiver's own header comment.
  • app/api/canvas-layout/route.ts, lib/services/canvasLayoutService.ts — the one row that gains a gate.
  • MOTIR-2294 — the precedent: what a retirement-by-disposition looks like in this document.
  • The operation inventory — where the original mapping came from.