Skip to content

Engine Logic

Source code
Astro astro
---
import GanttScheduling from '@revolist/revogrid-examples/components/gantt/GanttScheduling.vue';
---

<GanttScheduling client:only="vue" />
TypeScript ts
// src/components/gantt/GanttScheduling.ts
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 holidays
const 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'),
  ];

  document.querySelector(parentSelector)?.appendChild(grid);
}
Vue vue
<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'),
]);
</script>
React tsx
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'),
  ], []);

  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;
Angular ts
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-grid',
  standalone: true,
  imports: [RevoGrid],
  // Allows Angular demos to bind RevoGrid plugin props that are not wrapper inputs.
  schemas: [NO_ERRORS_SCHEMA],
  template: `
    <revo-grid
      style="min-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 GanttSchedulingGridComponent {
  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'),
  ];
}

The RevoGrid Gantt scheduling engine follows a deterministic pipeline to compute effective dates for all tasks. It processes data changes in five distinct phases to ensure dependency safety and constraint satisfaction.

The engine first builds a dependency graph and performs a topological sort. This ensures that tasks are ordered such that every predecessor is processed before its successors.

  • Summary tasks are handled separately to ensure they can roll up dates from their children.
  • Circular dependencies are detected and reported as errors.

In the default scheduleFrom: 'project-start' mode, the engine performs a forward pass:

  • Root tasks (those without predecessors) are anchored to the projectStartDate.
  • Successors are shifted forward to satisfy their incoming dependencies (FS, SS, FF, SF) plus any lagDays.
  • The engine uses the successor’s calendar to compute working-day offsets if lagCalendar: 'working-days' is enabled.

Once the dependency-driven dates are known, the engine applies per-task constraints:

  • Soft Constraints: SNET, SNLT, etc., refine the start date if the dependency date falls outside the constraint bound.
  • Hard Constraints: must-start-on and must-finish-on override the forward pass results. If a hard constraint violates a dependency, a conflict is reported, but the constraint date wins.

After all leaf tasks have their dates resolved, the engine rolls up the schedule to summary (parent) tasks:

  • A summary task’s startDate is the minimum of its children’s startDate.
  • A summary task’s endDate is the maximum of its children’s endDate.
  • Rollup is recursive, traversing the entire WBS (Work Breakdown Structure) tree.

The final phase computes the project’s early and late dates using a backward pass from the project finish date:

  • Total Slack: The number of days a task can be delayed without delaying the project finish.
  • Critical Path: Tasks with zero total slack are marked as isCritical.
  • This analysis helps planners identify the “chain” of tasks that determines the total project duration.

The engine prioritizes user intent and data integrity when scheduling rules collide:

PriorityRuleOutcome
HighActualsactualStartDate and actualFinishDate pin the task; the engine won’t move them.
HighHard ConstraintsMSO / MFO dates win over dependencies; conflict reported.
MediumDependenciesAuto-tasks are clamped to satisfy predecessors.
MediumSoft ConstraintsDates are adjusted to bounds where possible; conflict reported if windows are disjoint.
LowAuthored DatesUsed as the starting point for root tasks or manual tasks.