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

motir-core: the create-lesson seam — motirAiClient method behind a permission-asserting service

motir-core's half of the create path: a motirAiClient method for POST /v1/lessons and a service function above it that asserts the permission and resolves the project.

The MCP tool built next is a thin adapter over this, the way add_comment is a thin adapter over commentsService.addComment — no business logic in the tool. Putting the gate here rather than in the tool is what keeps it true for the next caller: if the settings UI ever grows an add a lesson button, it goes through this same function and inherits the same check.

The permission is asserted before the upstream call, not after. A caller without it must never cause a request to motir-ai — a refusal that has already written a row upstream is not a refusal, and it spends a tenant's budget on a rejected request.

The lesson is tenant-scoped by construction. The service resolves the acting project from the caller's context and passes that; it does not take a project from the caller and trust it, and it has no parameter that could produce a global lesson.

motirAiClient is the file that carries the warning about this envelope being a cross-repo contract that fails closed and silently. The same discipline as MOTIR-3350 applies: field names in one place, and the test asserts on the serialized body rather than on the argument object.

Errors from upstream are surfaced, not swallowed. The near-duplicate refusal that MOTIR-3359 returns carries the existing lesson's id and title, and the caller needs both — a generic "could not create" turns an actionable answer into a dead end.

Acceptance criteria

  • A motirAiClient method for POST /v1/lessons, following the neighbouring methods' shape.
  • A service function above it that asserts the lesson-library-change permission before the client is called, and no upstream request is made on refusal — asserted by a test that fails if the request goes out.
  • The project is resolved from the acting context; there is no parameter through which a caller can name another project or ask for a global lesson.
  • The upstream near-duplicate refusal reaches the caller with the existing lesson's id and title intact.
  • A test asserts on the serialized request body, not the argument object, and the wire field names appear as literals in the test.
  • Nothing in this card renders UI or registers a tool.

Context refs

  • motir-core lib/ai/motirAiClient.ts — the client, its serviceAuth calls, and its warning about silent contract failure.
  • motir-core lib/permissions/resolve.ts — how a key is checked at a call site.
  • motir-core lib/services/ — the service layer an MCP tool adapts over; commentsService is the shape.
  • MOTIR-3359 — the route this calls, and the refusals it must pass through.
  • MOTIR-3336 — the permission key this asserts (via the story-level ordering on MOTIR-3329).
  • MOTIR-3337 — the read seam; this is its write sibling and should look like it.