Files
ClaudeDo/docs/superpowers/specs/2026-08-11-dependency-chain-display-design.md
T

74 lines
4.1 KiB
Markdown

# Dependency chain display — Design
**Date:** 2026-08-11
**Status:** approved, not implemented
## Problem
`DependsOnTaskId` is invisible in the UI. `TaskRowViewModel` has no `DependsOnTaskId` property
and `TaskRowView.axaml` renders nothing for it — a chained task looks exactly like an unrelated
one. The execution order the user declared is not readable anywhere in the list.
## Target
A chain renders as a group: the head full width, its dependents indented behind a vertical rail,
each carrying a small circular step badge on the rail.
```
┌──────────────────────────────────────────┐
│ chain head (full width, no badge) │
└──────────────────────────────────────────┘
╷ ┌────────────────────────────────────┐
(1)│ first dependent │
╷ └────────────────────────────────────┘
╷ ┌────────────────────────────────────┐
(2)│ second │
╷ └────────────────────────────────────┘
╷ ┌────────────────────────────────────┐
(2)│ parallel to the second │
╷ └────────────────────────────────────┘
╷ ┌────────────────────────────────────┐
(3)│ third │
└────────────────────────────────────┘
```
## Model reading
- **Step number = hop distance from the chain head**, not a running counter. `DependsOnTaskId` is
a single FK, so several tasks may share one predecessor → same depth → **same number**. Two
rows showing `2` means "these two are both unblocked by step 1", which is the intended reading.
- **Chain head** = a task with no `DependsOnTaskId` that at least one other task points at. A task
with no dependents and no dependency is not a chain and renders exactly as today.
- Cycles are impossible (`TaskStateService.SetDependsOnAsync` rejects them), so the depth walk
always terminates. Still cap the walk defensively.
## Decisions
**One indent level only — parent wins.** The 24 px indent track already exists for planning
children (`TaskRowView.axaml:22-28`, gated on `ShowAsChild`). A planning child that *also* has a
`DependsOnTaskId` keeps the parent indent and shows its chain membership as a small inline
chip (`after #123`) instead of a second indent level. No nesting, no 48 px rows.
**The group is pulled together; the head carries it.** Chain members are re-ordered to sit
directly under their head regardless of `SortOrder`, so displayed order always equals execution
order. Dragging the head moves the whole group; members are not individually draggable.
**Head not in view → no orphan rails.** Exact precedent exists: `ParentInView` /`ShowAsChild`
(`TaskRowViewModel.cs:75`, computed in `Regroup` at `TasksIslandViewModel.cs:543`). Same
treatment — if the head is filtered out (other list, My Day, completed group), the row renders
flat with the `after #123` chip instead of a dangling rail.
**Badge has no `#`.** If task numbers (`2026-08-11-task-numbers-design.md`) also land, a row
would show a rail badge and a `#412` title prefix. The rail badge is a step position, not an
identity — render it bare (`2`), never `#2`.
## Slices
| # | Slice | Depends on |
|---|---|---|
| 1 | VM: `DependsOnTaskId` + depth/head computation in `Regroup`, group pull-together, drag semantics | — |
| 2 | View: rail, step badge, `after …` chip, locale keys | 1 |
Slice 1 exposes the contract Slice 2 binds to: `ShowAsChainMember`, `ChainStep`,
`ChainAfterLabel`.