@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,56 @@
1
+ # condition
2
+
3
+ ## DSL
4
+
5
+ ```twf
6
+ workflow JobCoordinator(config: JobConfig):
7
+ state:
8
+ condition jobReady
9
+
10
+ # ... later in body:
11
+ set jobReady
12
+
13
+ # ... elsewhere:
14
+ await jobReady
15
+ ```
16
+
17
+ ## Go
18
+
19
+ ```go
20
+ func JobCoordinator(ctx workflow.Context, config JobConfig) error {
21
+ jobReady := false
22
+
23
+ // ... later in body:
24
+ jobReady = true
25
+
26
+ // ... elsewhere:
27
+ err := workflow.Await(ctx, func() bool { return jobReady })
28
+ if err != nil {
29
+ return err
30
+ }
31
+ }
32
+ ```
33
+
34
+ ## Notes
35
+
36
+ - `condition name` in `state:` → `name := false` (a `bool` variable)
37
+ - `set name` → `name = true`
38
+ - `unset name` → `name = false`
39
+ - `await name` → `workflow.Await(ctx, func() bool { return name })`
40
+ - Conditions in `await one:` become selector cases via a goroutine that awaits the condition and sends to a channel — see the "condition promise" pattern in [await-one.md](./await-one.md)
41
+
42
+ ## Pitfalls
43
+
44
+ - **Never poll with `workflow.Sleep` in a loop.** Each `Sleep` generates 2 history events (timer-started + timer-fired). A tight polling loop can exhaust the 51,200 event limit and force the workflow into a non-recoverable state. Always use `workflow.Await` — it generates zero history events and re-evaluates on every state transition:
45
+ ```go
46
+ // WRONG: polling loop — 2 history events per iteration
47
+ for !ready {
48
+ workflow.Sleep(ctx, 5*time.Second)
49
+ }
50
+
51
+ // RIGHT: zero history events, wakes on any state change
52
+ workflow.Await(ctx, func() bool { return ready })
53
+ ```
54
+ - **Indefinite blocking.** `workflow.Await` blocks until the condition returns `true`. If the condition is never set (logic bug, missing signal handler, unreachable code path), the workflow hangs indefinitely. Consider using `workflow.AwaitWithTimeout` when a bounded wait is appropriate
55
+ - **Time-based conditions are unreliable.** `workflow.Await(ctx, func() bool { return workflow.Now(ctx).After(deadline) })` may never return — the condition is only re-evaluated on workflow state transitions (signals, activity completions, etc.), not on wall-clock time. Use `workflow.Sleep` or `workflow.NewTimer` for time-based waits
56
+ - `workflow.Await` returns `*CanceledError` if the context is cancelled
@@ -0,0 +1,151 @@
1
+ # control flow
2
+
3
+ ## if / else
4
+
5
+ ### DSL
6
+
7
+ ```twf
8
+ if (validated.priority == "high"):
9
+ activity ExpediteOrder(order)
10
+ else:
11
+ activity StandardProcessing(order)
12
+ ```
13
+
14
+ ### Go
15
+
16
+ ```go
17
+ if validated.Priority == "high" {
18
+ err := workflow.ExecuteActivity(ctx, ExpediteOrder, order).Get(ctx, nil)
19
+ if err != nil {
20
+ return Result{}, err
21
+ }
22
+ } else {
23
+ err := workflow.ExecuteActivity(ctx, StandardProcessing, order).Get(ctx, nil)
24
+ if err != nil {
25
+ return Result{}, err
26
+ }
27
+ }
28
+ ```
29
+
30
+ ## for (iteration)
31
+
32
+ ### DSL
33
+
34
+ ```twf
35
+ for (item in order.items):
36
+ activity ProcessItem(item)
37
+ ```
38
+
39
+ ### Go
40
+
41
+ ```go
42
+ for _, item := range order.Items {
43
+ err := workflow.ExecuteActivity(ctx, ProcessItem, item).Get(ctx, nil)
44
+ if err != nil {
45
+ return Result{}, err
46
+ }
47
+ }
48
+ ```
49
+
50
+ ## for (conditional)
51
+
52
+ ### DSL
53
+
54
+ ```twf
55
+ for (retries < maxRetries):
56
+ activity Attempt(data)
57
+ retries = retries + 1
58
+ ```
59
+
60
+ ### Go
61
+
62
+ ```go
63
+ for retries < maxRetries {
64
+ err := workflow.ExecuteActivity(ctx, Attempt, data).Get(ctx, nil)
65
+ if err != nil {
66
+ return Result{}, err
67
+ }
68
+ retries++
69
+ }
70
+ ```
71
+
72
+ ## for (infinite loop)
73
+
74
+ ### DSL
75
+
76
+ ```twf
77
+ for:
78
+ # body
79
+ if (done):
80
+ break
81
+ ```
82
+
83
+ ### Go
84
+
85
+ ```go
86
+ for {
87
+ // body
88
+ if done {
89
+ break
90
+ }
91
+ }
92
+ ```
93
+
94
+ ## switch
95
+
96
+ ### DSL
97
+
98
+ ```twf
99
+ switch (phase):
100
+ case "draft":
101
+ # ...
102
+ case "approved":
103
+ # ...
104
+ else:
105
+ # ...
106
+ ```
107
+
108
+ ### Go
109
+
110
+ ```go
111
+ switch phase {
112
+ case "draft":
113
+ // ...
114
+ case "approved":
115
+ // ...
116
+ default:
117
+ // ...
118
+ }
119
+ ```
120
+
121
+ ## Notes
122
+
123
+ - `break` → `break`, `continue` → `continue` — direct mapping
124
+ - DSL `else:` in switch → Go `default:`
125
+ - DSL boolean operators: `and` → `&&`, `or` → `||`, `not` → `!`
126
+ - All condition expressions (`if`, `for`, `switch`) must be deterministic — no `time.Now()`, `rand`, or non-deterministic function calls. See [workflow-def.md](./workflow-def.md) for the full constraint list
127
+
128
+ ## When to use: sequential vs parallel iteration
129
+
130
+ - **Sequential** (`.Get()` inside loop): each iteration waits for the previous to complete. Use when order matters or when items depend on prior results
131
+ ```go
132
+ for _, item := range items {
133
+ err := workflow.ExecuteActivity(ctx, Process, item).Get(ctx, nil)
134
+ // blocks here — next iteration starts after this one completes
135
+ }
136
+ ```
137
+ - **Parallel** (collect futures, `.Get()` after loop): all iterations start concurrently. Use when items are independent
138
+ ```go
139
+ var futures []workflow.Future
140
+ for _, item := range items {
141
+ futures = append(futures, workflow.ExecuteActivity(ctx, Process, item))
142
+ }
143
+ for _, f := range futures {
144
+ if err := f.Get(ctx, nil); err != nil { ... }
145
+ }
146
+ ```
147
+ - The DSL does not distinguish these — infer from context. If unsure, ask the user
148
+
149
+ ## Pitfalls
150
+
151
+ - **Manual retry loops** (the `for (retries < max)` pattern) are almost always inferior to SDK `RetryPolicy` on `ActivityOptions`. Use `RetryPolicy` for standard retry logic; reserve manual loops for non-standard control flow (e.g., retry with modified input, conditional retry based on error type)
@@ -0,0 +1,29 @@
1
+ # dependency resolution
2
+
3
+ Scan all activities in the `.twf` and identify external integration points. Most activities are thin wrappers around calls to external systems — the activity itself is simple, but the dependency behind it is not.
4
+
5
+ ## For each activity
6
+
7
+ - **Categorize:** external API call, storage operation, protocol client, or pure logic
8
+ - **Check existing code:** does the project already have a client/library for this?
9
+ - **Check `go.mod`:** is a relevant SDK already imported?
10
+ - **If unresolved:** suggest specific options with tradeoffs to the user
11
+ - **Read the chosen dependency's API:** identify the method the activity will call, then trace its signature to concrete types. See [types.md](./types.md) for the full resolution strategy
12
+
13
+ Resolve as many as possible early — it prevents expensive rework later. If a dependency choice is unclear or blocked on another decision, defer it and continue.
14
+
15
+ ## Deliverable
16
+
17
+ A dependency map, presented to the user for confirmation before generation begins.
18
+
19
+ A dependency is resolved when you can write the call expression with verified types. The map should include the method, every parameter type, and the return type — all confirmed from `go doc` or source, not inferred from names.
20
+
21
+ ```
22
+ Example dependency map:
23
+ ChargePayment → stripe-go
24
+ paymentintent.New(params *stripe.PaymentIntentParams) (*stripe.PaymentIntent, error)
25
+ SendPaymentConfirmation → sendgrid-go
26
+ client.SendWithContext(ctx, mail *sgmail.SGMailV3) (*rest.Response, error)
27
+ LoadOrderRecord → database/sql (no external dependency)
28
+ CalculateTotal → pure logic (no dependency)
29
+ ```
@@ -0,0 +1,52 @@
1
+ # detach
2
+
3
+ ## DSL
4
+
5
+ ```twf
6
+ detach workflow NotifyCustomer(order.customer)
7
+ ```
8
+
9
+ ## Go
10
+
11
+ ```go
12
+ childCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
13
+ ParentClosePolicy: enumspb.PARENT_CLOSE_POLICY_ABANDON,
14
+ })
15
+ childFuture := workflow.ExecuteChildWorkflow(childCtx, NotifyCustomer, order.Customer)
16
+ // DO NOT skip this — child may silently fail to spawn if parent completes first
17
+ if err := childFuture.GetChildWorkflowExecution().Get(ctx, nil); err != nil {
18
+ return Result{}, err
19
+ }
20
+ // Do NOT call childFuture.Get() — that waits for completion, defeating detach
21
+ ```
22
+
23
+ ## Detach with nexus
24
+
25
+ ### DSL
26
+
27
+ ```twf
28
+ detach nexus NotificationsEndpoint NotificationsService.SendConfirmation(order.customer, paymentResult)
29
+ ```
30
+
31
+ ### Go
32
+
33
+ ```go
34
+ c := workflow.NewNexusClient("NotificationsEndpoint", "NotificationsService")
35
+ c.ExecuteOperation(ctx, "SendConfirmation", sendConfirmationInput, workflow.NexusOperationOptions{})
36
+ // No .Get() — fire-and-forget. The operation starts when the schedule command is processed
37
+ ```
38
+
39
+ ## Notes
40
+
41
+ - `detach` = start the child but never wait for its result
42
+ - Set `ParentClosePolicy` to `ABANDON` so the child survives if the parent completes
43
+ - Always call `GetChildWorkflowExecution().Get()` to confirm the child started — without this, the child may never spawn if the parent completes first
44
+ - Do not call `.Get()` on the child workflow future itself (that would wait for completion)
45
+ - The child workflow runs independently and its success/failure does not affect the parent
46
+ - **Nexus fire-and-forget caveat:** omitting `.Get()` is an emergent pattern, not a first-class SDK mode. The caller workflow must not complete before the `ScheduleNexusOperation` command is processed, or the operation may not start. When the handler workflow completes, it attempts a callback to the (already-completed) caller, producing an ignorable error
47
+
48
+ ## When to use
49
+
50
+ - Use `detach` when the parent does not need the child's result and the child should outlive the parent (e.g., sending a notification after order completion)
51
+ - Use a normal child workflow call (with `.Get()`) when the parent needs the result or should fail if the child fails
52
+ - `detach` uses `ABANDON` so the child survives parent completion independently. The default `ParentClosePolicy` is `TERMINATE` (child killed). `REQUEST_CANCEL` is a middle ground — the child receives a cancellation request and can handle it gracefully before stopping, but does not run indefinitely like `ABANDON`
@@ -0,0 +1,84 @@
1
+ # heartbeat
2
+
3
+ ## DSL
4
+
5
+ ```twf
6
+ activity ProcessLargeFile(fileId: string) -> (ProcessResult):
7
+ file = download(fileId)
8
+ for (chunk in file.chunks):
9
+ process(chunk)
10
+ heartbeat(progress: {current: chunk, total: len(file.chunks)})
11
+ return ProcessResult{success: true}
12
+ ```
13
+
14
+ Calling the activity with heartbeat and timeout options:
15
+
16
+ ```twf
17
+ workflow ProcessFiles(fileId: string) -> (ProcessResult):
18
+ activity ProcessLargeFile(fileId) -> result
19
+ options:
20
+ start_to_close_timeout: 2h
21
+ heartbeat_timeout: 30s
22
+ close complete(result)
23
+ ```
24
+
25
+ ## Go
26
+
27
+ ```go
28
+ func ProcessLargeFile(ctx context.Context, fileId string) (ProcessResult, error) {
29
+ file, err := download(ctx, fileId)
30
+ if err != nil {
31
+ return ProcessResult{}, err
32
+ }
33
+ for _, chunk := range file.Chunks {
34
+ if err := process(ctx, chunk); err != nil {
35
+ return ProcessResult{}, err
36
+ }
37
+ activity.RecordHeartbeat(ctx, map[string]interface{}{
38
+ "current": chunk,
39
+ "total": len(file.Chunks),
40
+ })
41
+ }
42
+ return ProcessResult{Success: true}, nil
43
+ }
44
+ ```
45
+
46
+ ## Notes
47
+
48
+ - `heartbeat(args)` → `activity.RecordHeartbeat(ctx, details...)` — activity-only, never in workflows
49
+ - Heartbeat details are arbitrary; use a struct or map
50
+ - Set `HeartbeatTimeout` in `ActivityOptions` on the calling side — see [options.md](./options.md)
51
+ - The SDK throttles heartbeats automatically (sent at `heartbeatTimeout * 0.8`, capped at 60s). No manual batching needed — call `RecordHeartbeat` as often as desired
52
+ - The plain function shown above becomes a struct method in practice — see [activity-def.md](./activity-def.md) notes
53
+
54
+ ## Resume pattern
55
+
56
+ Activities that heartbeat should resume from the last recorded progress on retry. Check for previous heartbeat details at activity start:
57
+
58
+ ```go
59
+ func (a *Activities) ProcessLargeFile(ctx context.Context, fileId string) (ProcessResult, error) {
60
+ startIdx := 0
61
+ if activity.HasHeartbeatDetails(ctx) {
62
+ var lastIdx int
63
+ if err := activity.GetHeartbeatDetails(ctx, &lastIdx); err == nil {
64
+ startIdx = lastIdx + 1
65
+ }
66
+ }
67
+
68
+ file, err := a.download(ctx, fileId)
69
+ if err != nil {
70
+ return ProcessResult{}, err
71
+ }
72
+ for i := startIdx; i < len(file.Chunks); i++ {
73
+ if err := a.process(ctx, file.Chunks[i]); err != nil {
74
+ return ProcessResult{}, err
75
+ }
76
+ activity.RecordHeartbeat(ctx, i)
77
+ }
78
+ return ProcessResult{Success: true}, nil
79
+ }
80
+ ```
81
+
82
+ - `HasHeartbeatDetails` returns `false` on the first attempt (no prior failure)
83
+ - Details come from the last heartbeat delivered to the server (post-throttling) — may be slightly behind the last `RecordHeartbeat` call
84
+ - Heartbeat recording without a resume pattern is incomplete — the activity restarts from the beginning on every retry, wasting the progress tracking
@@ -0,0 +1,73 @@
1
+ # nexus service definition
2
+
3
+ ## DSL
4
+
5
+ ```twf
6
+ nexus service BillingService:
7
+ operation ChargePayment(PaymentRequest) -> (PaymentResult)
8
+ operation RefundPayment(RefundRequest) -> (RefundResult)
9
+ ```
10
+
11
+ ## Go — Service contract (shared types)
12
+
13
+ ```go
14
+ const BillingServiceName = "BillingService"
15
+ const ChargePaymentOp = "ChargePayment"
16
+ const RefundPaymentOp = "RefundPayment"
17
+ ```
18
+
19
+ ## Go — Async operation handler (workflow-backed)
20
+
21
+ ```go
22
+ var ChargePaymentOperation = temporalnexus.NewWorkflowRunOperation(
23
+ ChargePaymentOp,
24
+ BillingChargeWorkflow,
25
+ func(ctx context.Context, input PaymentRequest, options nexus.StartOperationOptions) (client.StartWorkflowOptions, error) {
26
+ return client.StartWorkflowOptions{
27
+ ID: "payment-" + input.OrderID, // business-meaningful ID for deduplication
28
+ }, nil
29
+ },
30
+ )
31
+ ```
32
+
33
+ ## Go — Sync operation handler
34
+
35
+ ```go
36
+ var RefundPaymentOperation = nexus.NewSyncOperation(RefundPaymentOp, func(ctx context.Context, input RefundRequest, options nexus.StartOperationOptions) (RefundResult, error) {
37
+ // Direct implementation or use temporalnexus.GetClient(ctx) for Temporal client calls
38
+ return RefundResult{}, nil
39
+ })
40
+ ```
41
+
42
+ ## Go — Registration on worker
43
+
44
+ ```go
45
+ service := nexus.NewService(BillingServiceName)
46
+ err := service.Register(ChargePaymentOperation, RefundPaymentOperation)
47
+ if err != nil {
48
+ log.Fatalln("Unable to register operations", err)
49
+ }
50
+ w.RegisterNexusService(service)
51
+ w.RegisterWorkflow(BillingChargeWorkflow) // handler workflows must also be registered
52
+ ```
53
+
54
+ ## Notes
55
+
56
+ - Imports: `"github.com/nexus-rpc/sdk-go/nexus"` for `nexus.NewService`, `nexus.NewSyncOperation`; `"go.temporal.io/sdk/temporalnexus"` for `NewWorkflowRunOperation`, `GetClient`
57
+ - Async operations (backed by workflows) use `temporalnexus.NewWorkflowRunOperation` — the workflow is started and the nexus operation resolves when the workflow completes
58
+ - Sync operations use `nexus.NewSyncOperation` — for direct computation or queries/signals via `temporalnexus.GetClient(ctx)`
59
+ - The service is registered on the handler worker (the target namespace's worker), not the caller
60
+ - Handler workflows must also be registered with `RegisterWorkflow` on the same worker
61
+
62
+ ## When to use: sync vs async operations
63
+
64
+ - **Sync** (`nexus.NewSyncOperation`): must complete within 10 seconds. Use for short computations, querying a workflow, signaling a workflow, sending an update, calling external services/databases directly. If the handler times out, the caller's Nexus machinery auto-retries until ScheduleToCloseTimeout
65
+ - **Async / workflow-backed** (`temporalnexus.NewWorkflowRunOperation`): arbitrary duration. Use when the operation is a Temporal workflow. The caller receives a completion callback when the handler workflow finishes. Supports cancellation propagation and re-attachment via operation token
66
+ - The choice is static — it depends on which SDK builder function you use, not on runtime conditions
67
+
68
+ ## Naming contract
69
+
70
+ - Operation names must match exactly at the wire level between caller and handler — no automatic transformation
71
+ - The string passed to `NewWorkflowRunOperation` or `NewSyncOperation` is the canonical operation name
72
+ - Define operation names as Go constants (as shown in the service contract section) and reference them from both caller and handler code
73
+ - In polyglot environments, both sides must agree on service name, operation names, and input/output types. Use Protobuf or JSON as the Data Converter format
@@ -0,0 +1,35 @@
1
+ # nexus
2
+
3
+ ## DSL
4
+
5
+ ```twf
6
+ nexus BillingEndpoint BillingService.ChargePayment(order.payment) -> paymentResult
7
+ ```
8
+
9
+ ## Go
10
+
11
+ ```go
12
+ c := workflow.NewNexusClient("BillingEndpoint", "BillingService")
13
+ var paymentResult PaymentResult
14
+ fut := c.ExecuteOperation(ctx, "ChargePayment", order.Payment, workflow.NexusOperationOptions{})
15
+ if err := fut.Get(ctx, &paymentResult); err != nil {
16
+ return Result{}, err
17
+ }
18
+ ```
19
+
20
+ ## Notes
21
+
22
+ - `workflow.NewNexusClient(endpoint, service)` creates a typed client scoped to one endpoint + service pair
23
+ - `ExecuteOperation(ctx, operationName, input, options)` starts the operation and returns a `NexusOperationFuture`
24
+ - The calling pattern mirrors child workflows — execute and `.Get()`
25
+ - For nexus options (timeouts), see [options.md](./options.md)
26
+ - For fire-and-forget nexus: see [detach.md](./detach.md)
27
+ - For nexus service definitions and handler registration: see [nexus-service-def.md](./nexus-service-def.md)
28
+ - Prefer referencing operation name constants from the service contract (see [nexus-service-def.md](./nexus-service-def.md#naming-contract)) rather than bare strings — keeps caller and handler in sync
29
+
30
+ ## When to use Nexus vs child workflow
31
+
32
+ - **Use Nexus when:** crossing namespace boundaries (child workflows are same-namespace only in Temporal Cloud), isolating service boundaries (caller only knows endpoint name + operation — not namespace, task queue, retry policy, or workflow ID constraints), enabling polyglot interop (caller and handler can use different SDKs/languages), or enforcing blast-radius isolation between teams
33
+ - **Use child workflows when:** within a single namespace and team, partitioning event history, representing a resource with a unique workflow ID, or executing periodic logic via Continue-As-New
34
+ - Nexus adds Endpoint configuration overhead — if you don't need cross-namespace or service isolation, child workflows are simpler
35
+ - Nexus works within a single namespace too — you can start with Nexus and later split into separate namespaces with configuration changes only
@@ -0,0 +1,138 @@
1
+ # options
2
+
3
+ ## Activity options
4
+
5
+ ### DSL
6
+
7
+ ```twf
8
+ activity QuickLookup(data.id) -> result
9
+ options:
10
+ start_to_close_timeout: 30s
11
+ ```
12
+
13
+ ### Go
14
+
15
+ ```go
16
+ actCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
17
+ StartToCloseTimeout: 30 * time.Second,
18
+ })
19
+ var result LookupResult
20
+ err := workflow.ExecuteActivity(actCtx, QuickLookup, data.Id).Get(ctx, &result)
21
+ ```
22
+
23
+ ## Activity options with retry policy
24
+
25
+ ### DSL
26
+
27
+ ```twf
28
+ activity UnreliableService(data) -> result
29
+ options:
30
+ start_to_close_timeout: 2m
31
+ retry_policy:
32
+ maximum_attempts: 5
33
+ initial_interval: 1s
34
+ backoff_coefficient: 2.0
35
+ maximum_interval: 60s
36
+ ```
37
+
38
+ ### Go
39
+
40
+ ```go
41
+ actCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
42
+ StartToCloseTimeout: 2 * time.Minute,
43
+ RetryPolicy: &temporal.RetryPolicy{
44
+ MaximumAttempts: 5,
45
+ InitialInterval: 1 * time.Second,
46
+ BackoffCoefficient: 2.0,
47
+ MaximumInterval: 60 * time.Second,
48
+ },
49
+ })
50
+ var result ServiceResult
51
+ err := workflow.ExecuteActivity(actCtx, UnreliableService, data).Get(ctx, &result)
52
+ ```
53
+
54
+ ## Child workflow options
55
+
56
+ ### DSL
57
+
58
+ ```twf
59
+ workflow ChildWorkflow(input.data) -> childResult
60
+ options:
61
+ workflow_execution_timeout: 1h
62
+ retry_policy:
63
+ maximum_attempts: 3
64
+ ```
65
+
66
+ ### Go
67
+
68
+ ```go
69
+ childCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
70
+ WorkflowExecutionTimeout: 1 * time.Hour,
71
+ RetryPolicy: &temporal.RetryPolicy{
72
+ MaximumAttempts: 3,
73
+ },
74
+ })
75
+ var childResult ChildResult
76
+ err := workflow.ExecuteChildWorkflow(childCtx, ChildWorkflow, input.Data).Get(ctx, &childResult)
77
+ ```
78
+
79
+ ## Child workflow `parent_close_policy`
80
+
81
+ ### DSL
82
+
83
+ ```twf
84
+ workflow NotifyCustomer(order.customer)
85
+ options:
86
+ parent_close_policy: REQUEST_CANCEL
87
+ ```
88
+
89
+ ### Go
90
+
91
+ ```go
92
+ childCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
93
+ ParentClosePolicy: enumspb.PARENT_CLOSE_POLICY_REQUEST_CANCEL,
94
+ })
95
+ err := workflow.ExecuteChildWorkflow(childCtx, NotifyCustomer, order.Customer).Get(ctx, nil)
96
+ ```
97
+
98
+ ## Nexus operation options
99
+
100
+ ### DSL
101
+
102
+ ```twf
103
+ nexus BillingEndpoint BillingService.ChargePayment(payment) -> paymentResult
104
+ options:
105
+ schedule_to_close_timeout: 1h
106
+ ```
107
+
108
+ ### Go
109
+
110
+ ```go
111
+ c := workflow.NewNexusClient("BillingEndpoint", "BillingService")
112
+ var paymentResult PaymentResult
113
+ fut := c.ExecuteOperation(ctx, "ChargePayment", payment, workflow.NexusOperationOptions{
114
+ ScheduleToCloseTimeout: 1 * time.Hour,
115
+ })
116
+ err := fut.Get(ctx, &paymentResult)
117
+ ```
118
+
119
+ ## Notes
120
+
121
+ - When no `options:` block is specified, set a default `ActivityOptions` with `StartToCloseTimeout` on `ctx` near the top of the workflow function
122
+ - Option keys map: `start_to_close_timeout` → `StartToCloseTimeout`, `schedule_to_close_timeout` → `ScheduleToCloseTimeout`, `heartbeat_timeout` → `HeartbeatTimeout`, `parent_close_policy` → `ParentClosePolicy` (type: `enumspb.ParentClosePolicy`; values: `PARENT_CLOSE_POLICY_TERMINATE` (default), `PARENT_CLOSE_POLICY_REQUEST_CANCEL`, `PARENT_CLOSE_POLICY_ABANDON`)
123
+ - `retry_policy:` → `&temporal.RetryPolicy{...}` (pointer)
124
+ - `NexusOperationOptions` fields: `ScheduleToCloseTimeout` (primary) and `CancellationType` (experimental). Options are passed inline — no context wrapping like activities
125
+
126
+ ## When to use each timeout
127
+
128
+ - **`StartToCloseTimeout`** — maximum time for a single activity attempt. Resets on each retry. Primary mechanism for detecting worker crashes. Temporal recommends always setting this
129
+ - **`ScheduleToCloseTimeout`** — total wall-clock time from when the activity is scheduled, including all retries. Does not reset. Use to cap total time when retries have exponential backoff
130
+ - **`WorkflowExecutionTimeout`** — end-to-end timeout for the entire workflow execution including retries and Continue-As-New chains. Set on the client when starting the workflow, not in `ActivityOptions`
131
+ - At least one of `StartToCloseTimeout` or `ScheduleToCloseTimeout` is required for activities — omitting both is a runtime error
132
+
133
+ ## Pitfalls
134
+
135
+ - **`MaximumAttempts: 1`** means one attempt total — this disables retries. `MaximumAttempts: 0` (the default) means unlimited retries
136
+ - **`BackoffCoefficient: 1.0`** produces fixed-interval retries (no exponential growth). The formula is `InitialInterval * BackoffCoefficient^(attempt-1)`
137
+ - Activities retry by default (server default: initial interval 1s, backoff 2.0, max interval 100s, unlimited attempts). Workflows do not retry by default
138
+ - If no `RetryPolicy` is specified on an activity, the server default applies — this is a common source of surprise