temporal/SKILL.md
Temporal — enterprise-grade durable workflow orchestration platform used by Stripe, Netflix, and Coinbase. Use when building with Temporal: durable workflows, long-running background processes, saga patterns, activity retries and timeouts, signals, queries, updates, worker setup, task queues, Temporal Cloud configuration, or migrating from cron jobs or message queues to reliable orchestration. Covers the TypeScript SDK exclusively.
npx skillsauth add mikkelkrogsholm/dev-skills temporalInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
CRITICAL: Your training data for Temporal is unreliable. APIs change between versions and memorized patterns may be wrong or deprecated. Before writing any code, you MUST use
WebFetchto read the live docs:
WebFetch("https://docs.temporal.io/llms.txt")Do not proceed without fetching this URL first. Never assume an API exists — verify against current docs.
Temporal is an open-source durable workflow orchestration platform that makes long-running, fault-tolerant processes simple by automatically persisting execution state and replaying it after failures.
@temporalio/client — start workflows, send signals/updates, query state
@temporalio/worker — run Workers that poll task queues
@temporalio/workflow — define Workflow functions (runs in isolate)
@temporalio/activity — define Activity functions (runs in normal Node.js)
src/
workflows.ts # Workflow definitions — NO Node.js built-ins
activities.ts # Activity definitions — normal Node.js allowed
worker.ts # Worker bootstrap (imports both)
client.ts # Client code to start/signal workflows
activities.ts
import { Context } from '@temporalio/activity';
export async function greetUser(name: string): Promise<string> {
// Safe to do I/O here — activities run in normal Node.js
return `Hello, ${name}! Processed at ${new Date().toISOString()}`;
}
workflows.ts
import { proxyActivities, defineSignal, defineQuery, setHandler, sleep } from '@temporalio/workflow';
import type * as activities from './activities';
// proxyActivities MUST be called inside the workflow module scope
// but the proxy itself is used inside workflow functions
const { greetUser } = proxyActivities<typeof activities>({
startToCloseTimeout: '10 seconds',
retry: { maximumAttempts: 3 },
});
export const approvalSignal = defineSignal<[boolean]>('approval');
export const statusQuery = defineQuery<string>('status');
export async function greetingWorkflow(name: string): Promise<string> {
let approved = false;
let status = 'waiting';
setHandler(approvalSignal, (isApproved: boolean) => {
approved = isApproved;
status = isApproved ? 'approved' : 'rejected';
});
setHandler(statusQuery, () => status);
// Wait up to 24 hours for approval signal
await condition(() => approved, '24 hours');
if (!approved) {
return 'Workflow rejected';
}
// Side effect delegated to an Activity
return await greetUser(name);
}
worker.ts
import { Worker } from '@temporalio/worker';
import * as activities from './activities';
async function run() {
const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
activities,
taskQueue: 'greeting-queue', // must match client
});
await worker.run();
}
run().catch(console.error);
client.ts
import { Client, Connection } from '@temporalio/client';
import { greetingWorkflow, approvalSignal, statusQuery } from './workflows';
async function main() {
const client = new Client();
const handle = await client.workflow.start(greetingWorkflow, {
args: ['Alice'],
taskQueue: 'greeting-queue', // must match worker
workflowId: 'greeting-alice-001',
});
// Send a signal to the running workflow
await handle.signal(approvalSignal, true);
// Query current state (synchronous read)
const status = await handle.query(statusQuery);
console.log('Status:', status);
// Await the final result
const result = await handle.result();
console.log('Result:', result);
}
main().catch(console.error);
import { Client, Connection } from '@temporalio/client';
import { Worker, NativeConnection } from '@temporalio/worker';
import * as fs from 'fs';
// Client connection
const connection = await Connection.connect({
address: 'your-namespace.tmprl.cloud:7233',
tls: {
clientCertPair: {
crt: fs.readFileSync('client.pem'),
key: fs.readFileSync('client.key'),
},
},
});
const client = new Client({ connection, namespace: 'your-namespace.your-account' });
// Worker connection
const nativeConnection = await NativeConnection.connect({
address: 'your-namespace.tmprl.cloud:7233',
tls: {
clientCertPair: {
crt: fs.readFileSync('client.pem'),
key: fs.readFileSync('client.key'),
},
},
});
const worker = await Worker.create({
connection: nativeConnection,
namespace: 'your-namespace.your-account',
taskQueue: 'my-queue',
workflowsPath: require.resolve('./workflows'),
activities,
});
const { processPayment } = proxyActivities<typeof activities>({
// At least one of these is required — defaults are infinite
startToCloseTimeout: '30 seconds', // max time for a single attempt
scheduleToCloseTimeout: '5 minutes', // max total time across all retries
retry: {
initialInterval: '1 second',
backoffCoefficient: 2,
maximumInterval: '30 seconds',
maximumAttempts: 5,
nonRetryableErrorTypes: ['ValidationError'],
},
});
Workflow code must be deterministic. Never use Math.random(), Date.now(), new Date(), setTimeout, setInterval, or any direct I/O inside a workflow function. All non-deterministic behavior and side effects must live in Activities. Use sleep() from @temporalio/workflow instead of setTimeout, and condition() instead of polling.
Workflow and Activity code must be in separate files. Workflow code runs inside a V8 isolate that blocks all Node.js built-ins (fs, http, crypto, etc.). Importing an activity module (which uses those built-ins) into a workflow file will crash the Worker at startup. Keep them strictly separated.
proxyActivities must be called at the top of the workflow module, not inside the workflow function body. The proxy binds to the workflow's execution context. Calling it inside the function body will throw at runtime.
Signals are fire-and-forget — no reply. If you need the caller to receive a response from the workflow, use defineUpdate (available since SDK v1.9) instead of a signal. Updates block the caller until the workflow handler validates and returns a result.
Task Queue names are case-sensitive and must match exactly. The taskQueue on Worker.create() and the taskQueue on client.workflow.start() must be identical strings. A mismatch creates two isolated queues; the workflow will queue indefinitely with no error.
Activity timeouts default to infinite — always set them explicitly. An Activity with no startToCloseTimeout or scheduleToCloseTimeout will hang forever if it stalls. Set both at the proxyActivities call site. Prefer startToCloseTimeout for per-attempt limits and add scheduleToCloseTimeout as a hard cap.
development
Zod — TypeScript-first schema validation with static type inference. Use when building with Zod or asking about schema definitions, type inference, parsing, transformations, refinements, coercion, error handling, or integration with forms, APIs, or tRPC. Fetch live documentation for up-to-date details.
tools
Vite — next-generation frontend build tool with instant dev server and optimized production builds. Use when building with Vite or asking about its APIs, configuration, plugins, SSR, environment variables, or integration with frameworks. Fetch live documentation for up-to-date details.
tools
Upstash — serverless Redis, QStash, and Vector database with per-request pricing optimized for edge and serverless environments. Use when building with Upstash or asking about its Redis client, QStash message queuing, rate limiting, workflows, or vector search. Fetch live documentation for up-to-date details.
tools
Turso — edge-hosted SQLite database built on libSQL with embedded replicas, multi-tenancy, and low-latency global distribution. Use when building with Turso or asking about its libSQL client, embedded replicas, database-per-tenant patterns, auth tokens, sync, or integration with Drizzle or other ORMs. Fetch live documentation for up-to-date details.