# 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`.