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

The OPERATION INVENTORY — enumerate every gated operation in the product and map it to a permission

Read the shipped code and produce the authoritative map from every user-initiated operation in motir-core to the permission that should gate it — as a document, with a decided policy per operation. This card produces the MAP and nothing else; growing the catalog to it is the next card, and re-pointing the enforcement is the per-domain split.

This card runs FIRST in the story. The design cannot be drawn until the permission list exists, and everything downstream of the design inherits that.

Why this card exists (rung 2 — measured on origin/main, 2026-08-05)

The catalog card derived its eleven keys from the eleven predicates in lib/projects/access.ts, on the stated principle that "a key with no enforcement point behind it is a promise the product cannot keep." That principle is right and stays. What it silently assumed is that the eleven predicates are the product's enforcement surface. They are not:

  • 241 API routes; 122 services, of which only 40 reach a project-access gate at all.
  • assertCanManage has ~50 call sites and is ONE boolean over members, workflow, board, fields, components, estimation, automation, repositories, AI planning and code access — the epic's own description already says this.
  • Whole product areas resolve only getWorkspaceContext() / getSession() and never consult a project permission:
    • POST /api/dashboards — its own comment reads "create a dashboard (any workspace member)".
    • GET /api/reports/* — workspace context only.
    • GET /api/plans/[id] — workspace context only.
    • POST /api/work-items/[id]/ai/plan — session only.
  • So the catalog is not merely short; for these areas there is no project-level policy to transcribe. Somebody has to decide what the policy IS, per operation, before a key can honestly exist.

Creating a dashboard, AI planning, the git / repository paths, code health, viewing a plan — none of them are expressible in the model today.

What to produce

  1. docs/decisions/permission-inventory.md. One row per user-initiated operation: the route or Server Action, the service method behind it, the gate it enforces TODAY (assertCanBrowse / assertCanEdit / assertCanManage / workspace-only / session-only / none), the domain it belongs to, and the permission key it SHOULD map to. Derived by walking app/api/**/route.ts, the 'use server' actions and lib/services/*read the code, do not recall it.
  2. A decided policy per ungoverned operation, with its reasoning. Three answers are all legitimate and each must be written down rather than left blank:
    • a new permission key (most of them),
    • "workspace-scoped, not project-scoped" — a dashboard may genuinely belong to the workspace, and that is a product decision, not an oversight,
    • "no gate is correct" — a background job, meter or sweep has no actor.
  3. The DOMAIN grouping the keys fall into, since the domain set is what the grid's information architecture is built on.
  4. The SCALE handoff. Report on this card: total permissions, total domains, the largest domain's row count, and how many keys are enforced today vs pending enforcement. The design is blocked on this card precisely because those four numbers decide its layout — whether groups collapse, whether the header sticks, whether it needs search.

Scope boundary

In: the document, the per-operation decisions, the domain grouping, the scale report.

Out: touching lib/permissions/catalog.ts (the next card), the no-ungoverned-operation guard (the card after), and any enforcement re-pointing (the split). This card writes Markdown and changes no runtime code — so its branch is docs/MOTIR-2274-… and CI skips E2E.

Acceptance criteria

  • docs/decisions/permission-inventory.md exists and covers every app/api/**/route.ts and every 'use server' action in the repo — asserted by a test that enumerates the filesystem and fails on an operation the document does not name, so the coverage cannot drift as routes are added.
  • Every row carries: the operation, its service method, its gate TODAY, its domain, and its target permission key.
  • Every row whose target is "none" or "workspace-scoped" carries a written reason. No row is blank.
  • The five surfaces this card was filed over — creating a dashboard, AI planning, the git/repository paths, code health, and viewing a plan — each appear with a decided policy.
  • The document names the domain set the keys group into, and that set is stated as the input the design's information architecture is built on.
  • The scale report (permissions / domains / largest domain / enforced-vs-pending) is posted as a comment on this card, so the design unblocks with numbers rather than a guess.

Context refs

  • app/api/**/route.ts — the 241 routes to enumerate.
  • lib/services/*.ts — the 122 services; 40 carry a gate, and the other 82 are the question this card answers.
  • lib/projects/access.ts — the eleven predicates the first catalog was derived from.
  • lib/permissions/catalog.ts — the eleven keys this map will grow (in the NEXT card, not this one).
  • docs/decisions/public-projects.md — the precedent for how an access decision is recorded in this repo.