Skip to content

[scheduler][umbrella] Event scheduling — dependencies, constraints & auto-scheduling engine #22853

Open
11 of 11 issues completed

Description

@rita-codes

This umbrella covers the full event-scheduling model for the Scheduler: a shared auto-scheduling engine driven by two kinds of input — dependencies (relationships between events) and constraints (placement rules for a single event).

These were previously split across #21864 (dependencies) and #21582 (constraints), but they're driven by the same engine — moving an event cascades through dependencies while respecting constraints. Tracking them together keeps that one engine coherent.


Dependencies — relationships between two events

A dependency links a predecessor to a successor and is rendered as an arrow on the timeline. It expresses how the position of one event conditions the other. There are four logical types, named by which ends (start/finish) are linked:

Type Meaning Example
FS — Finish-to-Start Successor can't start until predecessor finishes "Paint the wall" can't start until "plaster" finishes. ~90% of real cases.
SS — Start-to-Start Successor can't start until predecessor starts "Pour concrete" and "smooth it" start together (smoothing trails the pour)
FF — Finish-to-Finish Successor can't finish until predecessor finishes "Review docs" can't finish until "write docs" finishes
SF — Start-to-Finish Successor can't finish until predecessor starts Shift handover: the outgoing shift can't end until the incoming one starts. Rare.

Rendering (FS):

Predecessor ████████┐
                    └──▶ Successor ██████      (arrow: predecessor end → successor start)

Lag / lead — an optional offset on the relationship:

  • Lag (positive): "FS + 2 days" → successor starts 2 days after the predecessor finishes.
  • Lead (negative): "FS − 1 day" → the two overlap by 1 day.

Component scope: EventTimeline only (arrows only make sense on a timeline/Gantt).


Constraints — placement rules for a single event

A constraint does not relate two events. It limits where one event on its own can be placed in time. These are the per-event scheduling rules (MS Project / Bryntum terminology):

Constraint Meaning
ASAP — As Soon As Possible Place it as early as possible (default)
ALAP — As Late As Possible Place it as late as possible
SNET — Start No Earlier Than Can't start before date X
SNLT — Start No Later Than Can't start after date X
FNET — Finish No Earlier Than Can't finish before date X
FNLT — Finish No Later Than Can't finish after date X
MSO — Must Start On Must start exactly on date X
MFO — Must Finish On Must finish exactly on date X

Example: "Inspection can't start before March 1st" (SNET) even if all preceding tasks finish earlier.

Component scope: EventTimeline + EventCalendar (a fixed-date rule applies to both).


Auto-scheduling engine — shared by both

When an event moves or resizes, the engine reschedules dependent events (following the dependency types) while respecting each event's constraints. It also rejects invalid graphs (cycles).

How they interact: you move "plaster" 3 days later → "paint" reschedules behind it (FS), but if "paint" has an SNET of March 1st, it won't move before that date even though the arrow would push it there. Dependencies push; constraints set the limits; the engine reconciles both.


Architecture — plugin-based

Dependencies live only in EventTimelinePremiumStore; constraints apply to both EventTimelinePremiumStore and EventCalendarPremiumStore. Those two premium stores don't share a premium base (timeline extends SchedulerStore directly; calendar extends the community calendar store), so shared scheduling logic has no natural home in a store class.

The solution — already used twice in the codebase — is a plugin in x-scheduler-internals-premium:

  • Lazy loading: a shared SchedulerLazyLoadingPlugin (holds a store ref, lifecycle via DisposableStack) specialized by thin per-product subclasses.
  • Recurring events: an interface in the community base + implementation injected into SchedulerStore, which delegates to it — premium logic never leaks into community.

So the scheduling engine (data model + cascade + constraint reconciliation) should be a SchedulerSchedulingPlugin in x-scheduler-internals-premium, composed by each store:

SchedulerSchedulingPlugin  (x-scheduler-internals-premium — data model + engine)
├─ EventTimelinePremiumStore  → dependencies + constraints
└─ EventCalendarPremiumStore  → constraints only

Benefits (all proven by the existing plugins): a neutral home for the shared engine, lifecycle (dispose) for the engine's subscriptions, and premium code kept out of the community package.

The plugin is deliberately store-agnostic: if we ever decide to export a standalone Gantt component (not decided — just keeping the door open), its store would compose the same plugin and reuse the same headless TimelineGrid parts, rather than wrapping the public EventTimelinePremium. That's Bryntum's proven model — Scheduler Pro and Gantt are two products sharing one engine (ChronoGraph) and one DependencyModel. The only Timeline-bound piece today is where the dependencies state slice is declared (EventTimelinePremiumState), which is an internal, non-breaking refactor away from being shared.

Scope this quarter — FS end-to-end (vertical slice)

We ship Finish-to-Start (FS) end-to-end, including auto-scheduling: moving/resizing a predecessor reschedules its FS successors. The data model ships only what is implemented: the type union starts with FS, and the other types plus lag (with its unit) are added together with their implementations as additive changes — see #22854.

Being the first behavior, FS carries the shared foundation (data model, rendering, interaction, engine, cycle guard). Subsequent behaviors reuse it and are much lighter.

The FS behavior is broken down in the sub-issues linked to this issue (data model, render arrow, create/edit/delete, auto-scheduling engine, cycle guard).

Later behaviors (backlog)

  • Dependencies: SS · FF · SF (enforcement + arrow geometry) · lag/lead time
  • Constraints: SNET/SNLT · FNET/FNLT · MSO/MFO · ASAP/ALAP

Choosing the dependency type (future UX)

Two mechanisms, used together:

  1. Implicit — terminal-driven. Events get terminals on both edges (start + end). The type is encoded by which edge you drag from and drop on (fromSide / toSide):

    From… To… Type
    end (finish) start FS
    start start SS
    end end FF
    start end SF
  2. Explicit — arrow editor. Select an arrow → change its type (FS/SS/FF/SF) and lag from a menu. Needed for precision and as the keyboard/a11y path (corner-drag is too fine-grained).

Groundwork already in v1: the type field exists in the data model (#22854) from day 1, starting as an FS-only union that widens per type as they are implemented (store type as the semantic truth; treat fromSide/toSide as a render/interaction detail). v1 wires only the end-edge terminal (FS) (#22856); the future is adding the start-edge terminals → unlocks SS/FF/SF by geometry, plus the editor for type + lag. No redesign needed.


Standards & foundations

What this design is based on — three different levels of grounding:

Formal industry standards (the semantics). The dependency types and terminology are not vendor inventions:

De-facto market conventions (no formal spec exists). There is no normative standard for the data model or the engine behavior; the design follows where vendors converge:

No standard, no prior art — original design with documented rationale:

  • Accessibility of dependency arrows: no WAI-ARIA pattern covers dependency graphs, and no competitor exposes the relationship to assistive tech — the aria-hidden arrow + aria-describedby on the successor ([scheduler] Dependencies: render the FS arrow #22855) is our own design and a differentiation opportunity.
  • Recurring events × dependencies: no product in the market combines them (see Competitors below); the v1 rejection policy ([scheduler] Dependencies: data model #22854) is backed by the absence of prior art, not by prior art.

Competitors

  • Bryntum (Scheduler Pro / Gantt), DHTMLX, Syncfusion — all have dependencies + constraints.
  • FullCalendar has event constraints but no dependencies (open request Gantt chart arrows between events for timeline view fullcalendar/fullcalendar#4748).
  • No competitor combines recurring events with dependency scheduling. Bryntum Scheduler Pro has both features but never combines them (its recurrence demo disables dependencies); DHTMLX and Syncfusion split them across products (Scheduler = recurrence, Gantt = dependencies); MS Project / Wrike escape the problem by materializing each occurrence as a standalone task. This backs the v1 policy of rejecting dependencies on recurring events — details in [scheduler] Dependencies: data model #22854.

Folds in and supersedes #21864 (dependencies) and #21582 (constraints).

Activity

  1. added
    type: new featureExpand the scope of the product to solve a new problem.
    umbrellaFor grouping multiple issues to provide a holistic view
    scope: schedulerChanges related to the scheduler.
    on Jun 17, 2026
  2. added
    on holdThere is a blocker, we need to wait.
    plan: PremiumImpact at least one Premium user.
    on Jun 17, 2026
  3. removed
    on holdThere is a blocker, we need to wait.
    on Jul 8, 2026
  4. rita-codes commented on Jul 9, 2026

    @rita-codes
    MemberAuthor

    Note on exposure: the dependencies sub-issues (#22854–#22858) will land internal-only — no public props, no public API methods, no docs — so the incremental PRs don't surface a half-built feature to beta users. The feature becomes public in a deliberate final PR once FS works end-to-end. Mechanics in #22854.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

on holdThere is a blocker, we need to wait.plan: PremiumImpact at least one Premium user.scope: schedulerChanges related to the scheduler.type: new featureExpand the scope of the product to solve a new problem.umbrellaFor grouping multiple issues to provide a holistic view

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions