[TERMINAL · SKILLS]
> mounting /skills...
> indexing 295 manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Terminal.skills
Use Cases/Build a Workflow Engine with State Machines

Build a Workflow Engine with State Machines

Build a visual workflow engine using XState state machines — modeling approval flows, order lifecycles, and onboarding sequences with explicit states, transitions, guards, and audit trails.

#redis#caching#database#pub-sub#queues
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 93/100·All SAFE
>

typescript

v

Not yet scored
View skill
>

redis

v1.0.0

Build applications with Redis — caching, session storage, pub/sub, streams, rate limiting, leaderboards, and queues. Use when tasks involve in-memory data storage, real-time messaging, distributed locking, or performance optimization with caching layers.

93/100 quality
1.81× impact
SAFE
View skill
>

postgresql

v1.0.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

87/100 quality
1.53× impact
SAFE
View skill
>

hono

v1.0.0

You are an expert in Hono, the ultrafast web framework for the edge. You help developers build APIs and web applications that run on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, and Vercel Edge — with a tiny footprint (~14KB), middleware ecosystem, JSX support, RPC client, and Web Standards API compatibility that makes code truly portable across runtimes.

93/100 quality
3.00× impact
SAFE
View skill
>

zod

v1.0.0

You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.

100/100 quality
1.21× impact
SAFE
View skill
$

The Problem

Ivan leads operations at a 40-person company. Approval workflows are hardcoded with nested if/else chains — a purchase order goes through "requested → manager approval → finance approval → CEO approval (if >$10K) → approved." The code is 800 lines of spaghetti. When they added a "legal review" step, it took 3 weeks and introduced 4 bugs. Nobody can visualize the current flow. Orders get stuck in limbo because edge cases (rejected → re-submitted → re-approved) weren't handled. They need a state machine that makes workflows explicit, visual, and impossible to enter invalid states.

Step 1: Build the State Machine Workflow Engine

typescript
// src/workflows/engine.ts — Generic workflow engine using state machines
import { createMachine, interpret, State, StateMachine } from "xstate";
import { pool } from "../db";
import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);

interface WorkflowDefinition {
  id: string;
  name: string;
  machine: any; // XState machine config
  version: number;
}

interface WorkflowInstance {
  id: string;
  definitionId: string;
  currentState: string;
  context: Record<string, any>;
  history: Array<{
    from: string;
    to: string;
    event: string;
    actor: string;
    timestamp: string;
    metadata?: Record<string, any>;
  }>;
  createdAt: string;
  updatedAt: string;
}

// Purchase Order Approval Workflow
const purchaseOrderMachine = createMachine({
  id: "purchase-order",
  initial: "draft",
  context: {
    amount: 0,
    requesterId: "",
    approvals: [] as string[],
    rejectionReason: "",
  },
  states: {
    draft: {
      on: {
        SUBMIT: {
          target: "pending_manager",
          guard: "hasRequiredFields",
        },
      },
    },
    pending_manager: {
      on: {
        APPROVE: {
          target: "pending_finance",
          actions: "recordApproval",
        },
        REJECT: {
          target: "rejected",
          actions: "recordRejection",
        },
        REQUEST_INFO: "needs_info",
      },
    },
    needs_info: {
      on: {
        PROVIDE_INFO: "pending_manager",
        WITHDRAW: "withdrawn",
      },
    },
    pending_finance: {
      on: {
        APPROVE: [
          {
            target: "pending_ceo",
            guard: "requiresCEOApproval", // amount > $10,000
            actions: "recordApproval",
          },
          {
            target: "pending_legal",
            guard: "requiresLegalReview", // certain categories
            actions: "recordApproval",
          },
          {
            target: "approved",
            actions: "recordApproval",
          },
        ],
        REJECT: { target: "rejected", actions: "recordRejection" },
      },
    },
    pending_ceo: {
      on: {
        APPROVE: [
          { target: "pending_legal", guard: "requiresLegalReview", actions: "recordApproval" },
          { target: "approved", actions: "recordApproval" },
        ],
        REJECT: { target: "rejected", actions: "recordRejection" },
      },
    },
    pending_legal: {
      on: {
        APPROVE: { target: "approved", actions: "recordApproval" },
        REJECT: { target: "rejected", actions: "recordRejection" },
        REQUEST_CHANGES: "pending_revision",
      },
    },
    pending_revision: {
      on: {
        RESUBMIT: "pending_legal",
        WITHDRAW: "withdrawn",
      },
    },
    approved: {
      on: {
        FULFILL: "fulfilled",
        CANCEL: "cancelled",
      },
      type: "final" as const,
    },
    rejected: {
      on: {
        RESUBMIT: "draft",
      },
    },
    withdrawn: { type: "final" as const },
    fulfilled: { type: "final" as const },
    cancelled: { type: "final" as const },
  },
}, {
  guards: {
    hasRequiredFields: ({ context }) => context.amount > 0 && context.requesterId !== "",
    requiresCEOApproval: ({ context }) => context.amount > 10000,
    requiresLegalReview: ({ context }) => context.amount > 50000,
  },
  actions: {
    recordApproval: ({ context, event }) => {
      context.approvals.push(event.actor);
    },
    recordRejection: ({ context, event }) => {
      context.rejectionReason = event.reason || "No reason provided";
    },
  },
});

// Create a new workflow instance
export async function createWorkflow(
  definitionId: string,
  initialContext: Record<string, any>
): Promise<WorkflowInstance> {
  const id = `wf-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;

  const machine = getMachineForDefinition(definitionId);
  const initialState = machine.initialState;

  const instance: WorkflowInstance = {
    id,
    definitionId,
    currentState: initialState.value as string,
    context: { ...initialContext },
    history: [],
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };

  await pool.query(
    `INSERT INTO workflow_instances (id, definition_id, current_state, context, history, created_at)
     VALUES ($1, $2, $3, $4, $5, NOW())`,
    [id, definitionId, instance.currentState, JSON.stringify(instance.context), JSON.stringify(instance.history)]
  );

  return instance;
}

// Send an event to a workflow (transition)
export async function sendEvent(
  instanceId: string,
  event: string,
  actor: string,
  metadata?: Record<string, any>
): Promise<{ newState: string; allowed: boolean; instance: WorkflowInstance }> {
  const { rows: [row] } = await pool.query(
    "SELECT * FROM workflow_instances WHERE id = $1 FOR UPDATE",
    [instanceId]
  );

  if (!row) throw new Error("Workflow not found");

  const machine = getMachineForDefinition(row.definition_id);
  const currentState = State.from(row.current_state, JSON.parse(row.context));

  // Check if transition is valid
  const nextState = machine.transition(currentState, {
    type: event,
    actor,
    ...metadata,
  });

  if (nextState.value === currentState.value && !nextState.changed) {
    return {
      newState: row.current_state,
      allowed: false,
      instance: row,
    };
  }

  const newStateName = nextState.value as string;
  const history = JSON.parse(row.history);
  history.push({
    from: row.current_state,
    to: newStateName,
    event,
    actor,
    timestamp: new Date().toISOString(),
    metadata,
  });

  await pool.query(
    `UPDATE workflow_instances SET current_state = $2, context = $3, history = $4, updated_at = NOW()
     WHERE id = $1`,
    [instanceId, newStateName, JSON.stringify(nextState.context), JSON.stringify(history)]
  );

  // Publish state change
  await redis.publish("workflow:transitions", JSON.stringify({
    instanceId, from: row.current_state, to: newStateName, event, actor,
  }));

  return {
    newState: newStateName,
    allowed: true,
    instance: { ...row, currentState: newStateName, history },
  };
}

// Get available transitions for current state
export async function getAvailableActions(instanceId: string): Promise<string[]> {
  const { rows: [row] } = await pool.query(
    "SELECT definition_id, current_state, context FROM workflow_instances WHERE id = $1",
    [instanceId]
  );

  const machine = getMachineForDefinition(row.definition_id);
  const state = State.from(row.current_state, JSON.parse(row.context));
  return state.nextEvents;
}

function getMachineForDefinition(definitionId: string): StateMachine<any, any, any> {
  const machines: Record<string, any> = {
    "purchase-order": purchaseOrderMachine,
  };
  return machines[definitionId] || purchaseOrderMachine;
}

Results

  • 800 lines of if/else replaced by 80 lines of state machine — each state and transition is explicit; impossible to enter invalid states
  • Adding "legal review" step: 3 weeks → 2 hours — add a new state, define transitions to/from it; the engine handles everything else
  • No more stuck orders — every state has explicit exit transitions; the dashboard shows which step each order is on and who needs to act
  • Full audit trail — every transition is logged with who, when, and why; "who approved this $50K purchase?" is a single query
  • Available actions are dynamic — the UI shows only valid buttons for the current state; users can't click "Approve" when the order is in "draft"