Skip to main content

Node versioning

A saved workflow stores each node's type string literally. If you change a node's schema or behaviour incompatibly, workflows already built against the old version would break. Node versioning lets both versions coexist.

How a version is expressed

A node type belongs to a family and has a version. Two equivalent ways to declare version 2:

// A: suffix the type string
public static function type(): string
{
return 'app::send-slack-message{v2}';
}

// B: leave the type bare, override version()
public static function version(): int
{
return 2;
}

With option B the registry rewrites the stored type to app::send-slack-message{v2} for you. A type with no {vN} suffix and no version() override is version 1 of its own family.

Registering two classes that both claim the same version of the same family throws InvalidArgumentException.

Shipping a breaking change

  1. Keep the existing class registered, unchanged.
  2. Add a new class — same family, version() returning the next number — with the new schema / handle().
  3. Register both.
$registry->register([
SendSlackMessageNode::class, // family app::send-slack-message, v1
SendSlackMessageNodeV2::class, // same family, v2
], 'action');

What each version sees

Behaviour
Existing workflows (type app::send-slack-message)Keep resolving to the v1 class, forever. Their handle(), schema, and label are unchanged.
Existing workflows saved with …{v2}Resolve to the v2 class.
The builder palette / node listingsShow only the latest version per family — new workflows are built against v2.

Exact-type resolution — running a saved workflow, NodeRegistry::resolve(), has(), getLabel(), getResolver() — always honours the literal stored type. Only the listing methods (all(), byCategory(), palette(), search(), …) collapse to the latest version.

Compatibility note

If you have code that enumerates a category to wire up side effects (the pattern the engine uses internally for triggers), be aware that once a v2 of something in that category ships, the public listing methods stop returning the superseded v1. The engine handles its own triggers correctly; audit any equivalent code of your own.