Skip to content

Commit 801cb34

Browse files
committed
feat: add roadmap hierarchy date modes
1 parent edaa40c commit 801cb34

16 files changed

Lines changed: 938 additions & 43 deletions

File tree

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# WI-1016 roadmap hierarchy date semantics
2+
3+
Status: accepted design decision for the scheduling specification.
4+
5+
This document defines the optional roll-up and roll-down behavior in the
6+
roadmap. Dependency equations, calendars, constraints, and preview/apply
7+
behavior remain separate decisions.
8+
9+
## Decision
10+
11+
Work-item `start_date` and `end_date` are the canonical scheduling dates. The
12+
feature does not create another persisted set of dates.
13+
14+
The roadmap Settings menu provides three local presentation modes:
15+
16+
- **Off** shows every item's stored dates and preserves existing behavior.
17+
- **Roll up** derives summary ranges from descendants.
18+
- **Roll down** constrains descendant ranges to their parents.
19+
20+
Roll up and Roll down are available only when the roadmap uses `start_date` as
21+
its start field and `end_date` as its end field. Roadmaps using `due_date` or
22+
custom date fields keep their current behavior. Selecting the canonical fields
23+
does not migrate values from other fields.
24+
25+
The separate **Adjust related dates** control determines whether a roadmap
26+
date edit also persists the mode's related-date changes. Switching modes or
27+
switching adjustment on or off never writes item data.
28+
29+
Both controls are view-local. They do not enable scheduling behavior in boards,
30+
backlogs, lists, calendars, analytics, time tracking, or any other workspace
31+
module.
32+
33+
## Roll-up behavior
34+
35+
An item without dated descendants uses its own stored dates. A parent with
36+
dated descendants is a summary task:
37+
38+
- its effective start is the earliest descendant start;
39+
- its effective end is the latest descendant end;
40+
- nested summaries contribute recursively; and
41+
- its own stored dates are retained but ignored in the roll-up display.
42+
43+
The summary bar is read-only. It cannot be moved or resized directly; users
44+
change the range by editing descendants.
45+
46+
With date adjustment off, the summary exists only in the roadmap projection.
47+
With date adjustment on, a roadmap edit to a child persists the edited child
48+
and the recalculated ranges of each affected ancestor in one atomic operation.
49+
The bar remains a calculated summary while Roll up is active, even when its
50+
calculated range equals the newly stored parent range.
51+
52+
## Roll-down behavior
53+
54+
Roll down evaluates each descendant against its effective parent range,
55+
recursively. It changes only boundaries outside the parent:
56+
57+
- a child start before the parent start moves to the parent start;
58+
- a child end after the parent end moves to the parent end;
59+
- a boundary already inside the parent remains unchanged; and
60+
- a child wholly before or after the parent collapses to the nearest parent
61+
boundary, preserving a valid inclusive range.
62+
63+
For example, shrinking a parent to 2026-08-10 through 2026-08-20 produces:
64+
65+
| Child stored range | Effective range |
66+
| --- | --- |
67+
| 2026-08-12 through 2026-08-18 | unchanged |
68+
| 2026-08-05 through 2026-08-15 | 2026-08-10 through 2026-08-15 |
69+
| 2026-08-16 through 2026-08-25 | 2026-08-16 through 2026-08-20 |
70+
| 2026-08-25 through 2026-08-28 | 2026-08-20 through 2026-08-20 |
71+
72+
With date adjustment off, these effective ranges exist only in the roadmap
73+
projection. With date adjustment on, a roadmap edit to a parent persists the
74+
parent edit and only the descendant boundaries that the recursive calculation
75+
must move. The full set of changes is atomic: validation or permission failure
76+
on any item leaves every item unchanged.
77+
78+
## Date rules
79+
80+
- Values are Gregorian civil dates formatted as `YYYY-MM-DD`.
81+
- Dates are not converted through browser, user, or workspace timezones.
82+
- Start and end are inclusive. An item from 2026-08-10 through 2026-08-12
83+
occupies three calendar dates.
84+
- A single populated boundary is a one-day range for hierarchy calculations.
85+
- An item with neither date does not contribute to a roll-up range and is not
86+
changed by roll-down.
87+
88+
## View scope and permissions
89+
90+
The hierarchy defines the contributor universe. For every displayed item, the
91+
roadmap obtains the complete same-workspace descendant set that the viewer may
92+
see. Collection membership, transient filters, pagination, and collapsed tree
93+
state do not change an effective range. Descendants loaded only for calculation
94+
are not inserted into the visible collection.
95+
96+
Any roadmap viewer may select a mode because selection is read-only. Persisted
97+
adjustment uses the normal item-edit authorization, validation, history,
98+
notification, workflow, and automation contracts for every changed item.
99+
Hierarchy modes grant no additional access and do not reveal inaccessible
100+
descendant identities.
101+
102+
## Compatibility and isolation
103+
104+
- Off is the default for every roadmap.
105+
- Enabling, disabling, or changing a hierarchy mode emits no item history,
106+
audit entry, notification, workflow, or automation event.
107+
- Direct edits without adjustment continue through the ordinary single-item
108+
update path.
109+
- Related-date adjustment is performed only after a direct roadmap date edit.
110+
- Related updates are submitted as one atomic batch; partial persistence is not
111+
allowed.
112+
- Existing dependency links gain no scheduling semantics from these modes.
113+
- Other modules always read stored dates and never receive the roadmap's
114+
calculated projection as replacement item values.
115+
- The hierarchy response contains minimal date data and never shadows the
116+
standard item API fields.
117+
118+
## Required verification
119+
120+
Implementation must cover:
121+
122+
- Off defaults to existing roadmap behavior;
123+
- switching modes changes no persisted data;
124+
- nested descendants roll up recursively;
125+
- roll-up summaries are read-only;
126+
- roll-up adjustment persists a leaf and all affected ancestors atomically;
127+
- roll-down leaves in-range child boundaries alone;
128+
- roll-down clamps only overflowing boundaries, including nested descendants;
129+
- a wholly out-of-range child collapses to the nearest parent boundary;
130+
- filters, pagination, and collapse state do not change the projection;
131+
- unauthorized descendants are neither returned nor persisted;
132+
- a failed related-item validation or permission check rolls back the entire
133+
adjustment; and
134+
- SQLite and PostgreSQL produce the same inclusive civil-date results.
135+
136+
## Non-goals
137+
138+
This decision does not define dependency types, lag, calendars, duration,
139+
constraints, critical-path calculations, named plans, scenario schedules, or
140+
workspace-wide scheduling enablement.

frontend/src/lib/api/items.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,19 @@ export const items = {
166166
}),
167167
'update'
168168
),
169+
bulkPatch: withCrossTabNotice(
170+
(patches) =>
171+
fetchAPI('/items/bulk-patch', {
172+
method: 'POST',
173+
body: JSON.stringify({ patches }),
174+
}),
175+
'update'
176+
),
177+
getRoadmapHierarchyDates: (rootIds) =>
178+
fetchAPI('/items/roadmap-hierarchy-dates', {
179+
method: 'POST',
180+
body: JSON.stringify({ root_ids: rootIds }),
181+
}),
169182
// Perform a workflow status transition. Use this instead of passing
170183
// status_id to update() — the update endpoint rejects status_id so that
171184
// validator-mode and condition-mode workflow rules are always enforced.

0 commit comments

Comments
 (0)