@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,437 @@
1
+ # Testing Temporal Workflows
2
+
3
+ > **Example:** [`testing.twf`](./testing.twf)
4
+
5
+ Strategies for testing workflows, activities, and ensuring determinism.
6
+
7
+ ## Testing Pyramid for Temporal
8
+
9
+ ```text
10
+ ┌─────────────────┐
11
+ │ End-to-End │ Few, slow, high confidence
12
+ │ Tests │
13
+ ├─────────────────┤
14
+ │ Integration │ Test with real services
15
+ │ Tests │
16
+ ├─────────────────┤
17
+ │ Workflow │ Mock activities
18
+ │ Unit Tests │
19
+ ├─────────────────┤
20
+ │ Activity │ Mock clients/SDKs
21
+ │ Unit Tests │
22
+ └─────────────────┘ Many, fast, isolated
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Activity Unit Testing
28
+
29
+ Test activities in isolation by mocking external dependencies.
30
+
31
+ ### Pattern
32
+
33
+ > Note: Activity body implementations and test assertions are SDK-level code, not TWF notation.
34
+
35
+ ```pseudo
36
+ # Activity implementation
37
+ activity ProcessPayment(payment: Payment) -> PaymentResult:
38
+ # Validate
39
+ if payment.amount <= 0:
40
+ raise InvalidPaymentError("Amount must be positive")
41
+
42
+ # Call external service
43
+ result = paymentClient.charge(payment)
44
+
45
+ return PaymentResult{
46
+ transactionId: result.id,
47
+ status: result.status
48
+ }
49
+
50
+ # Test
51
+ test "ProcessPayment succeeds with valid payment":
52
+ mockPaymentClient = Mock()
53
+ mockPaymentClient.charge.returns({id: "txn-123", status: "success"})
54
+
55
+ result = ProcessPayment(Payment{amount: 100, cardId: "card-1"})
56
+
57
+ assert result.transactionId == "txn-123"
58
+ assert result.status == "success"
59
+ assert mockPaymentClient.charge.called_with(Payment{amount: 100})
60
+
61
+ test "ProcessPayment fails with invalid amount":
62
+ expect_error InvalidPaymentError:
63
+ ProcessPayment(Payment{amount: -50})
64
+ ```
65
+
66
+ ### What to Test in Activities
67
+ - Input validation
68
+ - Error handling for external failures
69
+ - Correct client/SDK usage
70
+ - Return value construction
71
+
72
+ ---
73
+
74
+ ## Workflow Unit Testing
75
+
76
+ Test workflow logic by mocking activities. Use Temporal's test framework.
77
+
78
+ ### Pattern
79
+
80
+ ```twf
81
+ workflow RenewalProcessing(renewal: Renewal) -> (RenewalResult):
82
+ activity ValidateRenewalRequest(renewal) -> validated
83
+ if not validated.success:
84
+ close fail(RenewalResult{status: "invalid"})
85
+
86
+ activity ChargeRenewalPayment(renewal.payment) -> payment
87
+ activity ActivateRenewedPlan(renewal)
88
+
89
+ close complete(RenewalResult{status: "completed", paymentId: payment.id})
90
+ ```
91
+
92
+ ```pseudo
93
+ # Test
94
+ test "RenewalProcessing completes successfully":
95
+ env = TestWorkflowEnvironment()
96
+
97
+ # Mock activities
98
+ env.mock_activity(ValidateRenewalRequest, returns: {success: true})
99
+ env.mock_activity(ChargeRenewalPayment, returns: {id: "pay-123"})
100
+ env.mock_activity(ActivateRenewedPlan, returns: {})
101
+
102
+ # Execute workflow
103
+ result = env.execute_workflow(RenewalProcessing, Renewal{id: "renew-1"})
104
+
105
+ # Assert result
106
+ assert result.status == "completed"
107
+ assert result.paymentId == "pay-123"
108
+
109
+ # Assert activity calls
110
+ assert env.activity_called(ValidateRenewalRequest, with: Renewal{id: "renew-1"})
111
+ assert env.activity_called(ChargeRenewalPayment)
112
+ assert env.activity_called(ActivateRenewedPlan)
113
+
114
+ test "RenewalProcessing returns invalid for failed validation":
115
+ env = TestWorkflowEnvironment()
116
+
117
+ env.mock_activity(ValidateRenewalRequest, returns: {success: false, error: "bad renewal"})
118
+
119
+ result = env.execute_workflow(RenewalProcessing, Renewal{id: "renew-1"})
120
+
121
+ assert result.status == "invalid"
122
+ assert not env.activity_called(ChargeRenewalPayment) # Not called
123
+ assert not env.activity_called(ActivateRenewedPlan) # Not called
124
+ ```
125
+
126
+ ### What to Test in Workflows
127
+ - Activity call ordering
128
+ - Conditional logic (correct branch taken)
129
+ - Error handling (activity failures)
130
+ - Signal/query handlers
131
+ - Timeout behavior
132
+
133
+ ---
134
+
135
+ ## Replay Testing (Determinism Verification)
136
+
137
+ Verify workflows are deterministic by replaying against recorded history.
138
+
139
+ ### Why Replay Testing
140
+
141
+ ```text
142
+ Version 1: Workflow runs, generates history
143
+ Version 2: Workflow code changes
144
+ Replay Test: Run version 2 against version 1 history
145
+ Result: PASS (deterministic) or FAIL (non-determinism detected)
146
+ ```
147
+
148
+ ### Pattern
149
+
150
+ ```pseudo
151
+ # Record workflow history
152
+ test "record RenewalProcessing history":
153
+ env = TestWorkflowEnvironment()
154
+ # ... execute workflow ...
155
+ history = env.get_workflow_history()
156
+ save_to_file("renewal_processing_v1.history", history)
157
+
158
+ # Replay test
159
+ test "RenewalProcessing replays deterministically":
160
+ history = load_from_file("renewal_processing_v1.history")
161
+
162
+ env = TestWorkflowEnvironment()
163
+ result = env.replay_workflow(RenewalProcessing, history)
164
+
165
+ assert result.replay_successful
166
+ assert not result.non_determinism_errors
167
+ ```
168
+
169
+ ### Maintaining History Files
170
+ - Store history files in version control
171
+ - Update when workflow signature changes
172
+ - Keep multiple versions for migration testing
173
+
174
+ ---
175
+
176
+ ## Testing Signals and Queries
177
+
178
+ ### Signal Testing
179
+
180
+ ```twf
181
+ workflow LeaveRequestApproval(request: LeaveRequest) -> (Decision):
182
+ await one:
183
+ signal Approved:
184
+ close complete(Decision{status: "approved"})
185
+ signal Rejected:
186
+ close complete(Decision{status: "rejected"})
187
+ timer(1h):
188
+ close complete(Decision{status: "timeout"})
189
+ ```
190
+
191
+ ```pseudo
192
+ # Test
193
+ test "LeaveRequestApproval handles Approved signal":
194
+ env = TestWorkflowEnvironment()
195
+
196
+ # Start workflow
197
+ handle = env.start_workflow(LeaveRequestApproval, LeaveRequest{id: "req-1"})
198
+
199
+ # Send signal
200
+ env.signal_workflow(handle, Approved, {approver: "alice"})
201
+
202
+ # Get result
203
+ result = env.get_workflow_result(handle)
204
+ assert result.status == "approved"
205
+
206
+ test "LeaveRequestApproval handles timeout":
207
+ env = TestWorkflowEnvironment()
208
+
209
+ handle = env.start_workflow(LeaveRequestApproval, LeaveRequest{id: "req-1"})
210
+
211
+ # Skip time forward
212
+ env.skip_time(2h)
213
+
214
+ result = env.get_workflow_result(handle)
215
+ assert result.status == "timeout" # or however timeout is handled
216
+ ```
217
+
218
+ ### Query Testing
219
+
220
+ ```twf
221
+ workflow StatusWorkflow(orderId: string) -> (OrderResult):
222
+ query GetStatus() -> (string):
223
+ return status
224
+
225
+ status = "pending"
226
+ activity StartProcessing(orderId)
227
+
228
+ status = "processing"
229
+ activity ProcessItems(orderId)
230
+
231
+ status = "completed"
232
+ close complete(OrderResult{status: status, orderId: orderId})
233
+ ```
234
+
235
+ ```pseudo
236
+ # Test
237
+ test "GetStatus query returns current status":
238
+ env = TestWorkflowEnvironment()
239
+
240
+ # Mock activity to block
241
+ blocker = env.mock_activity(ProcessItems, blocks: true)
242
+
243
+ handle = env.start_workflow(StatusWorkflow, "order-1")
244
+
245
+ # Query while processing
246
+ status = env.query_workflow(handle, GetStatus)
247
+ assert status == "processing"
248
+
249
+ # Unblock activity
250
+ blocker.unblock({})
251
+
252
+ # Query after completion
253
+ env.wait_for_workflow(handle)
254
+ status = env.query_workflow(handle, GetStatus)
255
+ assert status == "completed"
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Testing Timers
261
+
262
+ Use time-skipping to test timer behavior without waiting.
263
+
264
+ ```twf
265
+ workflow ReminderWorkflow(userId: string):
266
+ activity SendFirstReminder(userId)
267
+
268
+ await timer(24h)
269
+
270
+ activity SendSecondReminder(userId)
271
+
272
+ await timer(48h)
273
+
274
+ activity SendFinalReminder(userId)
275
+ ```
276
+
277
+ ```pseudo
278
+ # Test
279
+ test "ReminderWorkflow sends reminders at correct intervals":
280
+ env = TestWorkflowEnvironment()
281
+
282
+ env.mock_activity(SendFirstReminder, returns: {})
283
+ env.mock_activity(SendSecondReminder, returns: {})
284
+ env.mock_activity(SendFinalReminder, returns: {})
285
+
286
+ handle = env.start_workflow(ReminderWorkflow, "user-1")
287
+
288
+ # First reminder sent immediately
289
+ assert env.activity_called(SendFirstReminder)
290
+ assert not env.activity_called(SendSecondReminder)
291
+
292
+ # Skip 24 hours
293
+ env.skip_time(24h)
294
+
295
+ assert env.activity_called(SendSecondReminder)
296
+ assert not env.activity_called(SendFinalReminder)
297
+
298
+ # Skip 48 more hours
299
+ env.skip_time(48h)
300
+
301
+ assert env.activity_called(SendFinalReminder)
302
+ ```
303
+
304
+ ---
305
+
306
+ ## Testing Child Workflows
307
+
308
+ ```twf
309
+ workflow ParentWorkflow(data: Data) -> (Result):
310
+ workflow ChildWorkflow(data.item) -> childResult
311
+ close complete(Result{childData: childResult})
312
+
313
+ workflow ChildWorkflow(item: Item) -> (ChildResult):
314
+ activity ProcessItem(item)
315
+ close complete(ChildResult{processed: true})
316
+ ```
317
+
318
+ ```pseudo
319
+ # Test parent in isolation
320
+ test "ParentWorkflow calls child correctly":
321
+ env = TestWorkflowEnvironment()
322
+
323
+ env.mock_child_workflow(ChildWorkflow, returns: {processed: true})
324
+
325
+ result = env.execute_workflow(ParentWorkflow, Data{item: Item{id: "1"}})
326
+
327
+ assert result.childData.processed == true
328
+ assert env.child_workflow_called(ChildWorkflow, with: Item{id: "1"})
329
+
330
+ # Test parent and child together
331
+ test "ParentWorkflow integration with ChildWorkflow":
332
+ env = TestWorkflowEnvironment()
333
+
334
+ # Mock only activities, let child workflow run
335
+ env.mock_activity(ProcessItem, returns: {})
336
+
337
+ result = env.execute_workflow(ParentWorkflow, Data{item: Item{id: "1"}})
338
+
339
+ assert result.childData.processed == true
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Integration Testing
345
+
346
+ Test with real Temporal server (local or test cluster).
347
+
348
+ ### Setup
349
+
350
+ ```bash
351
+ # Start local Temporal for testing
352
+ temporal server start-dev
353
+
354
+ # Or use testcontainers
355
+ test_environment = TemporalTestContainer()
356
+ test_environment.start()
357
+ ```
358
+
359
+ ### Pattern
360
+
361
+ ```pseudo
362
+ test "RenewalProcessing end-to-end":
363
+ # Use real Temporal client
364
+ client = TemporalClient(address: "localhost:7233")
365
+
366
+ # Start real worker
367
+ worker = Worker(
368
+ client: client,
369
+ task_queue: "test-queue",
370
+ workflows: [RenewalProcessing],
371
+ activities: [ValidateRenewalRequest, ChargeRenewalPayment, ActivateRenewedPlan]
372
+ )
373
+ worker.start_async()
374
+
375
+ # Execute workflow
376
+ handle = client.start_workflow(
377
+ RenewalProcessing,
378
+ Renewal{id: "test-renew-1"},
379
+ workflow_id: "test-renew-1"
380
+ )
381
+
382
+ # Wait for result
383
+ result = handle.result(timeout: 30s)
384
+
385
+ assert result.status == "completed"
386
+
387
+ # Cleanup
388
+ worker.stop()
389
+ ```
390
+
391
+ ---
392
+
393
+ ## Testing Best Practices
394
+
395
+ ### Do's
396
+
397
+ | Practice | Rationale |
398
+ |----------|-----------|
399
+ | Mock activities in workflow tests | Isolate workflow logic |
400
+ | Use replay tests | Catch non-determinism early |
401
+ | Test failure paths | Verify error handling |
402
+ | Use time-skipping | Fast timer tests |
403
+ | Test signal ordering | Validate async behavior |
404
+
405
+ ### Don'ts
406
+
407
+ | Anti-Pattern | Problem |
408
+ |--------------|---------|
409
+ | Testing deterministic logic via integration tests | Slow, flaky |
410
+ | Skipping replay tests | Non-determinism in production |
411
+ | Mocking workflow internals | Brittle tests |
412
+ | Real time waits | Slow tests |
413
+ | Testing Temporal internals | Not your responsibility |
414
+
415
+ ---
416
+
417
+ ## Test Coverage Checklist
418
+
419
+ ### Activity
420
+ - [ ] Valid inputs produce correct output
421
+ - [ ] Invalid inputs produce appropriate errors
422
+ - [ ] External service failures handled
423
+ - [ ] Retryable vs non-retryable errors classified
424
+
425
+ ### Workflow
426
+ - [ ] Happy path completes successfully
427
+ - [ ] Each conditional branch tested
428
+ - [ ] Activity failures handled correctly
429
+ - [ ] Signals processed correctly
430
+ - [ ] Queries return correct state
431
+ - [ ] Timeouts handled appropriately
432
+ - [ ] Replay test passes
433
+
434
+ ### Integration
435
+ - [ ] End-to-end happy path
436
+ - [ ] Cross-service communication
437
+ - [ ] Failure recovery
@@ -0,0 +1,177 @@
1
+ # Source: testing.md
2
+ # Patterns: simple testable workflow/activity pairs
3
+ # Domain: subscription renewals + leave-of-absence approvals + reminder cadences
4
+ # + status-tracked status flow + condition-driven subscription manager.
5
+ # Each workflow is intentionally small and isolated so unit tests can drive
6
+ # signals/updates and assert on activity calls in isolation.
7
+
8
+ # --- Basic linear workflow: renewal processing ---
9
+
10
+ workflow RenewalProcessing(renewal: Renewal) -> (RenewalResult):
11
+ activity ValidateRenewalRequest(renewal) -> validated
12
+ if (validated.success == false):
13
+ close fail(RenewalResult{status: "invalid"})
14
+
15
+ activity ChargeRenewalPayment(renewal.payment) -> payment
16
+ activity ActivateRenewedPlan(renewal)
17
+
18
+ close complete(RenewalResult{status: "completed", paymentId: payment.id})
19
+
20
+ # --- Workflow with timer: scheduled reminder cadence ---
21
+
22
+ workflow ReminderWorkflow(userId: string):
23
+ activity SendFirstReminder(userId)
24
+
25
+ await timer(24h)
26
+
27
+ activity SendSecondReminder(userId)
28
+
29
+ await timer(48h)
30
+
31
+ activity SendFinalReminder(userId)
32
+ close complete
33
+
34
+ # --- Workflow with signals: leave-of-absence approval ---
35
+
36
+ workflow LeaveRequestApproval(request: LeaveRequest) -> (Decision):
37
+ signal Approved(approver: string):
38
+ approved = true
39
+ approverName = approver
40
+
41
+ signal Rejected(approver: string, reason: string):
42
+ rejected = true
43
+ rejectReason = reason
44
+
45
+ approved = false
46
+ rejected = false
47
+ approverName = ""
48
+ rejectReason = ""
49
+
50
+ activity NotifyManagers(request)
51
+
52
+ await one:
53
+ signal Approved:
54
+ close complete(Decision{status: "approved", approver: approverName})
55
+ signal Rejected:
56
+ close fail(Decision{status: "rejected", reason: rejectReason})
57
+ timer(1h):
58
+ close complete(Decision{status: "timeout"})
59
+
60
+ # --- Workflow with query: status flow ---
61
+
62
+ workflow StatusWorkflow(orderId: string) -> (OrderResult):
63
+ query GetStatus() -> (string):
64
+ return status
65
+
66
+ status = "pending"
67
+ activity StartProcessing(orderId)
68
+
69
+ status = "processing"
70
+ activity ProcessItems(orderId)
71
+
72
+ status = "completed"
73
+ close complete(OrderResult{status: status, orderId: orderId})
74
+
75
+ # --- Complex workflow for testing continue-as-new, conditions, and updates ---
76
+
77
+ workflow SubscriptionManager(userId: string, sub: Subscription):
78
+ state:
79
+ condition renewed
80
+
81
+ update RenewSubscription(payment: Payment) -> (RenewResult):
82
+ activity ValidatePayment(payment) -> validation
83
+ if (validation.ok):
84
+ set renewed
85
+ return RenewResult{accepted: true}
86
+ else:
87
+ return RenewResult{accepted: false, reason: validation.error}
88
+
89
+ query GetSubscription() -> (Subscription):
90
+ return sub
91
+
92
+ for:
93
+ # Wait for renewal or expiration
94
+ await one:
95
+ renewed:
96
+ activity ExtendSubscription(userId, sub) -> sub
97
+ unset renewed
98
+ timer(30d):
99
+ activity SendExpirationWarning(userId)
100
+ await one:
101
+ renewed:
102
+ activity ExtendSubscription(userId, sub) -> sub
103
+ unset renewed
104
+ timer(7d):
105
+ activity CancelSubscription(userId)
106
+ close complete
107
+
108
+ # Periodic state snapshot for continue-as-new
109
+ close continue_as_new(userId, sub)
110
+
111
+ # --- Supporting activities ---
112
+
113
+ activity ValidateRenewalRequest(renewal: Renewal) -> (ValidationResult):
114
+ return validate(renewal)
115
+
116
+ activity ChargeRenewalPayment(payment: Payment) -> (PaymentResult):
117
+ return charge(payment)
118
+
119
+ activity ActivateRenewedPlan(renewal: Renewal):
120
+ activate(renewal)
121
+
122
+ activity SendFirstReminder(userId: string):
123
+ remind(userId, "first")
124
+
125
+ activity SendSecondReminder(userId: string):
126
+ remind(userId, "second")
127
+
128
+ activity SendFinalReminder(userId: string):
129
+ remind(userId, "final")
130
+
131
+ activity NotifyManagers(request: LeaveRequest):
132
+ notify(request.managers)
133
+
134
+ activity StartProcessing(orderId: string):
135
+ start(orderId)
136
+
137
+ activity ProcessItems(orderId: string):
138
+ process(orderId)
139
+
140
+ activity ValidatePayment(payment: Payment) -> (PaymentValidation):
141
+ return validate(payment)
142
+
143
+ activity ExtendSubscription(userId: string, sub: Subscription) -> (Subscription):
144
+ return extend(userId, sub)
145
+
146
+ activity SendExpirationWarning(userId: string):
147
+ warn(userId)
148
+
149
+ activity CancelSubscription(userId: string):
150
+ cancel(userId)
151
+
152
+ # --- Worker and namespace ---
153
+
154
+ worker testingWorker:
155
+ workflow RenewalProcessing
156
+ workflow ReminderWorkflow
157
+ workflow LeaveRequestApproval
158
+ workflow StatusWorkflow
159
+ workflow SubscriptionManager
160
+ activity ValidateRenewalRequest
161
+ activity ChargeRenewalPayment
162
+ activity ActivateRenewedPlan
163
+ activity SendFirstReminder
164
+ activity SendSecondReminder
165
+ activity SendFinalReminder
166
+ activity NotifyManagers
167
+ activity StartProcessing
168
+ activity ProcessItems
169
+ activity ValidatePayment
170
+ activity ExtendSubscription
171
+ activity SendExpirationWarning
172
+ activity CancelSubscription
173
+
174
+ namespace testing:
175
+ worker testingWorker
176
+ options:
177
+ task_queue: "testing"