Skip to content

[scheduler] Dependencies: arrow editor (type + lag) and immediate cascade on create/edit #23395

Closed

Description

@rita-codes

Part of #22853 (event-scheduling umbrella). Depends on #23393 (PR #23623) for the lag and the SS/FF/SF engine.

An editor to change an existing dependency's type and lag from the UI. It is the explicit counterpart of the edge terminals (#23394), where the type comes from the drag gesture, and the only UI to set a lag.

Design

  • Entry point: mirrors the event gestures. Click selects the arrow (already shipped), double click opens the editor, right click opens a context menu with Edit dependency / Delete (like [scheduler] Add EventContextMenu with Edit and Delete actions #23379's EventContextMenu).
  • Hover style: hovering an arrow gives it a distinct style. It shows which arrow the double click will open and makes a dense graph easier to read. Today only the selected arrow is styled.
  • Surface: a dialog built from the same pieces as the event dialog: anchored at the double-click position, draggable and non-modal, open state in the store. The arrow and its two events stay visible while editing.
  • Fields:
    • The source and target event titles, labelled From and To, displayed as plain text (not inputs), so the user knows which arrow they are editing. Re-linking is delete + recreate.
    • Type: FS / SS / FF / SF.
    • Lag: a whole number ≥ 0 and a unit (minute | hour | day | week, default day), calendar time in the successor's timezone. The editor can never produce a lag that getDependencyLagIssue rejects.
    • A lag from props that the engine ignores (negative, not a whole number, unknown unit) shows as unset, since it is not applied.
    • On an all-day successor the lag is carried in whole days, rounded down (getEffectiveDependencyLag). The editor shows the wait that actually applies, so 36 hours does not silently become 1 day.
  • Rejections: an edit keeps the source → target pair, so it cannot create a cycle. It can hit a duplicate (source, target, type) when changing the type, and the read-only veto below. Both go through the same result contract as addDependency.

Read-only dependencies

Today a dependency is read-only when one of its events is (isDependencyReadOnly, which folds in the global readOnly, resource.areEventsReadOnly and the event's readOnly). That blocks too much: a dependency never moves its predecessor, and a successor only has to move when the constraint breaks, which the cascade veto already rejects. A read-only event locks its own dates and data, not the arrows attached to it.

This issue separates the two:

  • A dependency is read-only only when the whole scheduler is readOnly. A dependency-level readOnly field can come later if needed. A read-only dependency behaves like a read-only event: the context menu shows "Show details" instead of Edit and has no Delete, and the dialog shows the type and lag without letting the user edit them.
  • A read-only event only counts through the veto: any create or edit whose cascade would have to move it is rejected. A lag the slack absorbs, or a type change that breaks nothing, goes through.

The rule applies to creation and deletion too, which are already shipped: read-only events get terminals and are valid drop targets, and addDependency / deleteDependency stop rejecting on a read-only endpoint.

Cascade on create and edit

If a create or an edit leaves the constraint broken, the target is pushed immediately and the cascade continues behind it, like dragging a predecessor does. Data arriving via props is still rendered as-is, never rewritten.

Today the engine is only seeded by events whose dates change, and a seed with unchanged dates pushes nothing, so a dependency change needs its own way in:

  • The engine takes an optional enforcedEdges (the created or edited dependency). In collectBounds, an edge constrains when its source's bounding edge advanced in the batch or it is enforced. Discovery also starts from the enforced edge's target. Everything else (Kahn, push-only, all-day whole days, read-only veto) is unchanged. Only that edge is enforced, so pre-existing violations on other arrows stay as-is.
  • dependencies is fully controlled: the new or edited edge is not in the state until the parent re-renders. So it is passed to the engine explicitly, not read from the state.
  • One engine run: the cascade is computed first, then the dependency change and the event moves are emitted together. If the cascade would move a read-only event, nothing is emitted (neither onDependenciesChange nor the event updates) and the rejection is surfaced like the drag veto. The event updates must not trigger a second run: the pushed target would be treated as a repositioned seed and clamped by all its predecessors, repairing violations on other arrows.

Out of scope

Tests

  • Hovering an arrow applies the hover style to that arrow only.
  • Double click an arrow → change type → the arrow re-renders and the constraint is enforced.
  • A lag that breaks the constraint pushes the target; slack absorbs a lag that does not.
  • Creating an arrow whose constraint is born broken pushes the target.
  • Editing one arrow leaves pre-existing violations on other arrows (including other arrows into the same target) untouched.
  • A cascade into a read-only event rejects the whole change.
  • A type change that duplicates an existing dependency is rejected.
  • With the scheduler readOnly, an arrow offers "Show details" and no Delete, and its dialog cannot be edited.
  • An arrow into or out of a read-only event can be created, edited and deleted, as long as the read-only event does not have to move.
  • An invalid lag from props shows as unset; an all-day successor shows the rounded-down wait.

Activity

  1. added
    type: new featureExpand the scope of the product to solve a new problem.
    plan: PremiumImpact at least one Premium user.
    scope: schedulerChanges related to the scheduler.
    on Aug 21, 2026
  2. self-assigned this
    on Aug 21, 2026
  3. changed the title [-][scheduler] Dependencies: arrow editor (type + lag)[/-] [+][scheduler] Dependencies: arrow editor (type + lag) and immediate cascade on create/edit[/+] on Aug 25, 2026
  4. github-actions commented on Oct 20, 2026

    @github-actions
    Contributor

    This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue.
    Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.

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

Metadata

Metadata

Assignees

Labels

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.

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions