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

`ai:configure` — the project's AI-planning cadence and planner model (the settings write only, not the planner itself)

The smallest of the domain cards and the cleanest re-pointing in the story: projectAiSettingsService already asserts assertCanManage on its writes, and /api/projects/[key]/ai-settings is already assertCanBrowse on GET and assertCanManage on PATCH. All that changes is which key the write asks for — ai:configure instead of the umbrella.

Its whole value is the boundary it draws. ai:configure is "who may change the auto-plan cadence, the AI sprint-planning switch, the planner model and the drafted-explanation setting" — a settings decision with a spend consequence. It is NOT "who may run the planner", which is ai:plan and is MOTIR-2291's: those are ~26 routes, currently governed by nothing, and moving them changes what an ordinary member can do. Keeping the two apart is why the catalog has two keys.

The UI is out of scope. app/(authed)/settings/project/ai-planning/page.tsx and _components/AiPlanningSettingsEditor.tsx both read canManage to render the panel read-only for a non-admin. That is an affordance decision and belongs to the permission-gated UI story — leave both alone, and leave the nav entry browse-gated, so a member keeps seeing the configuration read-only exactly as today.

Acceptance criteria

  • Every write in lib/services/projectAiSettingsService.ts asserts ai:configure through projectAccessService.assertPermission; the read keeps its browse gate.
  • git grep -n "assertCanManage\|canManage" -- lib/services/projectAiSettingsService.ts "app/(authed)/settings/project/ai-planning" first, and the PR body states, per hit, whether it moved and why. The two client-side canManage reads must appear in that list as deliberately NOT moved.
  • ai:configure flips to enforcement: 'enforced' in lib/permissions/catalog.ts.
  • The /api/projects/[key]/ai-settings row in the inventory's ai section has its gate-after corrected and moves newexisting. No other row in that section is touched — every ai:plan and ai:view_plan row is MOTIR-2291's, and a diff here that moves one fails this card.
  • Tests: an admin saves the AI-planning settings; a project member is refused 403; a member can still READ them (the panel renders read-only); a non-browser gets 404.
  • The guard's pending pin is re-derived by running the suite on the branch (this card is expected to move it by zero — the route is already gated — and the PR says so rather than leaving the reader to wonder).
  • pnpm test green; pnpm lint and the prettier check pass repo-wide.

Context refs

  • lib/services/projectAiSettingsService.ts — the three assertCanManage calls.
  • app/api/projects/[key]/ai-settings/route.ts — GET browse, PATCH manage.
  • app/(authed)/settings/project/ai-planning/page.tsx · _components/AiPlanningSettingsEditor.tsx — the two client-side canManage reads this card leaves alone.
  • lib/settings/projectSettingsNav.ts — the ai-planning entry, browse-gated on purpose; read only.
  • docs/decisions/permission-inventory.md — the ai section; reason R17 (this card) versus R5 and R11 (not this card).
  • The seam this calls · the story that takes ai:plan.