From 5d78df4401b6366af30ef8338f978f2954d12394 Mon Sep 17 00:00:00 2001 From: Greg Bowyer Date: Tue, 9 Jun 2026 17:11:25 -0700 Subject: [PATCH] Expose memo on WorkflowStartOptions Adds a public `memo: Option>` field to the bon::Builder-generated builder for WorkflowStartOptions, and wires it through both the StartWorkflowExecutionRequest and SignalWithStartWorkflowExecutionRequest construction paths in client::start_workflow. Memos are returned by list_workflows and describe_workflow_execution, so this lets callers stash small caller-supplied metadata on a workflow without round-tripping through a query. The values must already be serialized Payloads -- the builder does not encode for you. Includes three unit tests covering builder default, builder set, and clone round-trip. --- crates/client/src/lib.rs | 2 ++ crates/client/src/options_structs.rs | 49 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 0fb02d22d..f918e4b1b 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -1150,6 +1150,7 @@ where workflow_run_timeout: options.run_timeout.and_then(|d| d.try_into().ok()), workflow_task_timeout: options.task_timeout.and_then(|d| d.try_into().ok()), search_attributes: options.search_attributes.map(|d| d.into()), + memo: options.memo.map(|fields| Memo { fields }), cron_schedule: options.cron_schedule.unwrap_or_default(), header: options.header.or(start_signal.header), user_metadata, @@ -1186,6 +1187,7 @@ where workflow_run_timeout: options.run_timeout.and_then(|d| d.try_into().ok()), workflow_task_timeout: options.task_timeout.and_then(|d| d.try_into().ok()), search_attributes: options.search_attributes.map(|d| d.into()), + memo: options.memo.map(|fields| Memo { fields }), cron_schedule: options.cron_schedule.unwrap_or_default(), request_eager_execution: options.enable_eager_workflow_start, retry_policy: options.retry_policy, diff --git a/crates/client/src/options_structs.rs b/crates/client/src/options_structs.rs index ed9fb97e1..0313bfc1c 100644 --- a/crates/client/src/options_structs.rs +++ b/crates/client/src/options_structs.rs @@ -322,6 +322,13 @@ pub struct WorkflowStartOptions { /// Multi-line static details for the workflow, shown in the Temporal UI. pub static_details: Option, + + /// Optional memo attached to the workflow execution. Memos are returned by + /// `list_workflows` and `describe_workflow_execution` so callers can stash small + /// caller-supplied metadata on a workflow without round-tripping through a query. + /// Each value must already be a serialized `Payload`; the builder does not encode + /// values for you. + pub memo: Option>, } /// A signal to send atomically when starting a workflow. @@ -539,3 +546,45 @@ pub struct WorkflowListOptions { #[derive(Debug, Clone, Default, bon::Builder)] #[non_exhaustive] pub struct WorkflowCountOptions {} + +#[cfg(test)] +mod tests { + use super::*; + + fn payload(s: &str) -> Payload { + Payload { + metadata: std::collections::HashMap::new(), + data: s.as_bytes().to_vec(), + ..Default::default() + } + } + + #[test] + fn workflow_start_options_memo_defaults_to_none() { + let opts = WorkflowStartOptions::new("tq", "wf-id").build(); + assert!(opts.memo.is_none()); + } + + #[test] + fn workflow_start_options_memo_is_settable_via_builder() { + let mut fields = HashMap::new(); + fields.insert("interview_start_time".to_owned(), payload("2026-06-09T18:00:00Z")); + fields.insert("deadline".to_owned(), payload("2026-06-09T19:30:00Z")); + let opts = WorkflowStartOptions::new("tq", "wf-id").memo(fields.clone()).build(); + let stored = opts.memo.as_ref().expect("memo should be set"); + assert_eq!(stored.len(), 2); + assert_eq!(stored.get("interview_start_time"), fields.get("interview_start_time")); + assert_eq!(stored.get("deadline"), fields.get("deadline")); + } + + #[test] + fn workflow_start_options_round_trip_through_clone() { + // Cloning the options must preserve the memo (callers can construct one set of + // options and reuse them across signal-with-start variants). + let mut fields = HashMap::new(); + fields.insert("k".to_owned(), payload("v")); + let opts = WorkflowStartOptions::new("tq", "wf-id").memo(fields).build(); + let cloned = opts.clone(); + assert_eq!(opts.memo.as_ref().unwrap().get("k"), cloned.memo.as_ref().unwrap().get("k")); + } +}