OSS-first docs
These docs teach the open system first: contracts, generated surfaces, runtimes, governance, and incremental adoption. Studio shows up as the operating layer on top, not as the source of truth.
Workflows
A WorkflowSpec orchestrates multi-step processes. It defines the sequence of operations, handles failures with retries and compensation, and provides observability into long-running tasks.
Core concepts
Identifiers
Each workflow has a unique workflowId and a version. This allows you to run multiple versions of the same workflow simultaneously during migrations or A/B tests.
Steps
A workflow is composed of steps. Each step invokes a CapabilitySpec, passes inputs, and receives outputs. Steps can run sequentially or in parallel.
Transitions
Transitions define the flow between steps. They can be conditional (e.g., "if payment succeeds, go to step 3; otherwise, go to step 5") or unconditional.
Retries
If a step fails, the workflow can retry it with exponential backoff. You specify the maximum number of retries and the backoff strategy in the spec.
Compensation
When a workflow fails partway through, compensation steps undo the effects of completed steps (e.g., refunding a payment, releasing a reservation). This ensures consistency even in failure scenarios.
SLAs
You can define Service Level Agreements (SLAs) for each step or the entire workflow. If a step exceeds its SLA, the system can trigger alerts or escalations.
Example WorkflowSpec (TypeScript)
Here's a simplified example of a payment workflow in TypeScript:
import { defineWorkflow } from '@contractspec/lib.contracts-spec/workflow';
import { ValidatePaymentMethod, ChargePayment, SendEmail } from './specs';
export const PaymentFlow = defineWorkflow({
meta: {
key: 'payment.flow',
version: '1.0.0',
description: 'End-to-end payment processing',
owners: ['team-payments'],
tags: ['payments'],
stability: 'stable',
},
steps: [
{
id: 'validate-payment',
operation: ValidatePaymentMethod,
inputs: (ctx, input) => ({
userId: ctx.userId,
paymentMethodId: input.paymentMethodId,
}),
retry: {
maxAttempts: 3,
backoff: 'exponential',
},
onSuccess: 'charge-payment',
onFailure: 'notify-user',
},
{
id: 'charge-payment',
operation: ChargePayment,
inputs: (ctx, input, steps) => ({
amount: input.amount,
paymentMethodId: input.paymentMethodId,
}),
compensation: 'refund-payment',
onSuccess: 'send-receipt',
onFailure: 'notify-admin',
},
{
id: 'send-receipt',
operation: SendEmail,
inputs: (ctx, input, steps) => ({
to: ctx.userEmail,
template: 'payment-receipt',
data: steps['charge-payment'].output,
}),
},
],
sla: {
maxDuration: 30000, // milliseconds
alertOnBreach: true,
},
});Triggers
Workflows can be triggered in several ways:
- Manual invocation – A user or system calls the workflow via an API endpoint.
- Event-driven – The workflow starts automatically when a specific event occurs (e.g., a new order is created).
- Scheduled – The workflow runs on a cron schedule (e.g., nightly batch processing).
- Chained – One workflow can invoke another as a step.
Monitoring and versioning
ContractSpec automatically instruments workflows with telemetry. You can view:
- Real-time execution status for each step
- Historical run data and success/failure rates
- Latency distributions and SLA compliance
- Compensation events and retry attempts
When you update a workflow, you increment its version. Running workflows continue on their original version, while new invocations use the latest version. This allows safe, zero-downtime deployments.
Best practices
- Keep steps idempotent – they should be safe to retry without side effects.
- Define compensation for any step that modifies external state.
- Use meaningful step IDs that describe the operation.
- Set realistic SLAs and monitor them in production.
- Test failure scenarios locally before deploying.
Data views
Define queryable, presentable views that stay aligned with the rest of the system.
Policy
Apply consistent governance, access rules, and risk controls across every surface.
Why ContractSpec
Keep educational and comparison content reachable without letting it define the primary OSS learning path.