The smallest useful RunnerQ program: a durable two-step workflow.
A workflow is just an activity handler. Inside it, each ctx.RunStep(name, fn)
is a step whose typed result is checkpointed in Postgres. A step whose
result was recorded never re-runs — if the process restarted between them,
the first would replay from its checkpoint and the second would resume. A
crash between the side effect and the checkpoint commit does re-run fn,
so keep external effects idempotent. (Example 02 proves the replay; here we
just run it through.)
docker compose up -d # from the examples/ directory, if not already up
go run .starting signup workflow...
▶ creating account for ada@example.com
▶ sending welcome email to ada@example.com
✓ signup complete: {"email":"ada@example.com","user_id":"u_1001"}
func (h *SignupWorkflow) Handle(ctx runnerq.ActivityContext, payload json.RawMessage) (json.RawMessage, error) {
user, err := ctx.RunStep("create-account", func(c context.Context) (Account, error) {
return createAccount(c, payload) // checkpointed: runs at most once
})
if err != nil { return nil, err }
_, err = ctx.RunStep("send-welcome", func(c context.Context) (bool, error) {
return true, sendWelcome(c, user) // only after the account is durable
})
if err != nil { return nil, err }
return json.Marshal(user)
}Next: 02 — Crash & Resume shows why the checkpoints matter.