Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Workflow versioning provides complete change tracking, rollback capability, and an immutable audit trail for compliance. Every time you publish a workflow, a permanent snapshot is created that preserves the exact state of nodes, edges, and canvas viewport.
Key Concepts
Versions vs. Drafts
- Draft Edits: Changes made in the workflow editor are not versioned automatically
- Publishing: Creates an immutable version snapshot with an incremented version number
- Version Numbers: Sequential integers starting at 1: v1, v2, v3, etc.
- Immutability: Once published, versions cannot be edited or deleted
Immutability
Workflow versions are permanently immutable:
- Cannot be edited after creation
- Cannot be deleted (preserves complete audit trail)
- Rollback creates a new version (doesn’t modify the old one)
- All versions remain accessible forever
This ensures regulatory compliance and provides a complete history of workflow changes.
Use Cases
1. Production Issue Recovery
Quickly rollback when workflow changes break production:
// User publishes v2 with bug
await createVersion({ notes: 'Added new approval step' });
// Production breaks - rollback to v1
await rollback({
target_version: 1,
notes: 'Emergency rollback - approval node causing timeout'
});
// Creates v3 (identical to v1) with rollback tag
2. Compliance Audit
Track who changed what and when for regulatory requirements:
- Who:
published_by user ID links to profile
- What: Complete snapshot of workflow state
- When:
published_at timestamp
- Why: Optional
notes field for change descriptions
Auditors can see:
- Version history timeline
- Changes between versions (visual diff)
- Rollback events and reasons
- User attribution for each change
3. Debugging
Compare versions to understand what changed:
const diff = await compareVersions(2, 3);
console.log(diff.nodes.added); // New nodes in v3
console.log(diff.nodes.removed); // Deleted from v2
console.log(diff.nodes.modified); // Changed configuration
console.log(diff.edges.added); // New connections
console.log(diff.edges.removed); // Disconnected paths
4. A/B Testing
Run different workflow versions and compare results:
// Deploy v2 to production
await createVersion({
notes: 'Test new notification timing',
tags: ['production', 'experiment-a']
});
// Later: Deploy v3 with different timing
await createVersion({
notes: 'Alternative notification timing',
tags: ['production', 'experiment-b']
});
// Compare execution results
// SELECT * FROM fw_workflow_executions WHERE workflow_version IN (2, 3)
5. Staged Rollouts
Use tags to manage deployment stages:
// Development
await createVersion({ tags: ['development', 'feature-xyz'] });
// Staging
await createVersion({ tags: ['staging', 'feature-xyz'] });
// Production
await createVersion({ tags: ['production', 'v2.1.0'] });
API Reference
useWorkflowVersions Hook
Complete workflow version management hook.
import { useWorkflowVersions } from '@/cores/fw/hooks/useWorkflowVersions';
const {
versions, // All versions (newest first)
isLoading, // Loading state
error, // Error state
getVersion, // Fetch specific version
createVersion, // Create new version
isCreatingVersion, // Creating state
rollback, // Rollback to version
isRollingBack, // Rolling back state
compareVersions, // Visual diff
} = useWorkflowVersions(ruleId);
Creating a Version
// Minimal
await createVersion({ notes: 'Bug fix' });
// With tags
await createVersion({
notes: 'Added approval gates',
tags: ['production', 'v2.0']
});
// Returns: Version number (e.g., 3)
Rolling Back
await rollback({
target_version: 2,
notes: 'Rollback due to production issue #123'
});
// Creates new version (e.g., v4) identical to v2
// Tags rollback version automatically
Comparing Versions
const diff = await compareVersions(1, 3);
// Returns:
{
versionA: { /* Version 1 data */ },
versionB: { /* Version 3 data */ },
diff: {
nodes: {
added: [/* Nodes added in v3 */],
removed: [/* Nodes removed from v1 */],
modified: [/* Nodes changed between versions */],
},
edges: {
added: [/* Connections added in v3 */],
removed: [/* Connections removed from v1 */],
}
}
}
Getting Specific Version
const version = await getVersion(2);
console.log(version.version); // 2
console.log(version.published_by); // User ID
console.log(version.published_at); // ISO timestamp
console.log(version.workflow_snapshot); // { nodes, edges, viewport }
console.log(version.notes); // Change description
console.log(version.tags); // ['production', 'v1.5']
Database Schema
fw_workflow_versions Table
CREATE TABLE fw_workflow_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES pf_organizations(id),
rule_id UUID NOT NULL REFERENCES fw_automation_rules(id),
version INTEGER NOT NULL,
published_by UUID NOT NULL REFERENCES auth.users(id),
published_at TIMESTAMPTZ NOT NULL DEFAULT now(),
workflow_snapshot JSONB NOT NULL, -- { nodes, edges, viewport }
notes TEXT,
tags TEXT[],
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (rule_id, version)
);
Snapshot Structure:
interface WorkflowSnapshot {
nodes: Array<{
id: string;
type: string;
position: { x: number; y: number };
data: Record<string, any>;
}>;
edges: Array<{
id: string;
source: string;
target: string;
sourceHandle?: string;
targetHandle?: string;
}>;
viewport?: {
x: number;
y: number;
zoom: number;
};
}
Execution Version Linking
The fw_workflow_executions table links each execution to the version used:
CREATE TABLE fw_workflow_executions (
id UUID PRIMARY KEY,
rule_id UUID NOT NULL REFERENCES fw_automation_rules(id),
workflow_version INTEGER, -- Links to version used
status TEXT NOT NULL,
-- ... other fields
);
Query executions by version:
SELECT * FROM fw_workflow_executions
WHERE rule_id = 'rule-uuid'
AND workflow_version = 2;
Best Practices
1. Publish Before Major Changes
Create a version before making risky changes:
// Before refactoring
await createVersion({
notes: 'Pre-refactor checkpoint',
tags: ['checkpoint']
});
// Make changes...
// After testing
await createVersion({
notes: 'Refactored error handling',
tags: ['refactor-complete']
});
2. Add Descriptive Notes
Help future maintainers understand changes:
// ❌ Bad
await createVersion({ notes: 'Changes' });
// ✅ Good
await createVersion({
notes: 'Fixed timeout in approval node (15min → 30min) per ticket #456'
});
Organize versions with consistent tagging:
// Environment tags
tags: ['development']
tags: ['staging']
tags: ['production']
// Release tags
tags: ['v1.0.0', 'production']
tags: ['v1.0.1', 'hotfix']
// Feature tags
tags: ['feature-notifications', 'staging']
// Special markers
tags: ['rollback', 'emergency']
tags: ['checkpoint', 'pre-refactor']
4. Review Diff Before Rollback
Always compare versions before rolling back:
// 1. Compare versions
const diff = await compareVersions(currentVersion, targetVersion);
// 2. Review changes
console.log('Will remove:', diff.nodes.removed.length, 'nodes');
console.log('Will add:', diff.nodes.added.length, 'nodes');
// 3. Confirm and rollback
if (userConfirmed) {
await rollback({ target_version: targetVersion });
}
5. Link Versions to Issues
Reference issue tracking systems in notes:
await createVersion({
notes: 'Implemented multi-level approval (JIRA-1234)',
tags: ['feature-approvals', 'sprint-23']
});
Security & Compliance
Immutability
Versions cannot be modified or deleted:
- No UPDATE policy on
fw_workflow_versions
- No DELETE policy on
fw_workflow_versions
- Even platform admins cannot alter versions
This ensures audit trail integrity for compliance.
Multi-Tenant Isolation
Row-Level Security (RLS) ensures:
- Users can only see versions in their organization
- Cross-tenant version access is blocked
- Execution version links are organization-scoped
Audit Trail
Every version captures:
- Who: User ID of publisher
- When: Timestamp of publication
- What: Complete workflow state
- Why: Optional notes explaining changes
Troubleshooting
Version Creation Fails
Symptom: create_workflow_version() returns error
Causes:
- No workflow definition exists for the rule
- User lacks organization access
- Invalid rule_id
Solution:
// Verify definition exists
const { data } = await supabase
.from('fw_workflow_definitions')
.select()
.eq('rule_id', ruleId)
.single();
if (!data) {
console.error('No workflow definition found');
}
Rollback Creates Wrong Version
Symptom: Rolled back version doesn’t match target
Cause: Snapshot stored incorrectly
Solution:
// Verify target version snapshot
const { data: targetVersion } = await supabase
.from('fw_workflow_versions')
.select('workflow_snapshot')
.eq('version', targetVersionNum)
.single();
console.log('Target nodes:', targetVersion.workflow_snapshot.nodes);
Version Comparison Incorrect
Symptom: Diff shows wrong changes
Cause: Node IDs changed between versions
Solution: Version comparison uses node IDs as identity. If you manually change node IDs, comparison will treat them as different nodes.
Migration Guide
Adding Versioning to Existing Workflows
If you have workflows created before versioning was implemented:
// For each existing workflow:
const { data: definition } = await supabase
.from('fw_workflow_definitions')
.select()
.eq('rule_id', ruleId)
.single();
if (definition) {
// Create initial version (v1)
await supabase.rpc('create_workflow_version', {
p_rule_id: ruleId,
p_published_by: currentUserId,
p_notes: 'Initial version (migrated)',
p_tags: ['migration', 'v1.0'],
});
}