92 lines
5.8 KiB
Markdown
92 lines
5.8 KiB
Markdown
---
|
|
status: shipped
|
|
created: 2026-07-18
|
|
owner: kandev
|
|
---
|
|
|
|
# Subtask detachment
|
|
|
|
## Why
|
|
|
|
Users can create and reorganize task trees, but they cannot promote an existing subtask to a top-level task from the task menus. Clearing only the hierarchy relationship is also unsafe for subtasks that inherit their parent's materialized workspace because future launches would lose the sharing policy.
|
|
|
|
## What
|
|
|
|
- A user can detach a subtask from its parent from the sidebar task context menu and the task card's three-dot and context menus.
|
|
- The action is shown only for tasks with a parent and requires confirmation.
|
|
- Confirmation explains that detachment changes task hierarchy only and that a workspace shared with the parent remains shared.
|
|
- Detachment clears the task's parent relationship without changing its workflow, workflow step, state, blockers, descendants, repositories, sessions, or agent execution.
|
|
- A detached task becomes a top-level root of its existing subtree. Its descendants remain attached to it.
|
|
- A task using `inherit_parent` workspace mode changes to `shared_group` mode when detached. Its workspace-group membership remains active, so current and future sessions continue using the same materialized workspace.
|
|
- Tasks already using `shared_group` or `new_workspace` keep that workspace mode.
|
|
- Detaching an already-root task is idempotent and succeeds without changing the task.
|
|
- Choosing `No parent` in the Office parent picker uses the same detachment behavior.
|
|
- Successful detachment is reflected in all connected board, sidebar, and task-detail views without a reload.
|
|
|
|
## Data model
|
|
|
|
Detachment updates existing persisted fields; it adds no table or column.
|
|
|
|
- `tasks.parent_id` becomes the empty string.
|
|
- `tasks.metadata.workspace.mode` changes from `inherit_parent` to `shared_group` when applicable.
|
|
- `task_workspace_group_members` is unchanged. Active membership remains the durable source of shared workspace access.
|
|
- Existing blocker rows, task-session rows, task-environment rows, and descendant `parent_id` values are unchanged.
|
|
|
|
The hierarchy update and workspace-mode update are persisted in the same task-row update.
|
|
|
|
## API surface
|
|
|
|
`POST /api/v1/tasks/:id/detach`
|
|
|
|
- Request body: none.
|
|
- Success: `200` with the updated task DTO. An already-root task returns its unchanged DTO.
|
|
- Missing task: `404`.
|
|
- Persistence failure: `500`; no successful response is returned.
|
|
|
|
On success, the backend publishes `task.updated`. Its payload includes `parent_id` even when cleared so clients can remove a previously cached parent relationship. When metadata changes, the payload includes the updated workspace policy.
|
|
|
|
## State transition
|
|
|
|
The operation has one transition:
|
|
|
|
`child(parent_id != "")` -> `root(parent_id = "")`
|
|
|
|
If workspace mode is `inherit_parent`, the same operation also transitions it to `shared_group`. No task, session, workflow, or executor lifecycle state changes.
|
|
|
|
## Failure modes
|
|
|
|
- If the task no longer exists, the UI keeps its current state and shows the request error.
|
|
- If persistence fails, neither the UI nor API reports success. The task remains attached according to durable state.
|
|
- Repeated submissions are safe because detaching an already-root task is a no-op.
|
|
- Detachment does not create, copy, move, clean, or delete workspace files.
|
|
|
|
## Persistence guarantees
|
|
|
|
The cleared parent relationship and normalized workspace mode survive backend restarts. Existing workspace-group membership and materialized workspace ownership remain unchanged and follow the existing cleanup lifecycle for the group.
|
|
|
|
## Scenarios
|
|
|
|
- **GIVEN** a subtask in the sidebar, **WHEN** the user chooses `Detach from parent` from its right-click menu and confirms, **THEN** the task appears as a top-level task without a page reload.
|
|
- **GIVEN** a subtask card on the kanban board, **WHEN** the user detaches it from the three-dot menu, **THEN** the card remains in the same workflow step and no longer belongs to its former parent.
|
|
- **GIVEN** a root task, **WHEN** either task menu opens, **THEN** `Detach from parent` is not shown.
|
|
- **GIVEN** an `inherit_parent` subtask, **WHEN** the confirmation opens, **THEN** it warns that the shared workspace remains shared.
|
|
- **GIVEN** an `inherit_parent` subtask with an active workspace-group membership, **WHEN** it is detached, **THEN** its mode becomes `shared_group`, its membership remains active, and later sessions resolve the same materialized environment.
|
|
- **GIVEN** a `new_workspace` or `shared_group` subtask, **WHEN** it is detached, **THEN** its workspace mode and workspace-group membership remain unchanged.
|
|
- **GIVEN** a subtask with descendants and blocker relationships, **WHEN** it is detached, **THEN** its descendants remain nested beneath it and all blocker relationships remain unchanged.
|
|
- **GIVEN** an Office task with a parent, **WHEN** the user chooses `No parent` in the parent picker, **THEN** the canonical detach operation applies the same workspace behavior.
|
|
- **GIVEN** a detached task update received over WebSocket, **WHEN** the client merges it into cached task state, **THEN** the prior parent relationship is cleared rather than preserved.
|
|
- **GIVEN** the mobile task switcher, **WHEN** a user opens the touch-accessible actions for a subtask, **THEN** the same detach and confirmation flow is available.
|
|
|
|
## Out of scope
|
|
|
|
- Reparenting a task to a different parent from the sidebar or kanban card menus (now covered by [subtask-reparenting-drag-drop](subtask-reparenting-drag-drop.md) for the sidebar; kanban-card reparenting remains out of scope).
|
|
- Copying or provisioning a separate workspace during detachment.
|
|
- Removing blocker relationships or changing descendant relationships.
|
|
- Stopping or restarting active sessions.
|
|
- Bulk detachment.
|
|
- Synthesizing an `on_children_completed` trigger for the former parent.
|
|
|
|
## Implementation plan
|
|
|
|
See [the implementation plan](../../plans/subtask-detachment/plan.md).
|