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

Build a Workflow State Machine

Build a workflow state machine with configurable states, transition guards, side effects, history tracking, and visualization for modeling complex business processes.

#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

Eva leads engineering at a 25-person company. Order processing has 8 states (draft→submitted→approved→processing→shipped→delivered→returned→cancelled) with complex rules: only managers can approve orders over $10K, cancelled orders can't be re-submitted, returned orders trigger refunds. This logic is scattered across 15 if/else blocks in 6 files. A bug allowed cancelled orders to be shipped. State transitions aren't logged — "how did this order end up in 'returned' state?" is unanswerable. They need a state machine: define states and transitions in one place, guard conditions, side effects on transition, and full history.

Step 1: Build the State Machine

typescript
import { pool } from "../db";
import { Redis } from "ioredis";
import { randomBytes } from "node:crypto";
const redis = new Redis(process.env.REDIS_URL!);

type GuardFn = (entity: any, context: any) => boolean | Promise<boolean>;
type EffectFn = (entity: any, transition: TransitionEvent) => void | Promise<void>;

interface State { name: string; onEnter?: EffectFn; onExit?: EffectFn; metadata?: Record<string, any>; }
interface Transition { from: string; to: string; event: string; guard?: GuardFn; effect?: EffectFn; }
interface TransitionEvent { from: string; to: string; event: string; entityId: string; userId: string; metadata?: Record<string, any>; timestamp: string; }

interface StateMachineDefinition { name: string; states: State[]; transitions: Transition[]; initialState: string; }

// Define order workflow
const ORDER_WORKFLOW: StateMachineDefinition = {
  name: "order",
  initialState: "draft",
  states: [
    { name: "draft" }, { name: "submitted" }, { name: "approved" },
    { name: "processing" }, { name: "shipped" }, { name: "delivered" },
    { name: "returned" }, { name: "cancelled" },
  ],
  transitions: [
    { from: "draft", to: "submitted", event: "submit" },
    { from: "submitted", to: "approved", event: "approve", guard: async (entity, ctx) => {
      if (entity.total > 10000) return ctx.userRole === "manager" || ctx.userRole === "admin";
      return true;
    }},
    { from: "submitted", to: "cancelled", event: "cancel" },
    { from: "approved", to: "processing", event: "start_processing" },
    { from: "approved", to: "cancelled", event: "cancel" },
    { from: "processing", to: "shipped", event: "ship", effect: async (entity) => {
      await redis.rpush("notification:queue", JSON.stringify({ type: "order_shipped", orderId: entity.id, customerId: entity.customerId }));
    }},
    { from: "shipped", to: "delivered", event: "deliver" },
    { from: "delivered", to: "returned", event: "return", effect: async (entity) => {
      await redis.rpush("refund:queue", JSON.stringify({ orderId: entity.id, amount: entity.total }));
    }},
  ],
};

const workflows = new Map<string, StateMachineDefinition>();
workflows.set("order", ORDER_WORKFLOW);

// Transition entity to new state
export async function transition(workflowName: string, entityId: string, event: string, context: { userId: string; userRole: string; metadata?: Record<string, any> }): Promise<TransitionEvent> {
  const workflow = workflows.get(workflowName);
  if (!workflow) throw new Error(`Workflow '${workflowName}' not found`);

  // Get current state
  const { rows: [entity] } = await pool.query(`SELECT * FROM ${workflowName}s WHERE id = $1`, [entityId]);
  if (!entity) throw new Error(`Entity '${entityId}' not found`);
  const currentState = entity.status || entity.state;

  // Find matching transition
  const trans = workflow.transitions.find((t) => t.from === currentState && t.event === event);
  if (!trans) throw new Error(`Invalid transition: '${event}' from state '${currentState}'. Allowed events: ${workflow.transitions.filter((t) => t.from === currentState).map((t) => t.event).join(", ")}`);

  // Check guard
  if (trans.guard) {
    const allowed = await trans.guard(entity, context);
    if (!allowed) throw new Error(`Transition '${event}' denied by guard condition`);
  }

  // Execute onExit of current state
  const currentStateDef = workflow.states.find((s) => s.name === currentState);
  if (currentStateDef?.onExit) await currentStateDef.onExit(entity, {} as any);

  // Update state
  await pool.query(`UPDATE ${workflowName}s SET status = $2 WHERE id = $1`, [entityId, trans.to]);

  const transitionEvent: TransitionEvent = { from: currentState, to: trans.to, event, entityId, userId: context.userId, metadata: context.metadata, timestamp: new Date().toISOString() };

  // Execute transition effect
  if (trans.effect) await trans.effect(entity, transitionEvent);

  // Execute onEnter of new state
  const newStateDef = workflow.states.find((s) => s.name === trans.to);
  if (newStateDef?.onEnter) await newStateDef.onEnter(entity, transitionEvent);

  // Log transition
  await pool.query(
    `INSERT INTO state_transitions (entity_type, entity_id, from_state, to_state, event, user_id, metadata, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())`,
    [workflowName, entityId, currentState, trans.to, event, context.userId, JSON.stringify(context.metadata || {})]
  );

  return transitionEvent;
}

// Get available events for current state
export function getAvailableEvents(workflowName: string, currentState: string): string[] {
  const workflow = workflows.get(workflowName);
  if (!workflow) return [];
  return workflow.transitions.filter((t) => t.from === currentState).map((t) => t.event);
}

// Get state history for an entity
export async function getHistory(entityType: string, entityId: string): Promise<TransitionEvent[]> {
  const { rows } = await pool.query(
    "SELECT * FROM state_transitions WHERE entity_type = $1 AND entity_id = $2 ORDER BY created_at ASC",
    [entityType, entityId]
  );
  return rows.map((r: any) => ({ from: r.from_state, to: r.to_state, event: r.event, entityId: r.entity_id, userId: r.user_id, metadata: JSON.parse(r.metadata || "{}"), timestamp: r.created_at }));
}

// Visualize workflow as Mermaid diagram
export function visualize(workflowName: string): string {
  const workflow = workflows.get(workflowName);
  if (!workflow) return "";
  let mermaid = "stateDiagram-v2\n";
  mermaid += `  [*] --> ${workflow.initialState}\n`;
  for (const t of workflow.transitions) {
    mermaid += `  ${t.from} --> ${t.to}: ${t.event}${t.guard ? " [guarded]" : ""}\n`;
  }
  return mermaid;
}

Results

  • Cancelled orders can't ship — no transition from "cancelled" to "shipped" defined; state machine throws clear error; bug impossible by design
  • $10K approval guard — orders over $10K require manager role; regular users get "denied by guard condition"; business rule enforced in one place
  • Side effects automated — shipping triggers customer notification; return triggers refund queue; no forgotten side effects across 6 files
  • Full history — "how did order #123 end up returned?" → history shows: draft→submitted(by Alice)→approved(by Bob)→processing→shipped→delivered→returned(by customer); every step traced
  • Available actions clear — UI shows only valid buttons for current state; no "Ship" button on cancelled orders; UX matches business logic