Skip to main content

resonate-migrate-from-temporal

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.

跳到安装

来源信息

仓库
resonatehq/resonate-skills
最近来源活动
2026年8月21日 13:59
检测到的 SKILL.md 语言
英语
星标
6
分支
0

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
resonate-migrate-from-temporal
description
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.
license
Apache-2.0
# Migrate from Temporal to Resonate A pattern-by-pattern playbook for porting a Temporal application to Resonate. Identify which Temporal construct each piece of the source uses, apply the matching transform, and reach for the linked per-SDK skill for idiomatic target code. ## Ground rules - **Never invent Temporal code.** Quote it from the user's source or a named `temporalio/samples-*` file. If you can't find it, say so. - **Resonate has no `@workflow`/`@activity` split.** A step is just a function made durable by `ctx.run`. Don't invent decorators. - **Temporal Rust samples live in-repo.** Temporal's Rust SDK ships examples in `temporalio/sdk-rust/crates/sdk/examples/` (no separate `samples-rust` repo) — source Rust migrations from there (hello_world, child_workflows, timer_examples, message_passing, saga, continue_as_new, …). Only mutex and encryption have no Temporal Rust example; for those, source from the Temporal *TypeScript* idiom. - **State coverage honestly.** A missing example ≠ impossible; it means no worked reference exists yet. - **Verify APIs against the pinned version before emitting** — the SDK surface drifts between versions. ## Pinned SDK versions (latest released at time of writing) | SDK | Version | Source | |---|---|---| | TypeScript | `@resonatehq/sdk` v0.11.4 | npm | | Python | `resonate-sdk` v0.7.4 | PyPI | | Rust | `resonate-sdk` v0.6.0 | crates.io | | Go | `0.1.0` (tag has no `v` prefix — `go get github.com/resonatehq/resonate-sdk-go@0.1.0`) | GitHub | ## Core mappings (apply everywhere) | Temporal | Resonate | |---|---| | `@workflow.defn` / Workflow type | a registered function (`@resonate.register`, `resonate.register("name", fn)`, `#[resonate::function]`, `resonate.Register(r, "name", fn)`) | | `@activity.defn` / Activity | a plain function invoked via `ctx.run(fn, args)` | | `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 | | Task Queue + Worker wiring | a worker `group` (only when you need distributed dispatch) | | `executeChild` / `ExecuteChildWorkflow(ChildType, …)` | `ctx.rpc("name", args)` or `ctx.run(fn, args)` — invoke by registered name; recursion is trivial | | `Promise.all` / `asyncio.gather` / parallel futures (fan-out) | start each non-blocking (`ctx.beginRun` / `ctx.rfi` / `.spawn()` / `ctx.RPC`), then await each | | `defineSignal` + `setHandler` + `condition` | one latent durable promise: `p = ctx.promise()` then await `p` | | `defineQuery` / query handler | delete — promise/result state is the source of truth | | `handle.signal(sig)` (external) | `resonate.promises.resolve(id, value)` (HTTP-addressable from anywhere) | | `workflow.sleep` / `NewTimer` | `ctx.sleep(duration)` | | saga compensation stack + drain-on-catch | inline `ctx.run(undo, …)` in the error branch, guarded by what completed | | `continueAsNew` | bounded loop: a plain loop. Unbounded loop: `ctx.detached(self, n+1)` tail-recursion | --- ## Pattern: Workflow + activity - **DETECT:** `@workflow.defn`/`@activity.defn` (py), `proxyActivities` (ts), `RegisterWorkflow`+`RegisterActivity` / `workflow.ExecuteActivity` (go). - **TRANSFORM:** Register one function. Turn each activity into a plain function called via `ctx.run`. Drop Task Queue wiring and `start_to_close_timeout`. - **TEMPORAL SOURCE:** `samples-{python,typescript,go}/hello-world` (py: `hello/hello_activity.py`). - **RESONATE TARGET:** `example-hello-world-{ts,py,rs,go}`. - **RELATED SKILL:** `resonate-basic-durable-world-usage-{typescript,python,rust,go}`. - **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅. ## Pattern: Composing functions (child workflows) - **DETECT:** `executeChild` (ts), `workflow.ExecuteChildWorkflow` (go), `workflow.execute_child_workflow` (py). - **TRANSFORM:** Replace the separate child Workflow type with a call to a registered function by name (`ctx.rpc("name", args)` / `ctx.run(fn, args)`). A function can recurse on itself. Optionally pin child ids with `.options(id=…)`. - **TEMPORAL SOURCE:** `samples-typescript/child-workflows`, `samples-go/child-workflow` (no `samples-python` child-workflow example). - **RESONATE TARGET:** `example-recursive-factorial-{ts,py,rs,go}`. - **RELATED SKILL:** `resonate-recursive-fan-out-pattern-{typescript,python,rust,go}`. - **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅. ## Pattern: Fan-out / fan-in (parallel + join) - **DETECT:** `Promise.all` over `executeChild`/activities (ts), `asyncio.gather` (py), multiple `workflow.ExecuteActivity` futures then `.Get()` (go). - **TRANSFORM:** Start each unit non-blocking — `ctx.beginRun` (ts) / `ctx.rfi` (py) / `ctx.run(...).spawn()` (rs) / `ctx.RPC` (go); each returns a future immediately. Then await each future. Start ALL before awaiting ANY, or the work serializes. - **TEMPORAL SOURCE:** `samples-typescript/child-workflows` (`Promise.all`), `samples-python/hello/hello_parallel_activity.py`, `samples-go/splitmerge-future`. - **RESONATE TARGET:** `example-fan-out-fan-in-{ts,py,rs,go}`. - **RELATED SKILL:** `resonate-recursive-fan-out-pattern-{typescript,python,rust,go}`. - **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅. ## Pattern: Durable timers - **DETECT:** `workflow.sleep(timedelta)` (py), `sleep('30 days')` (ts), `workflow.NewTimer` (go). - **TRANSFORM:** Replace with `ctx.sleep(duration)`. **Mind the units:** - TypeScript: **milliseconds** (`ctx.sleep(ms)`). - Python: **seconds** as float (`ctx.sleep(secs)`). - Go: `time.Duration` (`ctx.Sleep(d)` then `f.Await(nil)`). - Rust: `std::time::Duration` (`ctx.sleep(Duration::from_secs(n)).await?`). - **TEMPORAL SOURCE:** `samples-{python,typescript,go}/sleep-for-days`. - **RESONATE TARGET:** `example-durable-sleep-{ts,py,rs,go}`. - **RELATED SKILL:** `resonate-durable-sleep-scheduled-work-{typescript,rust,go}`. - **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅. ## Pattern: Signals → durable promises (human-in-the-loop) - **DETECT:** `defineSignal`/`setHandler`/`condition`/`defineQuery` (ts), `@workflow.signal`/`workflow.wait_condition` (py), `workflow.GetSignalChannel`/`workflow.Await`/`Selector` (go). - **TRANSFORM:** Replace the signal definition + handler + flag + condition with a single latent durable promise (`p = ctx.promise()`; await `p`). Surface `p.id` to whoever will resolve it (email/webhook/log). Replace `handle.signal(...)` with `resonate.promises.resolve(id, value)`. Delete Query handlers. - **RESOLVE API — verify against pinned version:** - ts (0.11.4): `resonate.promises.resolve(id, { data: Buffer.from(JSON.stringify(v)).toString("base64") })` - py (0.7.4): `await resonate.promises.resolve(id, value)` — positional `id` and a `Value`, not `resolve(id=…, ikey=…)`; there is no `ikey` kwarg on `Promises.resolve` in this release. - rs (0.6.0): `resonate.promises.resolve(&id, Value::from_serializable(v)?)` (the example repo may use `json!(v)` — verify it compiles against your released crate version; use the `Value` form if not) - go (0.1.0): `r.Promises().Resolve(ctx, id, v)` — the direct `Promises()` sub-client handles the codec encoding for you. The CLI (`resonate promises resolve <id> --value '{"data":"…"}'`) and the low-level `r.Sender().PromiseSettle(...)` (manual base64-encoded codec value) remain available for cross-process or non-Go settlement. - **TEMPORAL SOURCE:** `samples-typescript/signals-queries`, `samples-python/hello/hello_signal.py`, `samples-go/await-signals`. - **RESONATE TARGET:** `example-human-in-the-loop-{ts,py,rs,go}`. - **RELATED SKILL:** `resonate-human-in-the-loop-pattern-{typescript,python,rust,go}`. - **COVERAGE:** ts ✅ py ✅ rs ✅ go ✅ (`0.1.0` has a direct `Promises().Resolve`; the CLI and low-level `Sender().PromiseSettle` remain as alternates). ## Pattern: Saga / compensation - **DETECT:** a compensation list drained in `catch` (ts), stacked `defer` compensations (go), handler-coordinated compensation (py). - **TRANSFORM:** Run each step with `ctx.run`. On failure, run the undo inline in the `catch`/error branch, guarded by which steps actually completed. No compensation stack, no drain helper. Make compensations idempotent. - **TEMPORAL SOURCE:** `samples-typescript/saga`, `samples-go/saga`, `samples-python/message_passing/waiting_for_handlers_and_compensation`. - **RESONATE TARGET:** `example-saga-booking-ts`, `example-money-transfer-{py,rs}`. - **RELATED SKILL:** `resonate-saga-pattern-{typescript,python,rust,go}`. - **COVERAGE:** ts ✅ py ✅ rs ✅ go ⚠️ (no Go example yet — map by analogy). ## Pattern: Long-running loops - **DETECT:** `continueAsNew` (ts), `workflow.NewContinueAsNewError` (go), `workflow.continue_as_new` (py). - **TRANSFORM:** - Bounded loop → plain `while`/`for` with `ctx.run` + `ctx.sleep`. No `continueAsNew` equivalent needed. - **Truly unbounded loop → `ctx.detached(self, n+1)` tail-recursion**, split *inside* the per-iteration function. A naive infinite loop in a single durable invocation accumulates child promises that get re-walked on replay; once replay time exceeds the task lease, the worker loop stalls. Do not emit a naive unbounded loop (`while(true)` / `while True:` / `loop {}`) for genuinely infinite loops. - **TEMPORAL SOURCE:** `samples-typescript/continue-as-new`, `samples-go/child-workflow-continue-as-new`, `samples-python/hello/hello_continue_as_new.py`. - **RESONATE TARGET:** `example-infinite-workflow-{ts,go}`. - **COVERAGE:** ts ✅ go ✅ py ⚠️ rs ⚠️ (no py/rs example yet — map by analogy). ## Pattern: Distributed mutex (TypeScript only) - **DETECT:** a lock-manager workflow with a signal queue, `uuid4()` release tokens, and `continueAsNew`. - **TRANSFORM:** Delete the lock machinery. Sequential `yield* ctx.run()` calls in a generator are serialized by the runtime — the generator is the lock. No signals, no tokens, no deadlock surface. - **TEMPORAL SOURCE:** `samples-typescript/mutex`. - **RESONATE TARGET:** `example-distributed-mutex-ts`. - **COVERAGE:** ts ✅ (TypeScript only). ## Pattern: Encryption (TypeScript only) - **DETECT:** a `PayloadCodec` (`encode`/`decode` over `Payload[]`) + custom `DataConverter` + codec server. - **TRANSFORM:** Replace with a single `Encryptor` (`encrypt(Value): Value` / `decrypt(Value): Value`) passed as a constructor option: `new Resonate({ encryptor })`. Workflow code is unchanged; the SDK handles promise-store serialization. - **TEMPORAL SOURCE:** `samples-typescript/encryption`. - **RESONATE TARGET:** `example-encryption-ts`.
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看