Skip to content

Versioning & company scoping

Workflow definitions are versioned and company-scoped automatically — there is no XML to author for either. The same machinery serves wizards: both share one unified version history table. The implementation lives in Features/Flow/FlowVersionService.cs.

Every Save in the Workflow Designer runs through WorkflowDesignerService.SaveAsync, which:

  1. Parses and validates the XML (a Start node is required).
  2. Calls IFlowVersionService.SnapshotAsync, which mints a fresh 6-character VersionHash and appends a snapshot row to the unified Workflow.FlowVersions table only when the XML changed. Saving with no change keeps the current hash — no duplicate snapshot.
  3. Stores the new XML and hash on the Workflow.Definitions row.

There is no separate publish step — a save is immediately active.

A FlowVersion snapshot row records:

Column Meaning
FlowType Workflow or Wizard — the discriminator that unifies both flow kinds in one table.
DefinitionId Id of the owning definition (Workflow.Definitions or Wizard.Forms).
Name Definition name captured at snapshot time.
VersionHash The 6-char id, also stamped on the definition and on instances/responses.
FlowXml The complete XML for this version (immutable).
Notes Optional author note — a “commit message” describing the change.
CompanyId + audit traits Tenant + CreatedAt/CreatedById.

The hash is generated to be unique among a definition’s existing versions (bounded retry).

Each designer’s History dialog lists every version, newest first (saved time · hash · notes · IsCurrent), backed by GET /api/v1/genie/workflow/definitions/{id}/versions. Selecting a version loads its XML read-only onto the canvas (GET …/versions/{versionHash}).

  • Save prompts for an optional note (“what changed”), stored on the new snapshot.
  • Restore as latest re-saves a past version as a new version (auto-noted Restored from version …) so you can edit it — the older versions are never mutated.

Instance pinning (backward-compatible runs)

Section titled “Instance pinning (backward-compatible runs)”

Each running instance records the VersionHash it started on. At every runtime step the engine resolves that pinned XML from Workflow.FlowVersions (ParsePinnedModelAsyncResolveXmlAsync), so editing a definition never changes how already-running instances behave.

  • Instances started before versioning existed fall back to the definition’s current XML; the engine also lazily mints a first version (Initial version) on start when a definition has no hash yet.
  • The designer’s instance preview renders the pinned XML (falling back to the current XML), so the overlay shows the graph the instance actually runs.

The same guarantee extends to wizards: a submitted wizard response (Wizard.FormResponses) stamps the wizard’s VersionHash, recording exactly which version produced it.

Workflow (and wizard) definitions carry a CompanyId:

  • Designer-created definitions are stamped with the active session company (sessionCompany.GetSessionCompanyId()), so they are only visible there.
  • Definition list views are filtered to the session company, so different companies see and edit their own flows.
  • History snapshots inherit the definition’s company.

See Multi-tenant company scoping for how company scoping is enforced.