Allow moving tasks between parents in task_update and task_patch #243

doneCreated by Claude Code (User hu_yMavF3p1) • Unassigned • Normal priority

Description

Tasks could only get a parent at creation. Re-parenting meant cancel plus recreate, which burns the project-local number that memories, relations and cross-agent references point at via #N.

Design decision and full rationale: #241.

Contract as delivered

  • task_update: parentTaskNumber is required with no default, so omitting it is a schema error rather than a silent move to top level. Deliberate top level is an explicit null. Tasks have no revision history, so an accidental unparent would be unrecoverable and would drag the whole subtree along.
  • task_patch: parentTaskNumber is a normal explicit field — omitted preserves, null moves to top level, a number moves under that parent.
  • Both tools report the new parent. Previously the number came from the state read before the change and was wrong after a move.

Validation on a move

TaskService.RequireMovableParent rejects a parent from another project, a closed parent, the task itself, and any of its own descendants; the ancestor walk terminates because the tree is acyclic. The version check locks only the moved task; a concurrent close of the target relies on the Serializable transaction. Depth stays unbounded and rollups stay computed.

Resolved

The ProposalService caveat is gone: task proposals are being removed entirely (#247), because tasks are work organization rather than reviewed knowledge (#238). No move needs to pass proposal validation.

The parallel-session blocker is also gone — that work reverted its own TaskService change and the task routing it had added to TaskTools was withdrawn.

Delivered

Commit Change
6d6df1d RequireMovableParent, TaskService.Update takes the parent, TaskPatchChanges gains HasParent, a parent-only patch stops being a no-op.
83beeef parentTaskNumber on task_update (required) and task_patch, both echoing the new parent; service and MCP coverage plus the inputSchema.required check.

Still open: no parent picker in TaskEditor.razor — the UI keeps preserving the current parent.

Connection interrupted or session ended. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.