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

5.4.1 Schema — `label` + `work_item_label`, `component` + `work_item_component`, `watcher` (+ Epic-6 join-predicate contract, repos)

Estimate: 22m

The persistence for all three features. Schema + migration + repo skeletons only.

Labelid, workspaceId, projectId, name (display casing as first typed), nameLower (the case-insensitive uniqueness key: @@unique([projectId, nameLower]) — the wart-fix decision), createdAt. WorkItemLabelworkItemId (Cascade) + labelId (Cascade), @@unique([workItemId, labelId]), @@index([labelId]) (the Epic-6 by-label join). Labels are rows so the join is FK-clean, but they live ONLY while used (the service deletes a label row when its last join row goes — folksonomy semantics; no orphan-label GC needed by design).

Componentid, workspaceId, projectId, name + nameLower (@@unique([projectId, nameLower])), description?, defaultAssigneeId? (FK → User, SetNull — a departed user clears the default, never blocks), timestamps. WorkItemComponentworkItemId (Cascade) + componentId (Restrict — the service must run the move-or-remove flow before a component deletes; the DB backstops), @@unique([workItemId, componentId]), @@index([componentId]).

WatcherworkItemId (Cascade), userId (Cascade — a deleted user stops watching), createdAt, @@unique([workItemId, userId]), @@index([userId]) (the "issues I watch" read 5.7/6.x will want). Every FK a two-sided @relation (CLAUDE.md). The Epic-6 contract documented (schema comment + note): JOIN work_item_label/work_item_component ON … predicate sketches over the [labelId]/[componentId] indexes.

Repo skeletons (single-op, writes require tx): label find-or-create reads (findByNameLower, searchByPrefix(projectId, q, take)), join add/remove, countUsesByLabel; component CRUD + listByProject + join add/remove + countItemsByComponent + reassignItems(fromId, toId, tx); watcher add/remove/listByWorkItem(paged)/existsFor.

Acceptance criteria

  • The five models exist with the exact uniques/indexes/onDelete actions above (label joins Cascade; component join Restrict; defaultAssignee SetNull; watcher Cascade both sides); relations two-sided; prisma migrate dev re-run reports no drift.
  • The Epic-6 join-predicate contract is documented beside the 5.3.1 one.
  • Repo methods exist as single ops; Vitest verifies the uniques (case-insensitive label/component names), the Restrict backstop, the cascades, and empty-input guards (coverage gate).

Context refs

  • prisma/schema.prisma conventions + the 5.3.1 contract-comment pattern; motir-core/CLAUDE.md (FK rule, required-tx)
  • The verified mirror semantics in the Story 5.4 description (folksonomy delete-on-last-use; move-or-remove; watcher view-access)
  • lib/repositories/ conventions (workItemRepository paging shapes)