ScheduleResult#

The response payload of every scheduling call. It arrives as result in the success envelope returned by schedule, JsonProject.solve, JsonProject.preview and JsonProject.analyze. JsonProject.level returns the same fields plus rounds.

Cycles, conflicts and a planner that didn't converge are reported in the result with ok: true, not as an error envelope.

Fields#

Optional fields are left out when they don't apply. They're never null. Field names are camelCase; entries in updates use the snake_case task-data names. See Wire format.

Field Type Present Description
baselineId string see baselineId Token of the baseline this result produced or read.
updates Mutation[] always New dates and fields of the reported tasks.
cycles Cycle[] always Dependency cycles found in the project.
conflicts Conflict[] always Constraint and calendar conflicts.
notes Note[] always Dropped input records and repairs made on import.
converged boolean always true when the planner reached a fixed point within max_iterations of SchedulingConfig. analyze always returns true.
analytics Analytics when the call's options request criticalPath or slack Critical path and slack.
overallocations Overallocation[] when the project has at least one assignment Resource overload windows. [] when no resource is overloaded.
profile LoadProfile only from preview with options.profile set Resource load of the previewed dates. For the load of the accepted dates, call JsonProject.loadProfile.

The encoder doesn't keep a fixed key order. Read fields by name.

baselineId#

An opaque token that names an accepted baseline.

Call baselineId
solve Always present: the baseline the call accepted.
preview Always present: the baseline the preview read. A following preview or apply accepts it.
analyze without data_json Present only on a solved project with no unsolved edits.
analyze with data_json Absent.
schedule Present, but it names the baseline of a temporary project, so no later call can use it.

The token changes when the accepted dates change. Don't store it or compare tokens from separate runs. Which calls check it is listed on each call's page.

updates#

One Mutation per reported task.

  • By default the list is sparse: only tasks that changed. solve without data and level also report each task touched by apply since the last solve.
  • With includeUnchanged in SolveOptions, solve and schedule also list every unchanged scheduled task.
  • preview reports changes relative to the baseline it read.
  • On a project with cycles, the list is empty unless includeUnchanged is set.
  • analyze always returns [].

cycles and conflicts#

cycles holds every unique simple dependency cycle. When it isn't empty, no task is moved. conflicts lists constraint violations and non-working-time placements; analyze always returns [].

Mutation#

One entry of updates. Fields use the snake_case names of TaskData, with the same units. Only reported fields are present.

Field Type Present Description
id Id always Task id.
kind "task" | "summary" | "milestone" always Task kind. A task imported as "project" is reported as "summary".
start_date DateValue the start changed, or the task is reported in full New start.
end_date DateValue the end changed, or the task is reported in full New end.
duration number the duration changed, or the task is reported in full New working time in the task's duration unit.
leveling_delay number the leveling delay changed New leveling delay.
pauses Pause[] the pause list changed The complete new pause list; [] when every pause was removed.
reason Reason the call ran with reasons: true What placed the task. Absent when no planner ran (a cyclic project).

A task is reported in full (start_date, end_date and duration all present) with includeUnchanged, or when apply touched it since the last solve.

An entry decodes as a TaskUpdate: kind and reason are ignored there as unknown fields, so an entry can be sent back through apply as is.

Reason#

Tagged by kind.

kind Fields Description
"asap" linkId, sourceId The incoming link linkId from predecessor sourceId set the start.
"alap" linkId, targetId Backward scheduling: the outgoing link linkId to successor targetId set the end.
"summary-translation" summaryId The task moved with its summary as one block.
"constraint" - A task constraint set the date.
"preferred" - No link or constraint moved the task; it sits at its own date.

When several links give the same date, asap and alap name the first one. See Reading results.

Cycle#

Field Type Description
tasks Id[] Tasks on the cycle, in cycle order.
links Id[] Links that form the cycle, in path order. A cycle closed through summary containment has fewer links than tasks.

Conflict#

Tagged by kind.

kind Fields Reported when
"constraint-violation" task, constraint (ConstraintType), violation ("earliest-start" | "latest-start" | "earliest-end" | "latest-end"), required, actual (DateValue) A constraint placed the task at a date its driving link doesn't allow. required is the constraint's date, which the task keeps; actual is the date the link would give. Only with apply_constraints: true.
"calendar-non-working" task, proposed, snapped (DateValue) A summary moved as a block and a descendant landed on non-working time. snapped currently always equals proposed. Only with strict_calendar: true.

See Reading results.

Note#

Tagged by kind. Notes are information; the call still succeeds.

kind Fields When
"dropped-task" task, reason The task has unscheduled: true.
"dropped-link" link, reason The link can't take part in the plan, for example its source or target is unknown or unscheduled.
"dropped-assignment" assignment, reason The assignment's task is unscheduled.
"repaired" path, message The project was created with {"validation": "repair"} and the input was changed at path.

reason and message are human-readable text. Branch on kind, not on the text.

Analytics#

Field Type Present Description
status "available" | "invalid" always "invalid" on a cyclic project.
criticalPath { tasks: Id[], links: Id[] } criticalPath: true requested and status is "available" Critical tasks (summaries included) and links.
slack { task: Id, total: number, free: number }[] slack: true requested and status is "available" Total and free slack per task, in the configured duration_unit.

Check status before reading the other fields. The options are described in AnalyticsOptions; how slack is computed is in Critical path and slack.

Used by#

Example#

A schedule call with {"reasons": true, "analytics": {"criticalPath": true, "slack": true}} on two linked tasks, one unscheduled task, and two assignments. Only build moved:

{
  "ok": true,
  "result": {
    "baselineId": "1-bdea073f52fc7eca",
    "updates": [
      {
        "id": "build",
        "kind": "task",
        "start_date": "2026-08-12 00:00",
        "end_date": "2026-08-15 00:00",
        "reason": { "kind": "asap", "linkId": "l1", "sourceId": "design" }
      }
    ],
    "cycles": [],
    "conflicts": [],
    "notes": [
      { "kind": "dropped-task", "task": "qa", "reason": "unscheduled" },
      { "kind": "dropped-link", "link": "l2", "reason": "target task \"qa\" is unscheduled" }
    ],
    "converged": true,
    "analytics": {
      "status": "available",
      "criticalPath": { "tasks": ["design", "build"], "links": ["l1"] },
      "slack": [
        { "task": "design", "total": 0, "free": 0 },
        { "task": "build", "total": 0, "free": 0 }
      ]
    },
    "overallocations": []
  }
}