@temporal-architect/claude-plugin 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/package.json +37 -0
  4. package/skills/MANIFEST.json +373 -0
  5. package/skills/MANIFEST.md +121 -0
  6. package/skills/temporal-architect/SKILL.md +99 -0
  7. package/skills/temporal-architect/reference/decomposition.md +78 -0
  8. package/skills/temporal-architect-author-go/README.md +16 -0
  9. package/skills/temporal-architect-author-go/SKILL.md +191 -0
  10. package/skills/temporal-architect-author-go/SUBAGENT_ADOPTION.md +161 -0
  11. package/skills/temporal-architect-author-go/reference/activity-call.md +73 -0
  12. package/skills/temporal-architect-author-go/reference/activity-def.md +54 -0
  13. package/skills/temporal-architect-author-go/reference/assignment.md +36 -0
  14. package/skills/temporal-architect-author-go/reference/await-all.md +104 -0
  15. package/skills/temporal-architect-author-go/reference/await-one.md +193 -0
  16. package/skills/temporal-architect-author-go/reference/await-timer.md +35 -0
  17. package/skills/temporal-architect-author-go/reference/close.md +71 -0
  18. package/skills/temporal-architect-author-go/reference/composite-patterns.md +176 -0
  19. package/skills/temporal-architect-author-go/reference/condition.md +56 -0
  20. package/skills/temporal-architect-author-go/reference/control-flow.md +151 -0
  21. package/skills/temporal-architect-author-go/reference/dependency-resolution.md +29 -0
  22. package/skills/temporal-architect-author-go/reference/detach.md +52 -0
  23. package/skills/temporal-architect-author-go/reference/heartbeat.md +84 -0
  24. package/skills/temporal-architect-author-go/reference/nexus-service-def.md +73 -0
  25. package/skills/temporal-architect-author-go/reference/nexus.md +35 -0
  26. package/skills/temporal-architect-author-go/reference/options.md +138 -0
  27. package/skills/temporal-architect-author-go/reference/promise.md +73 -0
  28. package/skills/temporal-architect-author-go/reference/proto-driven.md +197 -0
  29. package/skills/temporal-architect-author-go/reference/query-handler.md +34 -0
  30. package/skills/temporal-architect-author-go/reference/signal-handler.md +73 -0
  31. package/skills/temporal-architect-author-go/reference/three-layer-testing.md +173 -0
  32. package/skills/temporal-architect-author-go/reference/types.md +72 -0
  33. package/skills/temporal-architect-author-go/reference/update-handler.md +64 -0
  34. package/skills/temporal-architect-author-go/reference/worker.md +215 -0
  35. package/skills/temporal-architect-author-go/reference/workflow-call.md +37 -0
  36. package/skills/temporal-architect-author-go/reference/workflow-def.md +45 -0
  37. package/skills/temporal-architect-author-infra/README.md +16 -0
  38. package/skills/temporal-architect-author-infra/SKILL.md +132 -0
  39. package/skills/temporal-architect-author-infra/reference/tcld.md +112 -0
  40. package/skills/temporal-architect-author-infra/reference/terraform.md +125 -0
  41. package/skills/temporal-architect-design/README.md +16 -0
  42. package/skills/temporal-architect-design/SKILL.md +224 -0
  43. package/skills/temporal-architect-design/reference/LANGUAGE.md +5 -0
  44. package/skills/temporal-architect-design/reference/anti-patterns.md +332 -0
  45. package/skills/temporal-architect-design/reference/common-errors.md +88 -0
  46. package/skills/temporal-architect-design/reference/core-principles.md +52 -0
  47. package/skills/temporal-architect-design/reference/design-checklist.md +59 -0
  48. package/skills/temporal-architect-design/reference/namespaces.md +84 -0
  49. package/skills/temporal-architect-design/reference/notation-examples.md +304 -0
  50. package/skills/temporal-architect-design/reference/notation-reference.md +70 -0
  51. package/skills/temporal-architect-design/reference/primitives-reference.md +65 -0
  52. package/skills/temporal-architect-design/reference/project-discovery-subagent.md +80 -0
  53. package/skills/temporal-architect-design/reference/reverse-engineering.md +53 -0
  54. package/skills/temporal-architect-design/reference/twf-conventions.md +43 -0
  55. package/skills/temporal-architect-design/reference/workflow-boundaries.md +43 -0
  56. package/skills/temporal-architect-design/topics/activities-advanced.md +358 -0
  57. package/skills/temporal-architect-design/topics/activities-advanced.twf +107 -0
  58. package/skills/temporal-architect-design/topics/child-workflows.md +347 -0
  59. package/skills/temporal-architect-design/topics/child-workflows.twf +171 -0
  60. package/skills/temporal-architect-design/topics/long-running.md +230 -0
  61. package/skills/temporal-architect-design/topics/long-running.twf +100 -0
  62. package/skills/temporal-architect-design/topics/nexus.md +248 -0
  63. package/skills/temporal-architect-design/topics/nexus.twf +148 -0
  64. package/skills/temporal-architect-design/topics/patterns.md +469 -0
  65. package/skills/temporal-architect-design/topics/patterns.twf +346 -0
  66. package/skills/temporal-architect-design/topics/promises-conditions.md +179 -0
  67. package/skills/temporal-architect-design/topics/promises-conditions.twf +213 -0
  68. package/skills/temporal-architect-design/topics/signals-queries-updates.md +319 -0
  69. package/skills/temporal-architect-design/topics/signals-queries-updates.twf +234 -0
  70. package/skills/temporal-architect-design/topics/task-queues.md +205 -0
  71. package/skills/temporal-architect-design/topics/task-queues.twf +184 -0
  72. package/skills/temporal-architect-design/topics/testing.md +437 -0
  73. package/skills/temporal-architect-design/topics/testing.twf +177 -0
  74. package/skills/temporal-architect-design/topics/timers-scheduling.md +131 -0
  75. package/skills/temporal-architect-design/topics/timers-scheduling.twf +129 -0
  76. package/skills/temporal-architect-design/topics/versioning.md +434 -0
  77. package/skills/temporal-architect-design/topics/versioning.twf +174 -0
@@ -0,0 +1,213 @@
1
+ # Source: promises-conditions.md
2
+ # Patterns: promise declarations, conditions, state blocks, set/unset
3
+
4
+ # --- Start-now, wait-later (promises with activities) ---
5
+
6
+ workflow ParallelProcessing(items: Items) -> (Result):
7
+ # Start two activities without blocking
8
+ promise handleA <- activity ProcessA(items.a)
9
+ promise handleB <- activity ProcessB(items.b)
10
+
11
+ # Do other work while they run
12
+ activity QuickSetup(items)
13
+
14
+ # Collect results
15
+ await handleA -> resultA
16
+ await handleB -> resultB
17
+
18
+ close complete(Result{a: resultA, b: resultB})
19
+
20
+ # --- Promise with child workflow ---
21
+
22
+ workflow WorkflowWithAsyncChild(input: Input) -> (Result):
23
+ # Start child workflow without blocking
24
+ promise childHandle <- workflow SlowChild(input.data)
25
+
26
+ # Do other work
27
+ activity QuickTask(input)
28
+
29
+ # Wait for child result
30
+ await childHandle -> childResult
31
+
32
+ close complete(Result{childResult})
33
+
34
+ # --- Promise with timer ---
35
+
36
+ workflow TimedOperation(data: Data) -> (Result):
37
+ promise timeout <- timer(5m)
38
+
39
+ promise work <- activity LongProcess(data)
40
+
41
+ # Race: process finishes or timeout fires
42
+ await one:
43
+ work -> result:
44
+ close complete(Result{data: result})
45
+ timeout:
46
+ close fail(Result{error: "timed out"})
47
+
48
+ # --- Update handler waits on condition ---
49
+
50
+ workflow ClusterManager(config: Config):
51
+ state:
52
+ condition clusterStarted
53
+
54
+ signal Shutdown():
55
+ shutdownRequested = true
56
+
57
+ update WaitUntilStarted() -> (ClusterState):
58
+ await clusterStarted
59
+ return ClusterState{started: true}
60
+
61
+ query GetStatus() -> (string):
62
+ return status
63
+
64
+ status = "provisioning"
65
+ activity ProvisionCluster(config)
66
+ activity StartCluster(config)
67
+ set clusterStarted
68
+ status = "running"
69
+
70
+ await signal Shutdown
71
+ close complete
72
+
73
+ # --- Condition racing against timer ---
74
+
75
+ workflow DepositCollector(accountId: string) -> (CollectionResult):
76
+ state:
77
+ condition thresholdReached
78
+
79
+ signal Deposit(amount: decimal):
80
+ balance = balance + amount
81
+ if (balance >= 1000):
82
+ set thresholdReached
83
+
84
+ balance = 0
85
+
86
+ await one:
87
+ thresholdReached:
88
+ close complete(CollectionResult{status: "threshold_reached", balance: balance})
89
+ timer(30d):
90
+ close complete(CollectionResult{status: "timeout", balance: balance})
91
+
92
+ # --- Promise racing in await one ---
93
+
94
+ workflow ResilientProcess(data: Data) -> (Result):
95
+ signal Cancel():
96
+ cancelRequested = true
97
+
98
+ promise handle <- activity LongProcess(data)
99
+
100
+ activity PrepareOutput(data)
101
+
102
+ await one:
103
+ handle -> result:
104
+ close complete(Result{data: result})
105
+ signal Cancel:
106
+ close fail(Result{error: "cancelled"})
107
+
108
+ # --- Promise with nexus workflow ---
109
+
110
+ workflow CrossNamespaceAsync(payment: Payment) -> (Result):
111
+ # Start nexus call without blocking
112
+ promise payHandle <- nexus BillingEndpoint BillingService.ChargePayment(payment)
113
+
114
+ # Do local work
115
+ activity PrepareReceipt(payment)
116
+
117
+ # Wait for nexus result
118
+ await payHandle -> paymentResult
119
+
120
+ close complete(Result{paymentId: paymentResult.id})
121
+
122
+ # --- Supporting activity definitions ---
123
+
124
+ activity ProcessA(data: Data) -> (ResultA):
125
+ return processA(data)
126
+
127
+ activity ProcessB(data: Data) -> (ResultB):
128
+ return processB(data)
129
+
130
+ activity QuickSetup(items: Items):
131
+ setup(items)
132
+
133
+ activity QuickTask(input: Input):
134
+ quick(input)
135
+
136
+ activity LongProcess(data: Data) -> (ProcessResult):
137
+ return longProcess(data)
138
+
139
+ activity PrepareOutput(data: Data):
140
+ prepare(data)
141
+
142
+ activity ProvisionCluster(config: Config):
143
+ provision(config)
144
+
145
+ activity StartCluster(config: Config):
146
+ start(config)
147
+
148
+ activity PrepareReceipt(payment: Payment):
149
+ prepare(payment)
150
+
151
+ # --- Supporting workflow definitions ---
152
+
153
+ workflow SlowChild(data: Data) -> (ChildResult):
154
+ activity DoSlowWork(data) -> result
155
+ close complete(ChildResult{result})
156
+
157
+ activity DoSlowWork(data: Data) -> (WorkResult):
158
+ return slowWork(data)
159
+
160
+ workflow BillingChargeWorkflow(payment: Payment) -> (PaymentResult):
161
+ activity ChargeCard(payment) -> result
162
+ close complete(PaymentResult{id: result.id})
163
+
164
+ activity ChargeCard(payment: Payment) -> (ChargeResult):
165
+ return charge(payment)
166
+
167
+ # --- Nexus service definition ---
168
+
169
+ nexus service BillingService:
170
+ async ChargePayment workflow BillingChargeWorkflow
171
+
172
+ # --- Worker and namespace definitions ---
173
+
174
+ worker promisesWorker:
175
+ workflow ParallelProcessing
176
+ workflow WorkflowWithAsyncChild
177
+ workflow TimedOperation
178
+ workflow ClusterManager
179
+ workflow DepositCollector
180
+ workflow ResilientProcess
181
+ workflow CrossNamespaceAsync
182
+ workflow SlowChild
183
+ activity ProcessA
184
+ activity ProcessB
185
+ activity QuickSetup
186
+ activity QuickTask
187
+ activity LongProcess
188
+ activity PrepareOutput
189
+ activity ProvisionCluster
190
+ activity StartCluster
191
+ activity PrepareReceipt
192
+ activity DoSlowWork
193
+
194
+ worker billingWorker:
195
+ workflow BillingChargeWorkflow
196
+ activity ChargeCard
197
+ nexus service BillingService
198
+
199
+ # Two namespaces: billing owns the service and exposes the endpoint;
200
+ # promises is the caller — CrossNamespaceAsync reaches across the boundary.
201
+
202
+ namespace billing:
203
+ worker billingWorker
204
+ options:
205
+ task_queue: "billing"
206
+ nexus endpoint BillingEndpoint
207
+ options:
208
+ task_queue: "billing"
209
+
210
+ namespace promises:
211
+ worker promisesWorker
212
+ options:
213
+ task_queue: "promises"
@@ -0,0 +1,319 @@
1
+ # Signals, Queries, and Updates
2
+
3
+ > **Example:** [`signals-queries-updates.twf`](./signals-queries-updates.twf)
4
+
5
+ External communication with running workflows. These three primitives let code outside the workflow interact with it during execution — as a **read**, a **write**, or a **read-write**.
6
+
7
+ ## Overview
8
+
9
+ | Primitive | I/O | Direction | Execution | Use Case |
10
+ |-----------|-----|-----------|-----------|----------|
11
+ | **Query** | Read | External → Workflow → External | Sync (request-response) | Read current state |
12
+ | **Signal** | Write | External → Workflow | Async (fire-and-forget) | Events, notifications, data injection |
13
+ | **Update** | Read-write | External → Workflow → External | Sync (request-response) | Mutate state with confirmation |
14
+
15
+ ---
16
+
17
+ ## Signals
18
+
19
+ Asynchronous messages sent to a running workflow. The sender doesn't wait for processing.
20
+
21
+ ### When to Use
22
+
23
+ - External events that workflow should react to
24
+ - Injecting data into a running workflow
25
+ - Triggering state transitions
26
+ - Human approval/rejection flows
27
+
28
+ ### Signal Handler Bodies
29
+
30
+ Signals are declared with handler body blocks that execute when the signal arrives. Handler bodies have access to the full workflow statement set (activities, child workflows, timers, etc.).
31
+
32
+ ```twf
33
+ signal PaymentReceived(transactionId: string, amount: decimal):
34
+ paymentStatus = "received"
35
+ lastTransactionId = transactionId
36
+ ```
37
+
38
+ The handler body executes when the signal arrives, whether via `await signal` or as a case in `await one`/`await all`. Handler bodies should primarily update workflow state. Heavy side effects (activity calls, child workflows) belong in the main workflow body after the signal is awaited, since handlers fire even when not being actively awaited and can execute between any two deterministic steps.
39
+
40
+ ### Handler Execution Semantics
41
+
42
+ When a signal is awaited (via `await signal` or as an `await one` case), the execution order is:
43
+
44
+ 1. **Signal arrives** → handler body runs first (updates state)
45
+ 2. **Await resolves** → case body runs (reacts to updated state, calls activities, etc.)
46
+
47
+ This two-phase execution means:
48
+
49
+ ```twf
50
+ workflow OrderTrackingWorkflow(orderId: string):
51
+ signal PaymentReceived(transactionId: string, amount: decimal):
52
+ # Phase 1: Handler runs immediately on signal arrival
53
+ paymentStatus = "received"
54
+ lastTransactionId = transactionId
55
+
56
+ # Phase 2: Case body runs after handler
57
+ await one:
58
+ signal PaymentReceived:
59
+ # paymentStatus is already "received" here
60
+ activity FulfillOrder(orderId, lastTransactionId)
61
+ close complete(OrderResult{status: "completed"})
62
+ timer(24h):
63
+ close fail(OrderResult{status: "timeout"})
64
+ ```
65
+
66
+ **Key implications:**
67
+ - Handler bodies run on every signal arrival, even if the workflow isn't actively awaiting that signal
68
+ - Keep handler bodies lightweight (state updates only, no activities)
69
+ - Place activity calls and side effects in the `await one` case body, not the handler body
70
+
71
+ ### Signal Considerations
72
+
73
+ | Consideration | Guidance |
74
+ |---------------|----------|
75
+ | **Ordering** | Signals are processed in order received, but arrival order isn't guaranteed |
76
+ | **Buffering** | Signals queue if workflow is busy; consider signal coalescing for high-volume |
77
+ | **Idempotency** | Signal handlers should be idempotent (same signal twice = same result) |
78
+ | **Validation** | Validate signal payload; invalid signals can corrupt workflow state |
79
+ | **Ambient arrival** | Signals can arrive between any two deterministic steps and are buffered until handled by `await signal` or `await one`/`await all` |
80
+
81
+ ---
82
+
83
+ ## Sending Signals to a Child Workflow
84
+
85
+ Everything above is the *receive* side — declaring handlers and awaiting arrivals. A workflow can also *send* a signal to a child it started and still holds a handle to. The handle is a workflow-bound promise (`promise handle <- workflow X(args)`); the dot-qualified name selects a signal the target workflow declares.
86
+
87
+ ```twf
88
+ workflow OrderSaga(order: Order) -> (SagaResult):
89
+ promise pay <- workflow ProcessPayment(order)
90
+ promise ship <- workflow ShipOrder(order)
91
+
92
+ # Notify the payment workflow that the order has shipped
93
+ signal pay.OrderShipped(shipmentId)
94
+
95
+ # The handle is still awaitable later — sending does not consume it
96
+ await all:
97
+ await pay -> payment
98
+ await ship -> shipment
99
+ close complete(SagaResult{payment, shipment})
100
+ ```
101
+
102
+ The send is **statement-only and fire-and-forget**. There is no `await` or `promise` form and it is not an `await one` case: a signal carries no return value, so there is nothing to bind. The only thing a sender could wait on is *send acceptance* (the server accepting the send), never the receiver's handler running — modeling that would invite the misreading "the target processed my signal."
103
+
104
+ ### Rules
105
+
106
+ - The handle must be **workflow-bound** (`promise h <- workflow X(args)`). Sending on a handle bound to a timer, signal, activity, etc. is an error.
107
+ - The target workflow must **declare** the named signal (`signal OrderShipped(...)` on `X`), or it is an error.
108
+ - A workflow-bound promise serves **two roles** on the same handle — an awaitable (`await pay -> payment`) and a signal target (`signal pay.OrderShipped(...)`). Sending a signal does not consume or affect a later `await` on it.
109
+
110
+ ### When to Use
111
+
112
+ - Coordinating sibling/child workflows in a saga — telling one child about an event another produced.
113
+ - Pushing an event into a child you started, without waiting for it to react.
114
+
115
+ This is the only cross-workflow send the DSL provides: handle-bound only. Addressing a workflow you did not start (external/ID-based sends), and cross-workflow queries or updates, are not modeled.
116
+
117
+ ---
118
+
119
+ ## Queries
120
+
121
+ Synchronous, read-only access to workflow state. The caller blocks until the query returns.
122
+
123
+ ### When to Use
124
+
125
+ - UI needs to display current workflow state
126
+ - Monitoring/debugging workflow progress
127
+ - External system needs workflow data
128
+ - Building workflow dashboards
129
+
130
+ ### Query Handler Bodies
131
+
132
+ Queries are declared with handler body blocks that execute when queried. Query handlers are restricted to activity-style statements (no temporal primitives like timers, signals, or child workflows).
133
+
134
+ ```twf
135
+ query GetStatus() -> (string):
136
+ return status
137
+
138
+ query GetProgress() -> (Progress):
139
+ return Progress{status: status, processed: itemCount}
140
+ ```
141
+
142
+ ### Query Considerations
143
+
144
+ | Consideration | Guidance |
145
+ |---------------|----------|
146
+ | **Read-only** | Queries MUST NOT modify workflow state |
147
+ | **Determinism** | Query handlers run during replay; must be deterministic |
148
+ | **Performance** | Queries replay workflow history; expensive for long histories |
149
+ | **Consistency** | Returns point-in-time state; may be stale by the time caller uses it |
150
+ | **Restrictions** | Query handlers use activity-restricted statement set (no timers, signals, workflows) |
151
+
152
+ ### Anti-Patterns
153
+
154
+ ```twf
155
+ # BAD: Query modifies state
156
+ query GetAndIncrementCounter() -> (int):
157
+ counter = counter + 1 # NOT ALLOWED
158
+ return counter
159
+
160
+ # GOOD: Pure read
161
+ query GetStatus() -> (string):
162
+ return status
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Updates
168
+
169
+ Synchronous read-write operations. The caller sends data, the workflow processes it, and the caller blocks until it receives a result (or error) back.
170
+
171
+ ### When to Use
172
+
173
+ - Need confirmation that change was applied
174
+ - Validating input before accepting
175
+ - Returning computed result from mutation
176
+ - Request-response pattern with workflow
177
+
178
+ ### Update Handler Bodies
179
+
180
+ Updates are declared with handler body blocks that execute when the update is received. Handler bodies have access to the full workflow statement set (activities, child workflows, timers, etc.) and **must return a value** to the caller.
181
+
182
+ Simple state mutation with immediate return:
183
+
184
+ ```twf
185
+ update ChangePlan(newPlan: string) -> (ChangeResult):
186
+ plan = newPlan
187
+ return ChangeResult{success: true, plan: plan}
188
+ ```
189
+
190
+ Validation via activity before accepting mutation:
191
+
192
+ ```twf
193
+ update ChangePlan(newPlan: string) -> (ChangeResult):
194
+ activity ValidatePlan(newPlan) -> validation
195
+ if (validation.valid):
196
+ plan = newPlan
197
+ return ChangeResult{success: true, plan: plan}
198
+ else:
199
+ return ChangeResult{success: false, error: validation.reason}
200
+ ```
201
+
202
+ The caller blocks until the handler returns — including any time spent waiting on activities, child workflows, or timers within the handler.
203
+
204
+ ### Handler Execution Semantics
205
+
206
+ Signal and update handlers run as coroutines alongside the main workflow body, but **only one piece of workflow code runs at a time** (cooperative scheduling). When a workflow wakes up, it processes pending messages (signals/updates) in order, then makes progress in the main workflow body.
207
+
208
+ This means:
209
+ 1. The update handler runs as part of the workflow execution loop
210
+ 2. If the handler blocks (on an activity, timer, etc.), the main workflow body can make progress while it waits
211
+ 3. The handler reads from and writes to the same shared workflow state as the main body and signal handlers
212
+ 4. The caller only receives a response after the handler has completed and returned
213
+
214
+ **Update handlers cannot call `close`** — they can mutate state and return values, but only the main workflow body can terminate the workflow.
215
+
216
+ ### Awaiting Updates
217
+
218
+ Updates can be awaited in the workflow body, similar to signals. This is useful when the main workflow body needs to wait for an external mutation before continuing:
219
+
220
+ ```twf
221
+ await update ChangeAddress
222
+ ```
223
+
224
+ Updates can also race against other operations in `await one`:
225
+
226
+ ```twf
227
+ await one:
228
+ update ChangeAddress -> (newAddress):
229
+ activity NotifyShipping(orderId, newAddress)
230
+ timer(1h):
231
+ activity FinalizeShipping(orderId)
232
+ ```
233
+
234
+ When the update wins the race, its handler body runs and returns a value to the caller, then the case body executes.
235
+
236
+ ### Update Handlers with Conditions
237
+
238
+ A common pattern has an update handler wait on workflow state using `condition`. The caller blocks until the condition becomes true, then receives a result reflecting the current state:
239
+
240
+ ```twf
241
+ workflow JobCoordinator(config: JobConfig):
242
+ state:
243
+ condition jobReady
244
+
245
+ signal Shutdown():
246
+ shutdownRequested = true
247
+
248
+ update WaitUntilReady() -> (JobState):
249
+ await jobReady
250
+ return JobState{ready: true}
251
+
252
+ # Main body provisions and starts the job runner
253
+ activity ProvisionJobRunner(config)
254
+ activity StartJobRunner(config)
255
+ set jobReady
256
+
257
+ await signal Shutdown
258
+ close complete
259
+ ```
260
+
261
+ In this pattern:
262
+ 1. The client calls the update and blocks waiting for a result
263
+ 2. The update handler starts running but yields on `await jobReady`
264
+ 3. The main workflow body mutates the condition via `set jobReady`
265
+ 4. The update handler resumes and returns a value
266
+ 5. The client receives the result
267
+
268
+ See [promises-conditions.md](./promises-conditions.md) for more on conditions and the `state:` block.
269
+
270
+ ### Updates vs Signals
271
+
272
+ | Aspect | Signal (write) | Update (read-write) |
273
+ |--------|--------|--------|
274
+ | **Response** | None (fire-and-forget) | Returns result to caller |
275
+ | **Validation** | In handler, but caller doesn't know | Caller receives validation errors |
276
+ | **Confirmation** | No guarantee processing happened | Caller knows when complete |
277
+ | **Handler can block** | Yes, but caller doesn't wait | Yes, and caller blocks until done |
278
+ | **Use when** | "Notify workflow of X" | "Change X and tell me if it worked" |
279
+
280
+ ### Update Considerations
281
+
282
+ | Consideration | Guidance |
283
+ |---------------|----------|
284
+ | **Atomicity** | Update handlers should be atomic; don't leave partial state |
285
+ | **Validation** | Validate before mutating; return errors, don't throw |
286
+ | **Idempotency** | Consider idempotency keys for critical updates |
287
+ | **Timeouts** | Caller should set appropriate timeout; handler may block on activities or state |
288
+ | **Shared state** | Handler reads/writes the same state as the main workflow body and signal handlers |
289
+ | **Ambient arrival** | Like signals, updates can arrive between any two deterministic steps and are buffered until handled by `await update` or `await one`/`await all` |
290
+
291
+ ---
292
+
293
+ ## Choosing Between Primitives
294
+
295
+ **Use QUERY (read) when:**
296
+ - Need to read current state
297
+ - Building UI/dashboard
298
+ - Debugging/monitoring
299
+
300
+ **Use SIGNAL (write) when:**
301
+ - Fire-and-forget is acceptable
302
+ - External event notification
303
+ - No response needed
304
+
305
+ **Use UPDATE (read-write) when:**
306
+ - Need confirmation that a change was applied
307
+ - Validating input before accepting
308
+ - Returning a computed result from a mutation
309
+
310
+ ---
311
+
312
+ ## Signal/Query/Update Naming Conventions
313
+
314
+ | Type | Convention | Examples |
315
+ |------|------------|----------|
316
+ | Signals | Event-style, past tense or imperative | `PaymentReceived`, `Cancel`, `AddItem` |
317
+ | Queries | Getter-style, "Get" prefix | `GetStatus`, `GetProgress`, `GetItems` |
318
+ | Updates | Action-style, verb phrase | `ChangePlan`, `AddCredits`, `UpdateAddress` |
319
+