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.
VersionHash + unified history
Section titled “VersionHash + unified history”Every Save in the Workflow Designer runs through WorkflowDesignerService.SaveAsync, which:
- Parses and validates the XML (a
Startnode is required). - Calls
IFlowVersionService.SnapshotAsync, which mints a fresh 6-characterVersionHashand appends a snapshot row to the unifiedWorkflow.FlowVersionstable only when the XML changed. Saving with no change keeps the current hash — no duplicate snapshot. - Stores the new XML and hash on the
Workflow.Definitionsrow.
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).
The History dialog
Section titled “The History dialog”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 (ParsePinnedModelAsync → ResolveXmlAsync), 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.
Company scoping
Section titled “Company scoping”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.