Skip to content

Commit 10e70b0

Browse files
committed
Continued cleanup
1 parent 139d7b8 commit 10e70b0

11 files changed

Lines changed: 105 additions & 70 deletions

File tree

.cargo/config.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ lint = [
3232
"--test",
3333
"manual_tests",
3434
"--test",
35+
"wasm_workflow_tests",
36+
"--test",
3537
"cloud_tests",
3638
"--test",
3739
"integ_runner",
@@ -53,6 +55,8 @@ lint-fix = [
5355
"--test",
5456
"manual_tests",
5557
"--test",
58+
"wasm_workflow_tests",
59+
"--test",
5660
"cloud_tests",
5761
"--test",
5862
"integ_runner",

.github/workflows/per-pr.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,29 @@ jobs:
188188
save-if: ${{ github.ref == 'refs/heads/main' }}
189189
- run: cargo integ-test
190190

191+
wasm-workflow-tests:
192+
name: WASM workflow tests
193+
timeout-minutes: ${{ github.ref == 'refs/heads/main' && 30 || 25 }}
194+
runs-on: ubuntu-latest
195+
steps:
196+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
197+
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
198+
with:
199+
targets: wasm32-unknown-unknown
200+
- name: Install protoc
201+
uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b # v3
202+
with:
203+
# TODO: Upgrade proto once https://github.com/arduino/setup-protoc/issues/99 is fixed
204+
version: "23.x"
205+
repo-token: ${{ secrets.GITHUB_TOKEN }}
206+
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
207+
with:
208+
save-if: ${{ github.ref == 'refs/heads/main' }}
209+
key: wasm-workflow-tests
210+
- name: Install cargo-component
211+
run: cargo install --locked cargo-component
212+
- run: cargo integ-test -t wasm_workflow_tests
213+
191214
cloud-tests:
192215
if: github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-rust'
193216
name: Cloud tests

crates/sdk-core/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,19 @@ path = "tests/manual_tests.rs"
198198
test = false
199199
required-features = ["test-utilities"]
200200

201+
[[test]]
202+
name = "wasm_workflow_tests"
203+
path = "tests/wasm_workflow_tests.rs"
204+
# Prevents autodiscovery, and hence these getting run with `cargo test`. Run with
205+
# `cargo integ-test -t wasm_workflow_tests`. Building these requires the `wasm32-unknown-unknown`
206+
# target and `cargo component`, which aren't installed by default.
207+
test = false
208+
required-features = [
209+
"temporalio-common/serde_serialize",
210+
"test-utilities",
211+
"ephemeral-server",
212+
]
213+
201214
[[test]]
202215
name = "global_metric_tests"
203216
path = "tests/global_metric_tests.rs"

crates/sdk-core/tests/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ mod integ_tests {
2525
mod schedule_tests;
2626
mod update_tests;
2727
mod visibility_tests;
28-
mod wasm_workflow_tests;
2928
mod worker_heartbeat_tests;
3029
mod worker_tests;
3130
mod worker_versioning_tests;

crates/sdk-core/tests/integ_tests/wasm_workflow_tests.rs renamed to crates/sdk-core/tests/wasm_workflow_tests.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
//! Tests that exercise the WASM workflow execution path. These are kept in a separate test binary
2+
//! because they require `cargo component` and the `wasm32-unknown-unknown` target to build the
3+
//! sample components, which not every CI environment has installed.
4+
5+
#[allow(dead_code)]
6+
mod common;
7+
18
use crate::common::CoreWfStarter;
29
use std::{path::PathBuf, time::Duration};
310
use temporalio_client::{UntypedWorkflow, WorkflowStartOptions};

crates/sdk/src/workflows.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
pub use temporalio_workflow::workflows::*;
44

55
pub use crate::workflow_registry::WorkflowDefinitions;
6+
#[doc(inline)]
7+
pub use temporalio_macros::{workflow, workflow_methods};

crates/workflow/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ wit-bindgen = { version = "0.57.1", default-features = false, features = ["macro
3232
path = "../common-wasm"
3333
version = "0.2"
3434

35+
[dependencies.temporalio-macros]
36+
path = "../macros"
37+
version = "0.3"
38+
3539
[lints]
3640
workspace = true
37-
38-
[dev-dependencies]
39-
temporalio-macros = { path = "../macros" }

crates/workflow/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
extern crate self as temporalio_workflow;
66

77
pub use temporalio_common_wasm as common;
8+
pub use temporalio_macros::{
9+
init, query, run, signal, update, update_validator, workflow, workflow_methods,
10+
};
811

912
#[doc(hidden)]
1013
pub mod __private {

crates/workflow/wit/README.md

Lines changed: 45 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,58 @@
11
# Workflow Runtime WIT
22

3-
This directory defines the canonical high-level workflow guest interface for the future WASM SDK.
3+
This directory defines `temporal:workflow-runtime@0.1.0`, the high-level guest interface that
4+
workflow code targets when it is compiled to a WebAssembly component. It is intentionally separate
5+
from the core activation/completion protocol that worker SDKs speak to the Temporal cluster: core
6+
remains the worker-facing protocol, and a worker is responsible for translating core activations
7+
and completions into calls against this interface.
8+
9+
## What's in here
10+
11+
- `world.wit` — the `workflow-module` world: imports `workflow-host`, exports `workflow-guest`.
12+
- `guest.wit``workflow-guest`: the entry points the guest exports for the host to drive
13+
(`list-workflows`, `instantiate-workflow`, and the `workflow-instance` resource with `activate`
14+
and `poll-routine`).
15+
- `host.wit``workflow-host`: the host capabilities the guest imports (e.g. `set-current-details`,
16+
`push-command`).
17+
- `types.wit` — shared records and variants used by both sides (init/activation/completion shapes,
18+
routine kinds, terminal outcomes, etc.). Some fields are typed as `list<u8>` and carry encoded
19+
protobuf messages — those proto schemas are part of the ABI; see *Stability* below.
20+
21+
## How a workflow runs against it
22+
23+
1. The host instantiates a workflow run by calling `instantiate-workflow` with `workflow-init`.
24+
2. For each activation, the host calls `activate` on the `workflow-instance` resource. The guest
25+
processes the activation's jobs and reports per-job results (started routines, query responses,
26+
update rejections).
27+
3. The guest drives its routines (main, signals, updates) by being polled — the host calls
28+
`poll-routine(routine-id)` until the routine either completes or reports it made no progress.
29+
4. While running, the guest invokes host functions to push commands and update execution state.
30+
31+
The guest interface is deliberately higher-level than core's activation protocol: ordering rules
32+
and worker bookkeeping live in the host translation layer, not in the guest contract.
433

5-
This is intentionally **not** the existing core activation/completion protocol. Core remains the
6-
worker-facing protocol for the foreseeable future, and other language SDKs will continue to use it.
7-
The Rust worker will translate core activations and completions into this higher-level interface.
8-
9-
## Why this lives in `temporalio-workflow`
10-
11-
`temporalio-workflow` is the crate that workflow implementations compile against. Checking the WIT
12-
in here gives the Rust runtime refactor a concrete target:
13-
14-
- `temporalio_workflow::runtime::*` should evolve toward a direct Rust mirror of these interfaces.
15-
- The native Rust worker should keep calling those Rust traits directly, with no WIT serialization
16-
in the hot path.
17-
- A future WASM backend should expose the same interface through the component model.
18-
19-
## Layering
20-
21-
The layers are:
22-
23-
1. Core activation/completion protocol
24-
2. Native worker translation layer
25-
3. This WIT-shaped workflow runtime interface
26-
4. Workflow guest code
27-
28-
That translation layer is where activation ordering and other core-specific details stay hidden.
29-
The guest interface here is deliberately higher level:
30-
31-
- the host instantiates a workflow run
32-
- the host applies activation-wide context
33-
- the host notifies signals, cancellation, patches, updates, and operation resolutions
34-
- the guest polls until it blocks or terminates
34+
## Synchronous ABI and the WASIp3 future
3535

36-
## Native and WASM execution
36+
The guest interface is synchronous: `activate` and `poll-routine` return ordinary results, and a
37+
routine signals "blocked" by returning `routine-poll-result.made-progress = false`. The host
38+
re-enters `poll-routine` after the relevant activation lands. This shape is what stable Wasmtime
39+
and `wit-bindgen` support today.
3740

38-
The runtime should support two backends behind the same worker translation logic:
41+
When component-model async (WASIp3 / Preview 3) is stable in Wasmtime, the natural evolution is:
3942

40-
- a native backend that invokes Rust traits directly in-process
41-
- a WASM backend that invokes a component implementing `workflow-module`
43+
- `activate` and `poll-routine` become `async func`.
44+
- `routine-poll-result.made-progress` and the explicit `routine-id`-keyed polling protocol can
45+
go away — the host can `await` each routine directly.
4246

43-
The goal is one logical execution model with two transport backends, not two independent workers.
47+
That is a breaking ABI change and will require a major-version WIT bump.
4448

4549
## Stability
4650

47-
The package is published as `temporal:workflow-runtime@0.1.0` and is **not yet stable**. Until
48-
this is bumped to `1.0.0` any release may bump the minor version with breaking changes. Once
49-
external SDKs (Python, TypeScript, Go, etc.) begin compiling guest workflows against this WIT,
50-
breaking changes need to follow normal SemVer discipline: bump the package version, dual-export
51-
the old and new world from the host for a deprecation window, and document the migration path.
51+
The package is published as `temporal:workflow-runtime@0.1.0` and is **not yet stable**. Until it
52+
is bumped to `1.0.0`, any release may bump the minor version with breaking changes. Once external
53+
SDKs (Python, TypeScript, Go, etc.) begin compiling guest workflows against this WIT, breaking
54+
changes need to follow normal SemVer discipline: bump the package version, dual-export the old
55+
and new world from the host for a deprecation window, and document the migration path.
5256

5357
Changes that force a major bump include:
5458

@@ -57,21 +61,3 @@ Changes that force a major bump include:
5761
- Reordering variant cases (the discriminant order is part of the ABI).
5862
- Changing the proto messages encoded into `list<u8>`-typed fields (`failure`, `payload`,
5963
`workflow-command`, `workflow-activation`, `continue-as-new-request`).
60-
61-
## Synchronous ABI and the WASIp3 future
62-
63-
The current guest interface is intentionally synchronous: `activate` and `poll-routine` both
64-
return ordinary results, and the guest is expected to suspend by returning
65-
`routine-poll-result.made-progress = false`. The host then re-enters `poll-routine` after the
66-
relevant activation lands.
67-
68-
This shape is what stable Wasmtime + `wit-bindgen` support today. When component-model async
69-
(WASIp3 / Preview 3) lands as stable in Wasmtime, the natural evolution is:
70-
71-
- `activate` and `poll-routine` become `async func`.
72-
- `routine-poll-result.made-progress` and the explicit `routine-id`-keyed polling protocol
73-
likely go away — the host can just `await` each routine directly.
74-
75-
That is a breaking ABI change, so it will require a major-version WIT bump. Any other-language
76-
SDK that ships a guest implementation should expect to regenerate bindings against the new
77-
world at that point.

samples/wasm-workflows/hello/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ version = "0.1.0"
44
edition = "2024"
55

66
[dependencies]
7-
temporalio-macros = { path = "../../../crates/macros" }
87
temporalio-workflow = { path = "../../../crates/workflow" }
98

109
[lib]

0 commit comments

Comments
 (0)