1---2name: resonate-migrate-from-temporal3description: Port a Temporal application to Resonate, pattern by pattern. Use when migrating Temporal Workflows/Activities, Signals, timers, sagas, continue-as-new loops, fan-out/fan-in, child workflows, distributed mutex, or encryption to the Resonate SDK. Maps canonical temporalio/samples-* examples to their Resonate equivalents across TypeScript, Python, Rust, and Go, with per-SDK API notes and honest coverage gaps. Foundational skill — delegates to the per-SDK pattern skills for idiomatic target code.4license: Apache-2.05---67# Migrate from Temporal to Resonate89A pattern-by-pattern playbook for porting a Temporal application to Resonate.10Identify which Temporal construct each piece of the source uses, apply the11matching transform, and reach for the linked per-SDK skill for idiomatic target12code.1314## Ground rules1516- **Never invent Temporal code.** Quote it from the user's source or a named17 `temporalio/samples-*` file. If you can't find it, say so.18- **Resonate has no `@workflow`/`@activity` split.** A step is just a function19 made durable by `ctx.run`. Don't invent decorators.20- **Temporal Rust samples live in-repo.** Temporal's Rust SDK ships examples in21 `temporalio/sdk-rust/crates/sdk/examples/` (no separate `samples-rust` repo) —22 source Rust migrations from there (hello_world, child_workflows, timer_examples,23 message_passing, saga, continue_as_new, …). Only mutex and encryption have no24 Temporal Rust example; for those, source from the Temporal *TypeScript* idiom.25- **State coverage honestly.** A missing example ≠ impossible; it means no worked26 reference exists yet.27- **Verify APIs against the pinned version before emitting** — the SDK surface28 drifts between versions.2930## Pinned SDK versions (latest released at time of writing)3132| SDK | Version | Source |33|---|---|---|34| TypeScript | `@resonatehq/sdk` v0.11.4 | npm |35| Python | `resonate-sdk` v0.7.4 | PyPI |36| Rust | `resonate-sdk` v0.6.0 | crates.io |37| Go | `0.1.0` (tag has no `v` prefix — `go get github.com/resonatehq/resonate-sdk-go@0.1.0`) | GitHub |3839## Core mappings (apply everywhere)4041| Temporal | Resonate |42|---|---|43| `@workflow.defn` / Workflow type | a registered function (`@resonate.register`, `resonate.register("name", fn)`, `#[resonate::function]`, `resonate.Register(r, "name", fn)`) |44| `@activity.defn` / Activity | a plain function invoked via `ctx.run(fn, args)` |45| `workflow.execute_activity(fn, …, start_to_close_timeout=…)` | `await ctx.run(fn, args)` (py, ts async engine) / `yield* ctx.run(fn, args)` (ts generator engine) / `ctx.run(fn, args).await?` (rs) / `ctx.Run(fn, args)` then `f.Await(&out)` (go) — no timeout policy required |46| Task Queue + Worker wiring | a worker `group` (only when you need distributed dispatch) |47| `executeChild` / `ExecuteChildWorkflow(ChildType, …)` | `ctx.rpc("name", args)` or `ctx.run(fn, args)` — invoke by registered name; recursion is trivial |48| `Promise.all` / `asyncio.gather` / parallel futures (fan-out) | start each non-blocking (`ctx.beginRun` / `ctx.rfi` / `.spawn()` / `ctx.RPC`), then await each |49| `defineSignal` + `setHandler` + `condition` | one latent durable promise: `p = ctx.promise()` then await `p` |50| `defineQuery` / query handler | delete — promise/result state is the source of truth |51| `handle.signal(sig)` (external) | `resonate.promises.resolve(id, value)` (HTTP-addressable from anywhere) |52| `workflow.sleep` / `NewTimer` | `ctx.sleep(duration)` |53| saga compensation stack + drain-on-catch | inline `ctx.run(undo, …)` in the error branch, guarded by what completed |54| `continueAsNew` | bounded loop: a plain loop. Unbounded loop: `ctx.detached(self, n+1)` tail-recursion |5556---5758## Pattern: Workflow + activity5960- **DETECT:** `@workflow.defn`/`@activity.defn` (py), `proxyActivities` (ts),61 `RegisterWorkflow`+`RegisterActivity` / `workflow.ExecuteActivity` (go).62- **TRANSFORM:** Register one function. Turn each activity into a plain function63 called via `ctx.run`. Drop Task Queue wiring and `start_to_close_timeout`.64- **TEMPORAL SOURCE:** `samples-{python,typescript,go}/hello-world`65 (py: `hello/hello_activity.py`).66- **RESONATE TARGET:** `example-hello-world-{ts,py,rs,go}`.67- **RELATED SKILL:** `resonate-basic-durable-world-usage-{typescript,python,rust,go}`.68- **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅.6970## Pattern: Composing functions (child workflows)7172- **DETECT:** `executeChild` (ts), `workflow.ExecuteChildWorkflow` (go),73 `workflow.execute_child_workflow` (py).74- **TRANSFORM:** Replace the separate child Workflow type with a call to a75 registered function by name (`ctx.rpc("name", args)` / `ctx.run(fn, args)`). A76 function can recurse on itself. Optionally pin child ids with `.options(id=…)`.77- **TEMPORAL SOURCE:** `samples-typescript/child-workflows`,78 `samples-go/child-workflow` (no `samples-python` child-workflow example).79- **RESONATE TARGET:** `example-recursive-factorial-{ts,py,rs,go}`.80- **RELATED SKILL:** `resonate-recursive-fan-out-pattern-{typescript,python,rust,go}`.81- **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅.8283## Pattern: Fan-out / fan-in (parallel + join)8485- **DETECT:** `Promise.all` over `executeChild`/activities (ts), `asyncio.gather`86 (py), multiple `workflow.ExecuteActivity` futures then `.Get()` (go).87- **TRANSFORM:** Start each unit non-blocking — `ctx.beginRun` (ts) / `ctx.rfi`88 (py) / `ctx.run(...).spawn()` (rs) / `ctx.RPC` (go); each returns a future89 immediately. Then await each future. Start ALL before awaiting ANY, or the90 work serializes.91- **TEMPORAL SOURCE:** `samples-typescript/child-workflows` (`Promise.all`),92 `samples-python/hello/hello_parallel_activity.py`, `samples-go/splitmerge-future`.93- **RESONATE TARGET:** `example-fan-out-fan-in-{ts,py,rs,go}`.94- **RELATED SKILL:** `resonate-recursive-fan-out-pattern-{typescript,python,rust,go}`.95- **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅.9697## Pattern: Durable timers9899- **DETECT:** `workflow.sleep(timedelta)` (py), `sleep('30 days')` (ts),100 `workflow.NewTimer` (go).101- **TRANSFORM:** Replace with `ctx.sleep(duration)`. **Mind the units:**102 - TypeScript: **milliseconds** (`ctx.sleep(ms)`).103 - Python: **seconds** as float (`ctx.sleep(secs)`).104 - Go: `time.Duration` (`ctx.Sleep(d)` then `f.Await(nil)`).105 - Rust: `std::time::Duration` (`ctx.sleep(Duration::from_secs(n)).await?`).106- **TEMPORAL SOURCE:** `samples-{python,typescript,go}/sleep-for-days`.107- **RESONATE TARGET:** `example-durable-sleep-{ts,py,rs,go}`.108- **RELATED SKILL:** `resonate-durable-sleep-scheduled-work-{typescript,rust,go}`.109- **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅.110111## Pattern: Signals → durable promises (human-in-the-loop)112113- **DETECT:** `defineSignal`/`setHandler`/`condition`/`defineQuery` (ts),114 `@workflow.signal`/`workflow.wait_condition` (py),115 `workflow.GetSignalChannel`/`workflow.Await`/`Selector` (go).116- **TRANSFORM:** Replace the signal definition + handler + flag + condition with a117 single latent durable promise (`p = ctx.promise()`; await `p`). Surface `p.id`118 to whoever will resolve it (email/webhook/log). Replace `handle.signal(...)`119 with `resonate.promises.resolve(id, value)`. Delete Query handlers.120- **RESOLVE API — verify against pinned version:**121 - ts (0.11.4): `resonate.promises.resolve(id, { data: Buffer.from(JSON.stringify(v)).toString("base64") })`122 - py (0.7.4): `await resonate.promises.resolve(id, value)` — positional `id` and a123 `Value`, not `resolve(id=…, ikey=…)`; there is no `ikey` kwarg on `Promises.resolve`124 in this release.125 - rs (0.6.0): `resonate.promises.resolve(&id, Value::from_serializable(v)?)`126 (the example repo may use `json!(v)` — verify it compiles against your127 released crate version; use the `Value` form if not)128 - go (0.1.0): `r.Promises().Resolve(ctx, id, v)` — the direct `Promises()` sub-client129 handles the codec encoding for you. The CLI (`resonate promises resolve <id> --value '{"data":"…"}'`)130 and the low-level `r.Sender().PromiseSettle(...)` (manual base64-encoded codec value)131 remain available for cross-process or non-Go settlement.132- **TEMPORAL SOURCE:** `samples-typescript/signals-queries`,133 `samples-python/hello/hello_signal.py`, `samples-go/await-signals`.134- **RESONATE TARGET:** `example-human-in-the-loop-{ts,py,rs,go}`.135- **RELATED SKILL:** `resonate-human-in-the-loop-pattern-{typescript,python,rust,go}`.136- **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅ (`0.1.0` has a direct `Promises().Resolve`; the CLI and low-level `Sender().PromiseSettle` remain as alternates).137138## Pattern: Saga / compensation139140- **DETECT:** a compensation list drained in `catch` (ts), stacked `defer`141 compensations (go), handler-coordinated compensation (py).142- **TRANSFORM:** Run each step with `ctx.run`. On failure, run the undo inline in143 the `catch`/error branch, guarded by which steps actually completed. No144 compensation stack, no drain helper. Make compensations idempotent.145- **TEMPORAL SOURCE:** `samples-typescript/saga`, `samples-go/saga`,146 `samples-python/message_passing/waiting_for_handlers_and_compensation`.147- **RESONATE TARGET:** `example-saga-booking-ts`, `example-money-transfer-{py,rs}`.148- **RELATED SKILL:** `resonate-saga-pattern-{typescript,python,rust,go}`.149- **COVERAGE:** ts ✅ py ✅ rs ✅ go ⚠️ (no Go example yet — map by analogy).150151## Pattern: Long-running loops152153- **DETECT:** `continueAsNew` (ts), `workflow.NewContinueAsNewError` (go),154 `workflow.continue_as_new` (py).155- **TRANSFORM:**156 - Bounded loop → plain `while`/`for` with `ctx.run` + `ctx.sleep`. No157 `continueAsNew` equivalent needed.158 - **Truly unbounded loop → `ctx.detached(self, n+1)` tail-recursion**, split159 *inside* the per-iteration function. A naive infinite loop in a single durable160 invocation accumulates child promises that get re-walked on replay; once161 replay time exceeds the task lease, the worker loop stalls. Do not emit a naive162 unbounded loop (`while(true)` / `while True:` / `loop {}`) for genuinely163 infinite loops.164- **TEMPORAL SOURCE:** `samples-typescript/continue-as-new`,165 `samples-go/child-workflow-continue-as-new`,166 `samples-python/hello/hello_continue_as_new.py`.167- **RESONATE TARGET:** `example-infinite-workflow-{ts,go}`.168- **COVERAGE:** ts ✅ go ✅ py ⚠️ rs ⚠️ (no py/rs example yet — map by analogy).169170## Pattern: Distributed mutex (TypeScript only)171172- **DETECT:** a lock-manager workflow with a signal queue, `uuid4()` release173 tokens, and `continueAsNew`.174- **TRANSFORM:** Delete the lock machinery. Sequential `yield* ctx.run()` calls in175 a generator are serialized by the runtime — the generator is the lock. No176 signals, no tokens, no deadlock surface.177- **TEMPORAL SOURCE:** `samples-typescript/mutex`.178- **RESONATE TARGET:** `example-distributed-mutex-ts`.179- **COVERAGE:** ts ✅ (TypeScript only).180181## Pattern: Encryption (TypeScript only)182183- **DETECT:** a `PayloadCodec` (`encode`/`decode` over `Payload[]`) + custom184 `DataConverter` + codec server.185- **TRANSFORM:** Replace with a single `Encryptor` (`encrypt(Value): Value` /186 `decrypt(Value): Value`) passed as a constructor option:187 `new Resonate({ encryptor })`. Workflow code is unchanged; the SDK handles188 promise-store serialization.189- **TEMPORAL SOURCE:** `samples-typescript/encryption`.190- **RESONATE TARGET:** `example-encryption-ts`.191- **COVERAGE:** ts ✅ (TypeScript only).192193---194195## Coverage gaps (do not fabricate)196197- Saga: no Go example (`example-saga-booking-go` / `example-money-transfer-go`).198- Long-running loops: no Python or Rust example (`example-infinite-workflow-py` / `-rs`).199- Distributed mutex, Encryption: TypeScript only.200- Rust source: Temporal Rust examples are in `temporalio/sdk-rust/crates/sdk/examples/`201 (hello_world, child_workflows, timer_examples, message_passing, saga,202 continue_as_new, …); only mutex and encryption have no Temporal Rust example.203204## Source of truth205206- Resonate examples: https://github.com/resonatehq-examples207- Temporal samples: https://github.com/temporalio (`samples-typescript`, `samples-python`, `samples-go`)208- Side-by-side guide: https://docs.resonatehq.io/evaluate/coming-from/temporal209- Concepts first: see the `durable-execution` and `resonate-philosophy` skills.