Accept messy input#
Schedule project data that strict validation rejects, such as data exported from a UI with duplicate ids or missing dates, and get a list of every fix the engine made.
By default a JsonProject uses "validation": "strict" and returns a validation error envelope for the first invalid field. "validation": "repair" coerces invalid data instead (deduplicates ids, derives missing dates, fixes inconsistent dates), then schedules the result.
Steps#
-
Create the project with repair validation:
const = new;The constructor throws the error envelope as a string if the options are invalid, for example
"validation": "fix". -
Pass your data to
solveas usual.analyzewith a data string repairs supplied data the same way. The one-shotscheduleexport always uses strict validation, so use aJsonProjecthere. -
Read the entries with
kind: "repaired"inresult.notesand show them to the user, or log them. Each one has apathand amessage:
The full example below has four kinds of problems: a duplicate id, a task with no dates, a dated constraint with no date, and a task whose start, end and duration disagree. Strict validation rejects this data. Repair schedules it:
const = ;
const = ;
const = ;
const = new;
What repair changes#
Repair reports each change below as one repaired note in ScheduleResult.notes, with the field path in path:
- Duplicate ids in
tasks,links,resources,assignmentsandcalendars: the last record replaces the earlier one and takes its position. The note is attasks[i].id, whereiis the index of the duplicate in your input. - A task with no start and no end gets
start_dateset toconfig.project_start, or to the earliest date of any task ifproject_startisn't set. - A leaf task (not a milestone) with no
durationand only one date getsduration: 1. - Start, end and duration that disagree: the dates win, and the duration becomes the working time between them. If the end is in non-working time, or the dates were computed on a different calendar, the duration is dropped and the task is resolved from its dates. An end that falls inside a pause moves to the pause start, with a second note at
tasks[i].end_date. - A summary with only one date gets the other date computed from the date and its duration.
- A dated constraint (
"snet","snlt","fnet","fnlt","mso","mfo") withoutconstraint_dateis cleared. - Invalid
pauseson a task: the whole list is cleared, with a note attasks[i].pauses. - An invalid assignment
offsetordurationis cleared, so the assignment books the whole task.
A task that already has two of start, end and duration is valid input. Repair doesn't touch it and doesn't report it.
Only the duplicate-id notes use input indexes. Every other note indexes the deduplicated list. In the example, the two build records become one, so deploy is tasks[4] in your input but tasks[3] in its note.
What repair doesn't fix#
- An entirely undated project, with no
config.project_startand no task that has a date, still fails with avalidationerror attasks[i].start_date. The engine never falls back to today's date. - Every other invalid input, such as a cycle in the
parenthierarchy or a calendar with no working time, still returns avalidationerror. See Error handling. - If repair or validation fails,
solvereturns an error envelope (ok: false) and the project keeps its previous state.
Result#
Run the example with node. It prints the fixes, then the scheduled dates:
tasks[3].id: duplicate id replaced the earlier record, retaining its position
tasks[2].constraint_type: dated constraint without date cleared
tasks[2].start_date: missing date defaulted to project anchor
tasks[2].duration: missing duration defaulted to one
tasks[3].duration: duration recomputed from supplied start and end
design 2026-01-01 00:00..2026-01-04 00:00 3
build 2026-01-01 00:00..2026-01-03 00:00 2
review 2026-01-01 00:00..2026-01-02 00:00 1
deploy 2026-01-01 00:00..2026-01-04 00:00 3
build uses the second record's duration of 2, and deploy has a duration of 3, taken from its dates. With new JsonProject("{}"), the same solve call returns a validation error at tasks[2].constraint_date.