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

`work_item:triage` + `work_item:delete` — accepting an inbound request, and destroying a subtree, stop being the same permission as editing a field

Two keys whose operations all reach a gate today, so the guard reports nothing — and the gates are the wrong ones. This card is a re-pointing with three corrections the inventory does not carry, each read off origin/main.

work_item:triage — the moderation acts, not the submission

triageService gates with assertCanBrowse (4 sites) and assertCanEdit (2). The inventory maps both its routes to work_item:triage, and one of those rows is wrong: createSubmission is somebody SUBMITTING an inbound request, which the catalog already governs with public_request:submit — Plane draws exactly this line, letting a Guest or Commenter submit an intake item while only Admin and Contributor accept or decline one. Triage is what happens to a submission after it arrives.

  • acceptTriageItem, promoteTriageItem, declineTriageItem, markDuplicateTriageItem, snoozeTriageItem, unsnoozeTriageItemwork_item:triage.
  • getTriageQueue, getTriageQueueByKey, getTriageItemDetailwork_item:triage as well: the queue is a moderation surface, and its contents are requests from outside the team that have not been accepted yet.
  • createSubmission → stays where the catalog already puts submitting, and its inventory row is CORRECTED to public_request:submit.

work_item:delete — and the hard delete the inventory files under work_item:edit

  • workItemsService.archiveWorkItem / unarchiveWorkItem (assertCanEdit today) and getDeletePreview (assertCanManage today) → work_item:delete.
  • DELETE /api/work-items/[id] is mapped to work_item:edit in the inventory while its preview is mapped to work_item:delete. A destroy and its own dry run cannot be governed by different keys — the row is corrected to work_item:delete, and the service method that performs it moves with it. This is the third correction, and it is the one that matters: today the preview is admin-gated and the delete itself is looser.

Jira's default permission scheme grants Delete Issues to the Administrators project role, which is what docs/decisions/member-facing-permissions.md follows: a member keeps every edit and loses the cascade.

Acceptance criteria

  • Each triage moderation method asserts assertPermission(projectId, ctx, 'work_item:triage'); createSubmission keeps a submit-shaped gate and does NOT assert the triage key.
  • archiveWorkItem, unarchiveWorkItem, getDeletePreview and the hard-delete service method each assert work_item:delete.
  • A unit test proves a project member can still edit a work item and can no longer archive or delete one, and that a viewer can neither triage nor delete.
  • The two corrected inventory rows (triage/submissionspublic_request:submit, DELETE /api/work-items/[id]work_item:delete) each carry a one-line note saying what was corrected and why, so the change is not silent.
  • Both keys flip to enforcement: 'enforced' in lib/permissions/catalog.ts, with tests/permissions/catalog.test.ts's enforced list and length pin extended by exactly these two.
  • The guard's pinned counts are re-derived by running it on this branch — this card gates operations that already read as gated, so the PENDING figure may not move at all, and that is the expected result rather than a sign nothing happened.
  • pnpm test tests/permissions/ tests/services/ is green.

Context refs

  • lib/services/triageService.ts — the ten methods and their current assertCanBrowse / assertCanEdit split.
  • lib/services/workItemsService.tsarchiveWorkItem, unarchiveWorkItem, getDeletePreview, and the hard delete.
  • docs/decisions/permission-inventory.md — the R23 and R42 rows, plus the R41 row this card corrects.
  • docs/decisions/member-facing-permissions.md — the role assignment for both keys.
  • The role-set seam — the prerequisite that makes both keys resolve.
  • comment:moderate's shipped shape — the model story's precedent for a moderation key that is narrower than editing.