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

motir-ai: GET /v1/lessons — list and detail for ONE project's tenant lessons, paged

The lessons live in motir-ai's database and motir-core has no way to read them: there is no /v1/lessons routegit grep '/v1/lessons' over src returns only the seed's import. This adds it.

Two shapes on the /v1 surface motirAiClient already talks to:

  • list — this project's lessons, lastOccurredAt descending, paged. The repository already defines the admin paging convention (ADMIN_PAGE_DEFAULT = 50, ADMIN_PAGE_MAX = 100); reuse it rather than inventing a second.
  • detail — one lesson, in full: title, body, why, howToApply, its axes, sourceRef, createdAt, lastOccurredAt, recurrence state.

Scoped to ONE project, at the query. The caller supplies the core project id and the route resolves it to an AiProject, exactly as getJob(jobId, coreProjectId) does. The WHERE is aiProjectId = <resolved>not the injection clause scope = 'global' OR aiProjectId = …, which would leak the shipped global corpus into a project's own list.

Global rows never appear here. That is the single assertion most worth writing: the natural instinct is to reuse the injection read, and the injection read is deliberately wider.

Return retired and expired lessons too — this is an inspection surface, not the injection path. What a project is no longer being taught is exactly what someone opening this screen wants to see; the read-time filters belong to injection.

Acceptance criteria

  • GET /v1/lessons returns one project's lessons, lastOccurredAt descending, paged on the existing admin convention.
  • A detail shape returns one lesson in full.
  • The project is resolved from the caller's core project id; a caller cannot ask for another project's lessons, asserted.
  • No scope = 'global' row is ever returned, asserted directly against a fixture holding both.
  • Retired and expired lessons ARE returned, and carry enough state for a client to render them differently.
  • The route follows the existing /v1 envelope, auth and error conventions — no new pattern.
  • Unit + integration tests against the real test database.

Context refs

  • motir-ai src/repositories/lessonRepository.ts — the row shape, ADMIN_PAGE_DEFAULT / ADMIN_PAGE_MAX, and the injection clause this must NOT reuse.
  • motir-ai src/routes/ — the /v1 surface, its envelope and auth.
  • motir-core lib/ai/motirAiClient.tsgetJob(jobId, coreProjectId), the project-scoping pattern to mirror.
  • MOTIR-3294 — the axes each row carries.