Calendar planning
Calendar planning and resource constraints
Section titled “Calendar planning and resource constraints”import { planGanttTasks } from '@alneas/task-graph/planning';const result = planGanttTasks([ { id: 'design', created_at: '2026-03-06', durationDays: 2, assignee_id: 'team' }, { id: 'build', created_at: '2026-03-06', durationDays: 1, assignee_id: 'team' },], [{ id: 'dependency', sourceId: 'design', targetId: 'build', type: 'FS' }], { calendar: { workingWeekdays: [1, 2, 3, 4, 5], holidays: ['2026-03-09'] }, resourceLeveling: { enabled: true, resources: [{ id: 'team', capacity: 1 }] },});This is a separate date-based model from the original elapsed-hour CPM. FS/SS/FF/SF and positive/negative integer lagDays are supported. Dates for the day planner must be valid local midnight Date/ISO datetime values or strictly YYYY-MM-DD strings, parsed locally. Invalid dates and sub-day timestamps throw. Calendar arithmetic preserves local days across DST, without date-fns or any runtime dependency. The host timezone determines the local calendar; use the same timezone on all hosts. The original two-argument scheduleGanttTasks preserves calendar-day/time-of-day behavior; supplying its third options argument explicitly opts into day planning.
Missing calendar means seven working days; calendar: {} means Monday–Friday. workingDates overrides holidays/weekdays. There is no country holiday lookup or individual resource calendar. Durations occupy [start, finish); finishes are exclusive working-day boundaries. durationDays overrides inferred effort, 0 means a milestone. A positive nonworking-only input span counts as one workday. Missing due dates infer one calendar-day span. Input start dates are release constraints.
Tasks require id/created_at; UI title/status/colour/avatar are not required or interpreted. Consumer metadata is retained and output date types allow Date values. Parents identified by child parent_id are summary rows, preserved unchanged and excluded from planning. Dependencies must connect leaf tasks, with unique task and dependency IDs, existing endpoints and an acyclic graph. Invalid input throws without mutating the original tasks/dates. Hierarchy rollup stays in the UI.
Capacity leveling is explicitly enabled. Default capacity/demand is 1; fractional positive finite values are accepted with a relative 1e-9 comparison tolerance. Assignments occupy all resources simultaneously over every working day; duplicate assignments are summed. Omitted assignments use assignee_id; [] means no resources. An explicit resource list rejects unknown IDs. Tasks are not split or moved before their releases. Stable topological/input order determines priority; the schedule is not guaranteed to minimize makespan. Status/actual/frozen work is not modeled.
criticalPath reports dependency-only CPM BEFORE leveling, honoring releases and
including every zero-float branch. resourceDelays separately reports direct and
propagated capacity delays. projectFinish includes leveling. Analyze with
analyzeGanttCriticalPath to ignore capacity. No leaf tasks yields null finish.
maxCalendarDays defaults to 3660, range 1..36600; finite horizons, capacity checks
and a 5,000,000-operation capacity search budget bound work. Exhaustion throws.
The sparse capacity index scales with reservations rather than resource×horizon.
/planning/capacity-index and /planning/graph are advanced helper entries; the
capacity index assumes validated integer half-open ranges within its axis.