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.

Zur Installation springen

Quellinformationen

Repository
resonatehq/resonate-skills
Letzte Quellaktivität
21. August 2026 um 13:59
Erkannte Sprache von SKILL.md
Englisch
Sterne
6
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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`.
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen