Skip to content

Scheduler Conventions

This page is a reference for evaluators and library users who need to know whether trueppm-scheduler models their schedule the way they expect. Each row is one rule the engine implements today, with the issue or ADR that decided it. Rows marked Differs are the ones where a schedule built in MS Project or Primavera P6 can come out differently here. Everything else follows the conventions those tools use.

The same list ships in the trueppm-scheduler README for PyPI readers. The narrative for each rule lives on CPM Scheduler and Monte Carlo.

Rulevs MS Project / P6Decided in
Durations and three-point estimates count working days, in whole days only. A sub-day duration or lag is rejected with InvalidScheduleInput.Differs — both tools schedule in hours.#826
Calendar.hours_per_day and Calendar.timezone are inert. They round-trip through serialization and change no computed date.Differs — in MS Project, hours per day converts a day-denominated duration into working time.#4131
Lag counts calendar days. The resulting date snaps forward to the successor’s next working day (backward for negative lag), so a weekend can absorb a short lag.Differs — MS Project counts lag in working time by default and treats elapsed days (2ed) as the opt-in. P6 counts lag on a working calendar.#2534; open question #2535
With per-task calendars, a task’s duration expands on its own calendar and a link’s lag is consumed on the successor’s calendar.Same as MS Project’s task calendars.ADR-0120
Rulevs MS Project / P6Decided in
All four link types (FS, SS, FF, SF) take a lead or lag.Same.—
A task driven by an FF or SF link stays contiguous and is right-aligned on its pinned finish, so its start moves back. As a result, CPM is not monotone in duration on a network with FF/SF links: a longer task can start earlier and pull an SS successor with it.Same as MS Project.#3806 (decided: keep)
An SF link finishes the successor at the start of the predecessor’s start day. With zero lag, the successor’s last working day is the day before. The rule is the same for a task predecessor and a milestone predecessor.Same.#4145
A task whose links are all SF is placed by them alone, even before the project start: a predecessor on the project’s first working day puts a zero-lag SF successor on the working day before it. Only the project-start floor is lifted. The data date, a planned_start, and a recorded actual_start still hold the task. Any FS, SS or FF link on the task keeps the project-start floor too, so leads stay floored. Adding a non-binding FS, SS or FF link to an SF-only task can therefore move it later, back to the project start.Same as MS Project, which schedules the SF successor ahead of the project start.#4218, #4220
A zero-duration milestone is an instant. A milestone driven by work sits at the end of its driver’s finish day, and a milestone held by a floor sits at the start of that day. When a lag, or a predecessor’s recorded finish on a non-working day, places the milestone after a weekend or holiday, it is shown at the start of the next working day. The project start only bounds where a milestone is shown. When a lead or an SF-only predecessor puts a milestone’s links before the project start, the milestone is shown on the project’s first working day, but its successors’ lags still count from the earlier point, so the project-start floor alone never makes inserting a milestone into a finish-to-start link move anything. The data date, a planned_start, and a recorded actual_start still hold the milestone and every lag measured from it, so a milestone held by one of those can move a successor later than the same link without it. A milestone held at the project start keeps zero total float and reads as critical, even when the tasks on both sides of it have slack.Same for the instant and the weekend rule: MS Project moves an elapsed lag that ends on non-working time to the next working time. Unverified for the project-start rule (#4225): passing the earlier point on to a floored milestone’s successors has not been checked against MS Project or P6.#4079, #4173, #4225
The only date constraint is start-no-earlier-than, via Task.planned_start, snapped to the next working day. planned_finish is reserved and inert: there is no deadline, finish constraint, must-start-on, or as-late-as-possible.Differs — MS Project has eight constraint types plus deadlines. P6 has primary and secondary constraints.#3345, #804
An SS or SF link from a summary task is rejected with InvalidScheduleInput. FS and FF links from a summary are expanded to its leaves.Differs — MS Project accepts any link type on a summary.ADR-0370
Rulevs MS Project / P6Decided in
A completed task (actual_finish set, or percent_complete at 100) is pinned to its recorded dates verbatim, even when they fall on a non-working day. It is out of network logic and is never re-sampled in Monte Carlo.—ADR-0136
An in-progress task schedules only its remaining work, duration − floor(duration × percent_complete / 100) working days. That work runs forward from the data date (status_date) and is floored at actual_start, which is kept unsnapped.—ADR-0132
Rulevs MS Project / P6Decided in
total_float is the working-day span from the early start to the late start, and is_critical means exactly total_float == 0.——
Float stops where a slip would move the shown finish. When the project ends at the end of a day (work finishing Friday), a milestone placed at the start of a day, by a start-to-start link from work or by a floor, can slip only to the start of that Friday: one more day puts it at the start of Monday and moves project_finish. A finish-to-start predecessor places the same milestone at the end of a day, so it keeps its float up to the finish.—#4174, #4183
free_float inverts the forward constraint across all four link types, anchored on each successor’s early date, and is capped at total float. A task with no live successor falls back to its total float, and completed successors are skipped. A slip that leaves a milestone on the same midnight but changes the edge of the day it is shown on still moves it: when a milestone sits at the end of Wednesday and a start-to-start link would place it at the start of Thursday, the same instant, free float through that link stops a day short.Same definition.#1828, #4183
The order of ScheduleResult.tasks is unspecified. Look tasks up by id, never by position.—#1862
ScheduleResult.project_finish and a milestone’s early_finish are the day the finish is shown on, not an instant, and milestone_at_day_end says which edge of that day it is. The end of a Friday and the start of the following Monday are the same point in working time, so the shown day can move across a weekend or holiday while the working-time finish does not. To measure a slip, compare in working time and read a start-of-day milestone finish as the end of the working day before it. Never subtract two shown days. TruePPM measures a finish shift this way in the project end-date shift notification, the activity feed, the milestone forecast digest and sprint bridge card, the Monte Carlo what-if, the cross-project sprint-boundary check, baseline drift (the activity feed, the overview’s “Slipped vs baseline” list, a task’s Baseline tab deltas and a closed sprint’s milestone slip), the milestone rollup variance, the program rollup’s baseline and schedule variance, and every Monte Carlo delta — each percentile against the CPM finish (delta_vs_cpm), added time (risk_premium_days), run to run in the forecast history, and the what-if. A baseline or Monte Carlo run recorded before TruePPM kept which edge of the day each finish sat on is read as the end of its day. A task’s own schedule_variance_days (actual finish vs baseline) and baseline_finish_variance_days (forecast finish vs baseline — the baseline chip on a board card and in the task drawer) are measured the same way (#4203). Monte Carlo percentiles carry the same reading (#4204).—#4178
Rulevs MS Project / P6Decided in
A three-point estimate samples a Beta distribution, fitted by method of moments to the classic PERT mean (O + 4M + P) / 6 and σ (P − O) / 6. This is not the λ=4 Beta-PERT. Both have the same mean, but on a symmetric estimate the band here is about 12% narrower, so P80 and P95 land slightly earlier. On an estimate whose most-likely value sits at one end, the band is wider.Differs from the λ=4 Beta-PERT that @RISK and Primavera Risk Analysis default to. Neither MS Project nor P6 samples on its own.#4133
Every sampled duration is floored at the task’s own duration, so the optimistic tail below the plan is not expressed. To model finishing early, lower duration.Differs — risk tools sample the whole estimate.#3765
No percentile precedes the CPM finish on a network built only from FS and SS links. On an FF/SF network a percentile can land earlier, because CPM itself is non-monotone there (see the FF/SF row above).—#3806
P50/P80/P95 are ranked by each run’s working-time finish. Where runs at the same position are shown on different days (the end of a Friday, the start of the Monday after it), each is shown on the day its rank lands on with runs ordered by position and then shown day, so it stays a quantile of the distribution in both directions, with p50_at_day_start / p80_at_day_start / p95_at_day_start saying which edge of that day each one is. A run whose finish is a start-of-day milestone ranks level with a run ending the Friday before it. Compare a percentile with the CPM finish, or with another run’s, in working time.—#4204
A fixed seed reproduces P50/P80/P95 exactly on the same numpy version and the same trueppm-scheduler version. Upgrading either one can move seeded percentiles.—#4099