Gantt Scheduling
The scheduling engine computes task dates from durations, dependencies, calendars, and constraints. It runs automatically on every data change and propagates dates forward through your dependency graph.
Source code
---import GanttScheduling from './GanttScheduling.vue';---
<GanttScheduling client:only="vue" />import { defineCustomElements } from '@revolist/revogrid/loader';defineCustomElements();
import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';import { currentTheme } from '../composables/useRandomData';
const { isDark } = currentTheme();
const PROJECT_ID = 'project-web-redesign';const CALENDAR_ID = 'cal-us';
// Calendar-aware scheduling: durations are in working days (Mon–Fri, excluding holidays)const ganttConfig = { id: PROJECT_ID, name: 'Website Redesign', version: '1', currency: 'USD', timeZone: 'America/New_York', primaryCalendarId: CALENDAR_ID, updatedAt: '2026-04-06T00:00:00Z', zoomPreset: 'week' as const, scheduling: { excludeHolidaysFromDuration: true, // durations skip weekends and holidays }, visuals: { shadeNonWorkingTime: true, // shade weekend columns on the timeline projectLineDate: '2026-04-06', // vertical status-date line },};
// US calendar with public holidaysconst calendars: CalendarEntity[] = [ { id: CALENDAR_ID, name: 'US Standard', timeZone: 'America/New_York', workingDays: [1, 2, 3, 4, 5], // Mon–Fri holidays: [ '2026-05-25', // Memorial Day '2026-07-04', // Independence Day ], hoursPerDay: 8, },];
const tasks: TaskEntity[] = [ { id: 't1', projectId: PROJECT_ID, parentId: null, wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't2', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't3', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], }, { id: 't4', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't5', projectId: PROJECT_ID, parentId: null, wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [], }, { id: 't6', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started', // constraint: cannot start before May 4 (waiting on external API) startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], constraintType: 'start-no-earlier-than', constraintDate: '2026-05-04', }, { id: 't7', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], deadlineDate: '2026-05-22', // deadline marker — project expects early delivery }, { id: 't8', projectId: PROJECT_ID, parentId: null, wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started', startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], },];
const dependencies: DependencyEntity[] = [ { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 }, { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 }, { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 }, { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 }, { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 }, { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },];
export function load(parentSelector: string) { const grid = document.createElement('revo-grid');
grid.theme = isDark() ? 'darkCompact' : 'compact'; grid.hideAttribution = true; grid.plugins = [GanttPlugin]; grid.gantt = ganttConfig; grid.ganttCalendars = calendars; grid.ganttDependencies = dependencies; grid.source = tasks; grid.columns = [ createDefaultTaskTableColumn('wbs'), createDefaultTaskTableColumn('name'), createDefaultTaskTableColumn('startDate'), createDefaultTaskTableColumn('endDate'), createDefaultTaskTableColumn('duration'), createDefaultTaskTableColumn('percentDone'), ];
document.querySelector(parentSelector)?.appendChild(grid);}<template> <RevoGrid hide-attribution style="height: 500px" :theme="isDark ? 'darkCompact' : 'compact'" :plugins="plugins" :source="tasks" :columns="columns" :gantt.prop="ganttConfig" :gantt-dependencies.prop="dependencies" :gantt-calendars.prop="calendars" /></template>
<script setup lang="ts">import { ref } from 'vue';import RevoGrid from '@revolist/vue3-datagrid';import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';import { currentThemeVue } from '../composables/useRandomData';
const { isDark } = currentThemeVue();
const PROJECT_ID = 'project-web-redesign';const CALENDAR_ID = 'cal-us';
const plugins = ref([GanttPlugin]);
const ganttConfig = ref({ id: PROJECT_ID, name: 'Website Redesign', version: '1', currency: 'USD', timeZone: 'America/New_York', primaryCalendarId: CALENDAR_ID, updatedAt: '2026-04-06T00:00:00Z', zoomPreset: 'week' as const, scheduling: { excludeHolidaysFromDuration: true, }, visuals: { shadeNonWorkingTime: true, projectLineDate: '2026-04-06', },});
const calendars = ref<CalendarEntity[]>([ { id: CALENDAR_ID, name: 'US Standard', timeZone: 'America/New_York', workingDays: [1, 2, 3, 4, 5], holidays: ['2026-05-25', '2026-07-04'], hoursPerDay: 8, },]);
const tasks = ref<TaskEntity[]>([ { id: 't1', projectId: PROJECT_ID, parentId: null, wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't2', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't3', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], }, { id: 't4', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't5', projectId: PROJECT_ID, parentId: null, wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [], }, { id: 't6', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started', startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], constraintType: 'start-no-earlier-than', constraintDate: '2026-05-04', }, { id: 't7', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], deadlineDate: '2026-05-22', }, { id: 't8', projectId: PROJECT_ID, parentId: null, wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started', startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], },]);
const dependencies = ref<DependencyEntity[]>([ { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 }, { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 }, { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 }, { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 }, { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 }, { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },]);
const columns = ref([ createDefaultTaskTableColumn('wbs'), createDefaultTaskTableColumn('name'), createDefaultTaskTableColumn('startDate'), createDefaultTaskTableColumn('endDate'), createDefaultTaskTableColumn('duration'), createDefaultTaskTableColumn('percentDone'),]);</script>import React, { useMemo } from 'react';import { RevoGrid } from '@revolist/react-datagrid';import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';import { currentTheme } from '../composables/useRandomData';
const { isDark } = currentTheme();
const PROJECT_ID = 'project-web-redesign';const CALENDAR_ID = 'cal-us';
const tasks: TaskEntity[] = [ { id: 't1', projectId: PROJECT_ID, parentId: null, wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't2', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't3', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], }, { id: 't4', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't5', projectId: PROJECT_ID, parentId: null, wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [], }, { id: 't6', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started', startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], constraintType: 'start-no-earlier-than', constraintDate: '2026-05-04', }, { id: 't7', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], deadlineDate: '2026-05-22', }, { id: 't8', projectId: PROJECT_ID, parentId: null, wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started', startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], },];
const dependencies: DependencyEntity[] = [ { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 }, { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 }, { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 }, { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 }, { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 }, { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 },];
const calendars: CalendarEntity[] = [ { id: CALENDAR_ID, name: 'US Standard', timeZone: 'America/New_York', workingDays: [1, 2, 3, 4, 5], holidays: ['2026-05-25', '2026-07-04'], hoursPerDay: 8, },];
function GanttScheduling() { const ganttConfig = useMemo(() => ({ id: PROJECT_ID, name: 'Website Redesign', version: '1', currency: 'USD', timeZone: 'America/New_York', primaryCalendarId: CALENDAR_ID, updatedAt: '2026-04-06T00:00:00Z', zoomPreset: 'week' as const, scheduling: { excludeHolidaysFromDuration: true, }, visuals: { shadeNonWorkingTime: true, projectLineDate: '2026-04-06', }, }), []);
const columns = useMemo(() => [ createDefaultTaskTableColumn('wbs'), createDefaultTaskTableColumn('name'), createDefaultTaskTableColumn('startDate'), createDefaultTaskTableColumn('endDate'), createDefaultTaskTableColumn('duration'), createDefaultTaskTableColumn('percentDone'), ], []);
return ( <RevoGrid style={{ height: '500px' }} theme={isDark() ? 'darkCompact' : 'compact'} hideAttribution plugins={[GanttPlugin]} source={tasks} columns={columns} gantt={ganttConfig} ganttDependencies={dependencies} ganttCalendars={calendars} /> );}
export default GanttScheduling;import { Component, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';import { RevoGrid } from '@revolist/angular-datagrid';import { GanttPlugin, createDefaultTaskTableColumn } from '@revolist/revogrid-enterprise';import type { TaskEntity, DependencyEntity, CalendarEntity } from '@revolist/revogrid-enterprise';import { currentTheme } from '../composables/useRandomData';
const PROJECT_ID = 'project-web-redesign';const CALENDAR_ID = 'cal-us';
@Component({ selector: 'gantt-scheduling', standalone: true, imports: [RevoGrid], schemas: [NO_ERRORS_SCHEMA], template: ` <revo-grid style="height: 500px" [theme]="theme" [hideAttribution]="true" [plugins]="plugins" [source]="tasks" [columns]="columns" [gantt]="ganttConfig" [ganttDependencies]="dependencies" [ganttCalendars]="calendars" ></revo-grid> `, encapsulation: ViewEncapsulation.None,})export class GanttSchedulingComponent { theme = currentTheme().isDark() ? 'darkCompact' : 'compact'; plugins = [GanttPlugin];
ganttConfig = { id: PROJECT_ID, name: 'Website Redesign', version: '1', currency: 'USD', timeZone: 'America/New_York', primaryCalendarId: CALENDAR_ID, updatedAt: '2026-04-06T00:00:00Z', zoomPreset: 'week' as const, scheduling: { excludeHolidaysFromDuration: true, }, visuals: { shadeNonWorkingTime: true, projectLineDate: '2026-04-06', }, };
calendars: CalendarEntity[] = [ { id: CALENDAR_ID, name: 'US Standard', timeZone: 'America/New_York', workingDays: [1, 2, 3, 4, 5], holidays: ['2026-05-25', '2026-07-04'], hoursPerDay: 8, }, ];
tasks: TaskEntity[] = [ { id: 't1', projectId: PROJECT_ID, parentId: null, wbsCode: '1', name: 'Design', type: 'summary', status: 'in-progress', startDate: '2026-04-06', endDate: '2026-04-24', durationDays: 15, progressPercent: 60, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't2', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.1', name: 'Wireframes', type: 'task', status: 'done', startDate: '2026-04-06', endDate: '2026-04-10', durationDays: 5, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't3', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.2', name: 'Design Review', type: 'milestone', status: 'done', startDate: '2026-04-10', endDate: '2026-04-10', durationDays: 0, progressPercent: 100, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], }, { id: 't4', projectId: PROJECT_ID, parentId: 't1', wbsCode: '1.3', name: 'Visual Design', type: 'task', status: 'in-progress', startDate: '2026-04-13', endDate: '2026-04-24', durationDays: 10, progressPercent: 40, calendarId: CALENDAR_ID, isCritical: true, tags: [], }, { id: 't5', projectId: PROJECT_ID, parentId: null, wbsCode: '2', name: 'Development', type: 'summary', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: false, tags: [], }, { id: 't6', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.1', name: 'Frontend', type: 'task', status: 'not-started', startDate: '2026-05-04', endDate: '2026-05-20', durationDays: 13, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], constraintType: 'start-no-earlier-than', constraintDate: '2026-05-04', }, { id: 't7', projectId: PROJECT_ID, parentId: 't5', wbsCode: '2.2', name: 'Backend', type: 'task', status: 'not-started', startDate: '2026-04-27', endDate: '2026-05-28', durationDays: 24, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: [], deadlineDate: '2026-05-22', }, { id: 't8', projectId: PROJECT_ID, parentId: null, wbsCode: '3', name: 'Launch', type: 'milestone', status: 'not-started', startDate: '2026-05-28', endDate: '2026-05-28', durationDays: 0, progressPercent: 0, calendarId: CALENDAR_ID, isCritical: true, tags: ['milestone'], }, ];
dependencies: DependencyEntity[] = [ { id: 'd1', predecessorTaskId: 't2', successorTaskId: 't3', type: 'finish-to-start', lagDays: 0 }, { id: 'd2', predecessorTaskId: 't3', successorTaskId: 't4', type: 'finish-to-start', lagDays: 1 }, { id: 'd3', predecessorTaskId: 't4', successorTaskId: 't6', type: 'finish-to-start', lagDays: 1 }, { id: 'd4', predecessorTaskId: 't4', successorTaskId: 't7', type: 'finish-to-start', lagDays: 1 }, { id: 'd5', predecessorTaskId: 't6', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 }, { id: 'd6', predecessorTaskId: 't7', successorTaskId: 't8', type: 'finish-to-start', lagDays: 0 }, ];
columns = [ createDefaultTaskTableColumn('wbs'), createDefaultTaskTableColumn('name'), createDefaultTaskTableColumn('startDate'), createDefaultTaskTableColumn('endDate'), createDefaultTaskTableColumn('duration'), createDefaultTaskTableColumn('percentDone'), ];}Calendar-Aware Duration
Section titled “Calendar-Aware Duration”By default, durationDays is measured in calendar days (including weekends).
Enable excludeHolidaysFromDuration to measure in working days instead:
grid.gantt = { // …required fields scheduling: { excludeHolidaysFromDuration: true, },};With this option, a 5-day task starting on a Monday ends on the following Friday — weekends and public holidays are skipped.
Calendars with Holidays
Section titled “Calendars with Holidays”Define holidays as ISO date strings in your CalendarEntity:
grid.ganttCalendars = [{ id: 'cal-us', name: 'US Standard', timeZone: 'America/New_York', workingDays: [1, 2, 3, 4, 5], // Mon–Fri holidays: [ '2026-05-25', // Memorial Day '2026-07-04', // Independence Day '2026-09-07', // Labor Day ], hoursPerDay: 8,}];Task Constraints
Section titled “Task Constraints”Constraints override the automatic forward-pass scheduling. Set constraintType and constraintDate on a task:
{ id: 't5', name: 'External Integration', constraintType: 'start-no-earlier-than', constraintDate: '2026-05-04', // cannot start before this date // …other fields}Available Constraint Types
Section titled “Available Constraint Types”| Constraint | Behaviour |
|---|---|
'start-no-earlier-than' | Task cannot start before the constraint date |
'start-no-later-than' | Task cannot start after the constraint date |
'finish-no-earlier-than' | Task cannot finish before the constraint date |
'finish-no-later-than' | Task cannot finish after the constraint date |
'must-start-on' | Task is pinned to start on an exact date |
'must-finish-on' | Task is pinned to finish on an exact date |
Deadline Dates
Section titled “Deadline Dates”Set a deadlineDate to flag tasks that are running late without constraining the scheduler:
{ id: 't6', name: 'Frontend Development', deadlineDate: '2026-05-15', // …other fields}The timeline renders a visual deadline marker on the bar when a task’s computed end date exceeds its deadline.
Manually Scheduled Tasks
Section titled “Manually Scheduled Tasks”Set manuallyScheduled: true on a task to lock its dates and exclude it from automatic propagation:
{ id: 't3', name: 'Fixed Kickoff Event', manuallyScheduled: true, startDate: '2026-04-20', endDate: '2026-04-20', // …other fields}Manually scheduled tasks keep their dates regardless of dependency changes upstream.
How the Engine Runs
Section titled “How the Engine Runs”- Topological sort — tasks are ordered by dependency depth
- Forward pass — earliest start/end dates are propagated from predecessors to successors
- Constraint application — constraints shift dates as required
- Summary rollup — parent task dates are derived from their earliest/latest children
- Critical path — total slack is computed for all tasks (see Critical Path)