The 24-step release plan from the article (architecture → platform builds → offline sync → push notifications → QA → store submission) is modeled as a task DAG: each task has a duration, a list of prerequisite tasks, and a resource pool (Backend / iOS / Android / QA) with limited parallel capacity.
Critical path (CPM, unconstrained): a forward pass with infinite resources gives the earliest finish of the sink task — the theoretical floor no team size can beat, because it's bounded by dependencies alone:
EF(t) = duration(t) + max(EF(p) for p in predecessors(t))
CriticalPath = max(EF(t) for t with no successors)
RCPSP list scheduler (serial SGS): with finite capacity, tasks compete for their pool. Each task gets a priority equal to its tail length — the longest remaining chain of work after it finishes — so the most schedule-critical work is never left waiting behind less urgent work:
tail(t) = duration(t) + max(tail(s) for s in successors(t))
At each free resource unit: start the ready task with the largest tail(t)
The simulator re-runs this scheduler every time you move a capacity slider. Below the critical-path floor, adding developers to a track shortens the project; once a track's queue is empty the rest of the delay is pure dependency waiting — more heads stop helping, exactly the diminishing-returns effect real release plans hit when a bottleneck team (usually QA) can't be parallelized further.
- Capacity sliders — how many developers/testers work a track in parallel; re-solves the schedule live.
- Play — sweeps a "today" plane through the 3D Gantt chart; bars glow while active.
- Resource-caused delay — the gap between the constrained schedule and the unconstrained critical path: the cost of understaffing.