MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-798

2.8.7 Service + route — delete preview (subtree count + per-kind breakdown the confirm dialog reads)

Done
Description

Estimate: 35m · Type: code · Depends on: 2.8.2

The cascade-count READ the 2.8.4 delete-confirm dialog displays before the user confirms — the backend the 2.8.1 design-notes explicitly flagged as the out-of-scope cascade COUNT backend (2.8.x service) but that no 2.8.x subtask was ever created for. Found at run time when 2.8.4 (MOTIR-795) went to build — the run-time "X doesn't exist" gate; plan mistake logged in notes.html.

Add workItemsService.getDeletePreview(id, ctx) returning the recursive subtree shape the dialog needs: { totalCount, descendantCount, byKind } where descendantCount = subtreeSize − 1, totalCount = 1 + descendantCount (the Delete N items magnitude on the button), and byKind is the per-kind descendant breakdown (e.g. { subtask: 5, task: 1, bug: 1 }) — built over the existing workItemRepository.findSubtree (already returns each row's kind; no new CTE). Permission-gated identically to deleteWorkItem (the same delete/assertCanManage gate — a viewer who can't delete must not be able to probe the subtree), tenant-checked, typed WorkItemNotFoundError on missing / cross-workspace id. Add a DTO in lib/dto.

Thin HTTP read: GET /api/work-items/[id]/delete-preview/route.ts — a NEW sub-path (do NOT touch [id]/route.ts, which 2.8.3 / MOTIR-794 owns for the DELETE handler) → getWorkspaceContext()getDeletePreview → map typed errors (permission → 403, missing → 404). No business logic in the route (CLAUDE.md 4-layer).

Acceptance criteria

  • getDeletePreview returns { totalCount, descendantCount, byKind } over the FULL subtree (recursive, via findSubtree); per-kind breakdown correct; leaf item → descendantCount 0 / empty byKind.
  • Permission-gated like delete (assertCanManage); cross-workspace / missing → WorkItemNotFoundError.
  • GET /api/work-items/[id]/delete-preview returns the preview / 403 / 404; no business logic in the route.
  • Unit/integration coverage (cascade count, per-kind, permission, leaf, missing) — per-file coverage gate on touched core files.

Context refs

  • lib/repositories/workItemRepository.ts findSubtree (returns each row's kind); lib/services/workItemsService.ts deleteWorkItem (same gate + tenant check to mirror); app/api/work-items/[id]/rollup/route.ts (thin-GET pattern); design design/work-items/delete-confirm.mock.html + design-notes "cascade COUNT backend" out-of-scope slot. Blocks 2.8.4 / MOTIR-795.