Quickstart
Install pyrula-workflows[ui], then scaffold a runnable workflow:
pyrula init --workflowpyrula dev --openThe packages require a source build until their first public release. See Install for the current commands.
Generated workflow
Section titled “Generated workflow”pyrula init --workflow writes app.py: a checkout workflow that reserves inventory, charges a
card, suspends for two seconds, and sends a receipt. Every side effect goes through
ctx.step, so a resumed run reads back what already happened instead of repeating it.
The getting-started quickstart shows the generated file,
console output, and event stream.
Minimal workflow
Section titled “Minimal workflow”The workflow body takes a context (ctx). Decorated steps record their results for
replay.
import asynciofrom pyrula.workflows import step, workflow, InlineWorkflowRunner, MemoryStore
@stepdef build(name: str) -> str: return f"hello, {name}"
@workflow(name="greet")async def greet(ctx, name: str) -> str: return await build(name)
async def main() -> None: runner = InlineWorkflowRunner(store=MemoryStore()) run = await runner.client.submit("greet", {"name": "ada"}) print(await run.result()) # hello, ada for event in await run.events(): print(event.kind, event.payload)
asyncio.run(main())submit queues a run and returns a handle. result() executes it in this process and
returns its value. events() returns the lifecycle and step events. MemoryStore keeps
the stream in process for tests; a Valkey or Redis store preserves it across restarts.
On replay, a completed step returns its recorded result instead of running again. See Steps for the API and Determinism for the replay contract.
Options
Section titled “Options”@workflow accepts execution and event-stream limits. timeout bounds one execution
attempt; the remaining options prevent unbounded event growth.
@workflow( name="report", timeout=600.0, # wall-clock cap per attempt max_event_count_per_run=10_000, max_event_payload_bytes=1_000_000, max_attempt_wall_seconds=3600.0, # also per attempt, NOT a whole-run budget)async def report(ctx): ...Next: Runs as Event Streams.