resonate-migrate-from-temporal — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited resonate-migrate-from-temporal (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
temporalio/samples-* file. If you can't find it, say so.
made durable by ctx.run. Don't invent decorators.
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.
reference exists yet.
drifts between versions.
| SDK | Version | Source |
|---|---|---|
| TypeScript | @resonatehq/sdk v0.10.2 | npm |
| Python | resonate-sdk v0.6.7 | PyPI |
| Rust | resonate-sdk v0.4.0 | crates.io |
| Go | pre-release, tracks main | GitHub |
| 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=…) | yield ctx.run(fn, args) — 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 |
@workflow.defn/@activity.defn (py), proxyActivities (ts),RegisterWorkflow+RegisterActivity / workflow.ExecuteActivity (go).
called via ctx.run. Drop Task Queue wiring and start_to_close_timeout.
samples-{python,typescript,go}/hello-world(py: hello/hello_activity.py).
example-hello-world-{ts,py,rs,go}.resonate-basic-durable-world-usage-{typescript,python,rust,go}.executeChild (ts), workflow.ExecuteChildWorkflow (go),workflow.execute_child_workflow (py).
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=…).
samples-typescript/child-workflows,samples-go/child-workflow (no samples-python child-workflow example).
example-recursive-factorial-{ts,py,rs,go}.resonate-recursive-fan-out-pattern-{typescript,python,rust,go}.Promise.all over executeChild/activities (ts), asyncio.gather(py), multiple workflow.ExecuteActivity futures then .Get() (go).
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.
samples-typescript/child-workflows (Promise.all),samples-python/hello/hello_parallel_activity.py, samples-go/splitmerge-future.
example-fan-out-fan-in-{ts,py,rs,go}.resonate-recursive-fan-out-pattern-{typescript,python,rust,go}.workflow.sleep(timedelta) (py), sleep('30 days') (ts),workflow.NewTimer (go).
ctx.sleep(duration). Mind the units:ctx.sleep(ms)).ctx.sleep(secs)).time.Duration (ctx.Sleep(d) then f.Await(nil)).std::time::Duration (ctx.sleep(Duration::from_secs(n)).await?).samples-{python,typescript,go}/sleep-for-days.example-durable-sleep-{ts,py,rs,go}.resonate-durable-sleep-scheduled-work-{typescript,rust,go}.defineSignal/setHandler/condition/defineQuery (ts),@workflow.signal/workflow.wait_condition (py), workflow.GetSignalChannel/workflow.Await/Selector (go).
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.
resonate.promises.resolve(id, { data: Buffer.from(JSON.stringify(v)).toString("base64") })resonate.promises.resolve(id=…, ikey=…)resonate.promises.resolve(&id, Value::from_serializable(v)?)(the example repo tracks SDK main and uses json!(v), which does NOT compile against the released v0.4.0 crate — use the Value form)
resonate promises resolve <id> --value '{"data":"…"}' or r.Sender().PromiseSettle(...) with a base64-encoded codec value (resonate-sdk-go#28).
samples-typescript/signals-queries,samples-python/hello/hello_signal.py, samples-go/await-signals.
example-human-in-the-loop-{ts,py,rs,go}.resonate-human-in-the-loop-pattern-{typescript,python,rust,go}.catch (ts), stacked defercompensations (go), handler-coordinated compensation (py).
ctx.run. On failure, run the undo inline inthe catch/error branch, guarded by which steps actually completed. No compensation stack, no drain helper. Make compensations idempotent.
samples-typescript/saga, samples-go/saga,samples-python/message_passing/waiting_for_handlers_and_compensation.
example-saga-booking-ts, example-money-transfer-{py,rs}.resonate-saga-pattern-{typescript,python,rust,go}.continueAsNew (ts), workflow.NewContinueAsNewError (go),workflow.continue_as_new (py).
while/for with ctx.run + ctx.sleep. NocontinueAsNew equivalent needed.
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.
samples-typescript/continue-as-new,samples-go/child-workflow-continue-as-new, samples-python/hello/hello_continue_as_new.py.
example-infinite-workflow-{ts,go}.uuid4() releasetokens, and continueAsNew.
yield* ctx.run() calls ina generator are serialized by the runtime — the generator is the lock. No signals, no tokens, no deadlock surface.
samples-typescript/mutex.example-distributed-mutex-ts.PayloadCodec (encode/decode over Payload[]) + customDataConverter + codec server.
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.
samples-typescript/encryption.example-encryption-ts.example-saga-booking-go / example-money-transfer-go).example-infinite-workflow-py / -rs).temporalio/sdk-rust/crates/sdk/examples/(hello_world, child_workflows, timer_examples, message_passing, saga, continue_as_new, …); only mutex and encryption have no Temporal Rust example.
samples-typescript, samples-python, samples-go)durable-execution and resonate-philosophy skills.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.