2.9.9 Backend — split getDeletePreview into live vs archived descendant counts
Estimate: 40m · Type: code
Extend the delete-preview so the archived-item confirm modal (2.9.10) can warn about live (non-archived) descendants a cascade delete would also destroy. Motir's archive is single-node (workItemsService.ts:1469 — archiving a parent never archives its children), so an archived parent can still own non-archived LIVE descendants on the active boards/lists; deleteWorkItem cascades the whole subtree (workItemsService.ts:1560, findSubtree), permanently destroying those live items too. The preview must surface that live count.
Today getDeletePreview (lib/services/workItemsService.ts:1614) returns { totalCount, descendantCount, byKind } over the WHOLE subtree and does NOT split live vs archived (verified — lib/dto/workItems.ts:665). Add a live-(non-archived)-descendant count + per-kind breakdown (subtree rows where archivedAt IS NULL, EXCLUDING the root), leaving total/descendant/byKind unchanged (the total still counts every descendant — the design keeps the full cascade count and only ADDS the live split; do NOT filter the total).
- DTO (
lib/dto/workItems.tsWorkItemDeletePreviewDto): addliveDescendantCount: number+liveByKind: Partial<Record<WorkItemKindDto, number>>. - Repository (
workItemRepository): a single-op read counting the subtree's non-archived descendants by kind (thearchivedAtcolumn carries archive state). Keep it one Prisma op (4-layer). - Service (
getDeletePreview): compute the live split (root excluded), map into the DTO via the delete-preview mapper. SameassertCanManagegate (:1619). - Route
app/api/work-items/[id]/delete-preview/route.tsreturns the widened DTO (additive). Existing callers (fetchDeletePreview,DeleteWorkItemDialog) keep working.
Design: design/work-items/design-notes.md §2.9.7 ("Data note for 2.9.5") + delete-confirm.mock.html panels 6-7 (the warning the count drives). No UI in this subtask.
Acceptance criteria
WorkItemDeletePreviewDtocarriesliveDescendantCount+liveByKind; existing fields unchanged.- Archived parent with both live + archived descendants → correct live (non-archived) count + per-kind breakdown (root excluded); all-archived subtree → live count 0; leaf → 0.
- Still
assertCanManage-gated; one Prisma op per repo read. Service test covers live / all-archived / leaf; theworkItemsService/workItemRepository≥90% coverage gate holds.
Context refs
lib/services/workItemsService.ts:1614getDeletePreview,:1560deleteWorkItem/findSubtree,:1469single-node archivelib/dto/workItems.ts:665WorkItemDeletePreviewDto;lib/mappers/*(delete-preview mapper);lib/repositories/workItemRepository.tsfindSubtreeapp/api/work-items/[id]/delete-preview/route.ts- design:
design/work-items/design-notes.md§2.9.7,delete-confirm.mock.htmlpanels 6-7