Add parent move validation and wiring in TaskService #244
Description
Service layer for #243. Self-contained: TaskService.cs is not touched by the parallel rebuild.
Work
RequireMovableParent(db, item, parentTaskId)— same project, active parent, no self-parent, no descendant. The descendant check walks ancestors of the new parent and rejects on reaching the moved task; the existing tree is acyclic, so the walk terminates.- Call it from
UpdateandPatch, but only when the parent actually changes. TaskService.Updatetakesint? parentTaskIdas a required positional parameter afterpriority, and assignsitem.ParentTaskId.TaskPatchChangesgainsHasParent/ParentTaskId, appended with defaults so positional construction elsewhere keeps compiling.Patchapplies the move and stops treating a parent-only patch as a no-op — extend the early-return guard.
Callers to fix so the solution builds
TaskTools.cs:141, TaskEditor.razor:45 (pass the current ParentTaskId to preserve today's behaviour), RelationTests.cs:26, SecurityTests.cs:653.
Leave PatchInTransaction alone — that is the proposal path, and a parent-only patch cannot reach it.
A finished version of this existed as a scratchpad patch in the planning session. If it is gone, rebuild from #241 — it is roughly 25 lines.