@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,304 @@
1
+ # TWF Notation Examples
2
+
3
+ ## Basic Structure
4
+
5
+ A complete `.twf` file: workflows, activities, worker registration, and namespace deployment.
6
+
7
+ ```twf
8
+ workflow WorkflowName(input: InputType) -> (OutputType):
9
+ activity ActivityName(input) -> result
10
+ workflow ChildWorkflowName(input) -> childResult
11
+ close complete(OutputType{result, childResult})
12
+
13
+ workflow ChildWorkflowName(input: InputType) -> (ChildResult):
14
+ activity DoWork(input) -> result
15
+ close complete(ChildResult{result})
16
+
17
+ activity ActivityName(input: InputType) -> (Result):
18
+ return process(input)
19
+
20
+ activity DoWork(input: InputType) -> (WorkResult):
21
+ return work(input)
22
+
23
+ worker mainWorker:
24
+ workflow WorkflowName
25
+ workflow ChildWorkflowName
26
+ activity ActivityName
27
+ activity DoWork
28
+
29
+ namespace default:
30
+ worker mainWorker
31
+ options:
32
+ task_queue: "main"
33
+ ```
34
+
35
+ ## Activity Body Detail
36
+
37
+ Activity bodies are intentionally free-form (`raw_stmt`) — pseudocode or descriptive text representing SDK-level implementation. Every activity needs at least one statement (comments alone are not enough). Detail level depends on how obvious the behavior is from name and signature:
38
+
39
+ **Obvious** — minimal body:
40
+
41
+ ```twf
42
+ activity SendEmail(to: string, body: string):
43
+ send(to, body)
44
+ ```
45
+
46
+ **Non-obvious** — comments describe intent, pseudocode anchors the body:
47
+
48
+ ```twf
49
+ activity ExecuteToolCalls(toolCalls: ToolCalls) -> (ToolResults):
50
+ # Look up each tool by name in the tool registry
51
+ # Execute calls in parallel where possible
52
+ # If a tool is not found, return an error result (don't fail the activity)
53
+ registry.executeAll(toolCalls)
54
+ ```
55
+
56
+ **Complex contract** — describe error conditions, ordering, and idempotency:
57
+
58
+ ```twf
59
+ activity ReconcileInventory(warehouseId: string, expected: Inventory) -> (ReconcileResult):
60
+ # Fetch current inventory, diff against expected, flag discrepancies
61
+ # Must be idempotent — running twice with same input produces same flags
62
+ # Warehouse API is rate-limited: max 10 requests/second
63
+ warehouse.reconcile(warehouseId, expected)
64
+ ```
65
+
66
+ ## Control Flow
67
+
68
+ ```twf
69
+ workflow ProcessOrder(order: Order) -> (Result):
70
+ activity ValidateOrder(order) -> validated
71
+
72
+ # Conditionals
73
+ if (validated.priority == "high"):
74
+ activity ExpediteOrder(order)
75
+ else:
76
+ activity StandardProcessing(order)
77
+
78
+ # Sequential loop — use for when each iteration depends on order or shared state
79
+ # For independent iterations, consider await all with parallel activities instead
80
+ for (item in order.items):
81
+ activity ProcessItem(item)
82
+
83
+ # Parallel execution — use await all when tasks are independent and all results needed
84
+ await all:
85
+ activity ReserveInventory(order) -> inventory
86
+ activity ProcessPayment(order) -> payment
87
+
88
+ close complete(Result{inventory, payment})
89
+
90
+ # Every referenced activity must be defined
91
+ activity ValidateOrder(order: Order) -> (ValidateResult):
92
+ return validate(order)
93
+
94
+ activity ExpediteOrder(order: Order):
95
+ expedite(order)
96
+
97
+ activity StandardProcessing(order: Order):
98
+ process(order)
99
+
100
+ activity ProcessItem(item: Item) -> (ItemResult):
101
+ return process(item)
102
+
103
+ activity ReserveInventory(order: Order) -> (Inventory):
104
+ return reserve(order)
105
+
106
+ activity ProcessPayment(order: Order) -> (Payment):
107
+ return charge(order)
108
+ ```
109
+
110
+ ## Temporal Primitives in Notation
111
+
112
+ ```twf
113
+ workflow OrderFulfillment(orderId: string) -> (OrderResult):
114
+ # Handlers go before body
115
+ # signal = write (fire-and-forget)
116
+ signal PaymentReceived(transactionId: string, amount: decimal):
117
+ paymentStatus = "received"
118
+ lastTransactionId = transactionId
119
+
120
+ # query = read (caller gets result)
121
+ query GetOrderStatus() -> (OrderStatus):
122
+ return OrderStatus{status: status, payment: paymentStatus}
123
+
124
+ # update = read-write (caller sends data, gets result)
125
+ update UpdateShippingAddress(address: Address) -> (Result):
126
+ activity ValidateAddress(address) -> validation
127
+ if (validation.valid):
128
+ shippingAddress = address
129
+ return Result{success: true}
130
+ else:
131
+ return Result{success: false, error: validation.reason}
132
+
133
+ # Workflow body starts after handlers
134
+ activity GetOrder(orderId) -> order
135
+ paymentStatus = "pending"
136
+ status = "awaiting_payment"
137
+
138
+ # Durable timer
139
+ await timer(1h)
140
+
141
+ # Wait for signal with timeout
142
+ await one:
143
+ signal PaymentReceived:
144
+ status = "processing"
145
+ timer(24h):
146
+ activity CancelOrder(orderId)
147
+ close fail(OrderResult{status: "cancelled"})
148
+
149
+ # Child workflow
150
+ workflow ShipOrder(order) -> shipResult
151
+
152
+ # Cross-namespace nexus call
153
+ nexus NotificationsEndpoint NotificationsService.SendNotification(order.customer, "shipped")
154
+
155
+ close complete(OrderResult{status: "completed"})
156
+
157
+ # Supporting definitions
158
+ activity GetOrder(orderId: string) -> (Order):
159
+ return db.get(orderId)
160
+
161
+ activity ValidateAddress(address: Address) -> (Validation):
162
+ return validate(address)
163
+
164
+ activity CancelOrder(orderId: string):
165
+ cancel(orderId)
166
+
167
+ workflow ShipOrder(order: Order) -> (ShipResult):
168
+ activity CreateShipment(order) -> shipment
169
+ close complete(ShipResult{shipment})
170
+
171
+ activity CreateShipment(order: Order) -> (Shipment):
172
+ return ship(order)
173
+
174
+ workflow SendNotification(customer: Customer, message: string):
175
+ activity Notify(customer, message)
176
+ close complete
177
+
178
+ activity Notify(customer: Customer, message: string):
179
+ send(customer, message)
180
+
181
+ nexus service NotificationsService:
182
+ async SendNotification workflow SendNotification
183
+
184
+ worker orderFulfillmentWorker:
185
+ workflow OrderFulfillment
186
+ workflow ShipOrder
187
+ workflow SendNotification
188
+ activity GetOrder
189
+ activity ValidateAddress
190
+ activity CancelOrder
191
+ activity CreateShipment
192
+ activity Notify
193
+ nexus service NotificationsService
194
+
195
+ namespace default:
196
+ worker orderFulfillmentWorker
197
+ options:
198
+ task_queue: "orderFulfillment"
199
+ nexus endpoint NotificationsEndpoint
200
+ options:
201
+ task_queue: "orderFulfillment"
202
+ ```
203
+
204
+ ## Async Patterns
205
+
206
+ ```twf
207
+ workflow OrderPipeline(order: Order) -> (PipelineResult):
208
+ state:
209
+ condition paymentConfirmed
210
+
211
+ # Update handler — validates and confirms payment
212
+ update ConfirmPayment(txn: Transaction) -> (ConfirmResult):
213
+ activity ValidateTxn(txn) -> validation
214
+ if (validation.ok):
215
+ set paymentConfirmed
216
+ return ConfirmResult{accepted: true}
217
+ else:
218
+ return ConfirmResult{accepted: false, reason: validation.error}
219
+
220
+ # Promise — start async, await later
221
+ promise inventory <- activity CheckInventory(order)
222
+
223
+ # Detach — fire-and-forget, no result observation
224
+ detach workflow AuditLog(order)
225
+
226
+ # Await condition — blocks until handler sets it
227
+ await paymentConfirmed
228
+
229
+ # Await promise — get the result started earlier
230
+ await inventory -> stock
231
+
232
+ # Switch — multi-branch dispatch
233
+ switch (stock.level):
234
+ case "high":
235
+ activity ShipStandard(order) -> shipment
236
+ case "low":
237
+ activity ShipFromWarehouse(order, stock.warehouseId) -> shipment
238
+ case "none":
239
+ close fail(PipelineResult{error: "out of stock"})
240
+
241
+ close complete(PipelineResult{shipment})
242
+
243
+ # Heartbeat — report progress from long-running activity
244
+ activity ProcessLargeDataset(datasetId: string) -> (ProcessResult):
245
+ # Call heartbeat() periodically to report progress
246
+ # If worker dies, Temporal detects missed heartbeat and retries on another worker
247
+ heartbeat()
248
+ return process(datasetId)
249
+
250
+ # Call-level options — override timeout, routing, retry for a specific call
251
+ workflow DeployService(config: DeployConfig) -> (DeployResult):
252
+ activity BuildArtifact(config) -> artifact
253
+ activity Deploy(artifact) -> result
254
+ options:
255
+ start_to_close_timeout: 30m
256
+ heartbeat_timeout: 5m
257
+ retry_policy:
258
+ maximum_attempts: 3
259
+ close complete(DeployResult{result})
260
+
261
+ # Supporting definitions
262
+ activity CheckInventory(order: Order) -> (InventoryStatus):
263
+ return inventory.check(order)
264
+
265
+ activity ValidateTxn(txn: Transaction) -> (TxnValidation):
266
+ return payments.validate(txn)
267
+
268
+ workflow AuditLog(order: Order):
269
+ activity RecordAudit(order)
270
+ close complete
271
+
272
+ activity RecordAudit(order: Order):
273
+ audit.record(order)
274
+
275
+ activity ShipStandard(order: Order) -> (Shipment):
276
+ return shipping.standard(order)
277
+
278
+ activity ShipFromWarehouse(order: Order, warehouseId: string) -> (Shipment):
279
+ return shipping.fromWarehouse(order, warehouseId)
280
+
281
+ activity BuildArtifact(config: DeployConfig) -> (Artifact):
282
+ return build(config)
283
+
284
+ activity Deploy(artifact: Artifact) -> (DeployStatus):
285
+ return deploy(artifact)
286
+
287
+ worker pipelineWorker:
288
+ workflow OrderPipeline
289
+ workflow AuditLog
290
+ workflow DeployService
291
+ activity CheckInventory
292
+ activity ValidateTxn
293
+ activity RecordAudit
294
+ activity ShipStandard
295
+ activity ShipFromWarehouse
296
+ activity ProcessLargeDataset
297
+ activity BuildArtifact
298
+ activity Deploy
299
+
300
+ namespace default:
301
+ worker pipelineWorker
302
+ options:
303
+ task_queue: "pipeline"
304
+ ```
@@ -0,0 +1,70 @@
1
+ # TWF Notation Reference
2
+
3
+ | Syntax | Meaning |
4
+ |--------|---------|
5
+ | `activity Name(args) -> result` | Call activity, bind result (default for single operations) |
6
+ | `workflow Name(args) -> result` | Call child workflow, bind result (multi-step with own failure boundary) |
7
+ | `nexus Endpoint Service.Op(args) -> result` | Nexus service operation call |
8
+ | `detach nexus Endpoint Service.Op(args)` | Fire-and-forget nexus call (no result observation possible) |
9
+ | `promise p <- nexus Endpoint Service.Op(args)` | Start async nexus call |
10
+ | `promise p <- activity Name(args)` | Start async activity (use when you need the result later, not immediately) |
11
+ | `promise p <- workflow Name(args)` | Start async child workflow (parallel child execution) |
12
+ | `promise p <- timer(duration)` | Start async timer |
13
+ | `promise p <- signal Name` | Promise for signal |
14
+ | `await p -> result` | Await promise, bind result |
15
+ | `state:` | Workflow state block (conditions and variable initializations) |
16
+ | `condition name` | Named boolean awaitable (in `state:` block) |
17
+ | `set name` | Set condition to true (coordinate between handlers and main body) |
18
+ | `unset name` | Set condition to false |
19
+ | `await name` | Await condition |
20
+ | `detach workflow Name(args)` | Fire-and-forget child workflow (no result observation possible) |
21
+ | `await timer(duration)` | Durable sleep |
22
+ | `await signal Name` | Wait for signal |
23
+ | `await update Name` | Wait for update |
24
+ | `await nexus Endpoint Service.Op(args) -> result` | Wait for nexus call |
25
+ | `await one:` | Race: first to complete wins (timeouts, signal-or-timer patterns). Non-winning operations are **not** cancelled — they keep running until the workflow run ends |
26
+ | `await all:` | Join: wait for all (parallel execution) |
27
+ | `heartbeat()` | Report progress from long-running activity (detect worker death) |
28
+ | `options: key: value` | Options block for activity/workflow/nexus calls |
29
+ | `-> (Type)` | Return type (always parenthesized) |
30
+ | `-> result` | Bind preceding result |
31
+ | `close complete\|fail\|continue_as_new(Value)` | End workflow with result, failure, or continuation |
32
+ | `if (expr):` / `else:` | Conditional |
33
+ | `for (x in collection):` | Bounded loop |
34
+ | `for:` | Infinite loop (needs `close continue_as_new` or `close complete`) |
35
+ | `switch (expr):` / `case val:` | Multi-branch conditional |
36
+ | `close continue_as_new(args)` | Reset history and continue |
37
+ | `signal Name(params):` | Signal handler (in workflow, before body) |
38
+ | `query Name(params) -> (Type):` | Query handler (in workflow, before body) |
39
+ | `update Name(params) -> (Type):` | Update handler (in workflow, before body) |
40
+ | `nexus service Name:` | Nexus service definition (top-level) |
41
+ | `async OpName workflow WorkflowName` | Async nexus operation (in service body) |
42
+ | `sync OpName(params) -> (Type):` | Sync nexus operation (in service body) |
43
+ | `worker name:` | Worker type set definition |
44
+ | `nexus service Name` (in worker) | Register nexus service on worker |
45
+ | `namespace name:` | Namespace definition (deployment with options) |
46
+ | `nexus endpoint Name` (in namespace) | Nexus endpoint instantiation with task_queue |
47
+
48
+ ## Common `options:` Keys
49
+
50
+ `options:` blocks attach operational config to a call. A clean `twf check` does not require any of these — but the design must reason about them (idempotency, history cost, failure behavior, routing). This is a lookup table; the *reasoning* lives in the worked example in [SKILL.md](../SKILL.md#design-flow) and the topic docs.
51
+
52
+ | Key | Attaches to | Why it matters |
53
+ |-----|-------------|----------------|
54
+ | `task_queue` | activity, workflow | Routing — pins the call to a specific worker pool (capability, isolation, region) |
55
+ | `start_to_close_timeout` | activity | Failure behavior — bounds a single attempt; required in practice for any real activity |
56
+ | `schedule_to_close_timeout` | activity, nexus | Total time budget across queueing + attempts |
57
+ | `schedule_to_start_timeout` | activity | Tolerance for queue wait before a worker picks it up |
58
+ | `heartbeat_timeout` | activity | Worker-death detection for long activities (pairs with `heartbeat()`) |
59
+ | `retry_policy` | activity, workflow, nexus | Failure behavior — `initial_interval`, `backoff_coefficient`, `maximum_interval`, `maximum_attempts`, `non_retryable_error_types` |
60
+ | `workflow_execution_timeout` / `workflow_run_timeout` / `workflow_task_timeout` | workflow | Bounds for total / per-run / per-task duration |
61
+ | `parent_close_policy` | workflow | Child lifecycle when parent closes: `TERMINATE` (default), `REQUEST_CANCEL`, `ABANDON` |
62
+ | `workflow_id_reuse_policy` | workflow | Idempotency on retry: `ALLOW_DUPLICATE`, `ALLOW_DUPLICATE_FAILED_ONLY`, `REJECT_DUPLICATE`, `TERMINATE_IF_RUNNING` |
63
+ | `cron_schedule` | workflow | Recurring child execution |
64
+ | `priority` | activity, workflow, nexus | Relative dispatch priority |
65
+
66
+ > The child-workflow **ID** itself is an SDK-level concern, not a TWF call option — see [child-workflows.md](../topics/child-workflows.md#workflow-id-design). `task_queue` is intentionally **not** a nexus-call option; nexus routing comes from the endpoint declaration.
67
+
68
+ > **Worker-instantiation `options:` are a separate set** from the call options above — they attach to a `worker` (or `nexus endpoint`) inside a `namespace`, not to a call. `task_queue` is required; `versioning` (`none` / `build_id` / `deployment`) is the design-altitude strategy key. The set is the SDK union, accepted permissively — see [task-queues.md](../topics/task-queues.md#worker-options) and `twf spec`.
69
+
70
+ Full grammar: [`tools/spec/sections/`](../../../tools/spec/sections/) (or run `twf spec`).
@@ -0,0 +1,65 @@
1
+ # Temporal Primitives Reference
2
+
3
+ ## Workflow Execution
4
+
5
+ | Primitive | Purpose | Details |
6
+ |-----------|---------|---------|
7
+ | `activity` | Side-effecting operation | Core primitive |
8
+ | `workflow` | Child workflow | [child-workflows.md](../topics/child-workflows.md) |
9
+ | `nexus` | Cross-namespace call | [nexus.md](../topics/nexus.md) |
10
+ | `promise` | Async operation, await later | [promises-conditions.md](../topics/promises-conditions.md) |
11
+ | `detach` | Fire-and-forget child/nexus | [child-workflows.md](../topics/child-workflows.md), [nexus.md](../topics/nexus.md) |
12
+ | `close continue_as_new` | Reset history, continue | [long-running.md](../topics/long-running.md) |
13
+
14
+ **Selection:** `activity` for single side-effecting operations. `workflow` for multi-step orchestration needing its own retry/failure boundary. `nexus` when crossing namespace or team boundaries. `promise` when you need the result later, not immediately. `detach` for fire-and-forget — you cannot observe the result. `close continue_as_new` when history grows unbounded (long-running or entity workflows).
15
+
16
+ ## Timing
17
+
18
+ | Primitive | Purpose | Details |
19
+ |-----------|---------|---------|
20
+ | `timer` | Durable sleep (survives restarts) | [timers-scheduling.md](../topics/timers-scheduling.md) |
21
+ | `schedule` | Cron-like recurring execution | [timers-scheduling.md](../topics/timers-scheduling.md) |
22
+ | `timeout` | Deadline for operations | [timers-scheduling.md](../topics/timers-scheduling.md) |
23
+
24
+ **Selection:** `timer` for durable waits inside workflow logic (survives replay). Activity-level timeouts (`heartbeat_timeout`, `start_to_close_timeout` in `options:`) for bounding activity execution. `schedule` for cron-like recurring workflow starts — this is platform configuration, not TWF notation.
25
+
26
+ ## External Communication
27
+
28
+ Read, write, or read-write interaction with a running workflow:
29
+
30
+ | Primitive | I/O | Purpose | Details |
31
+ |-----------|-----|---------|---------|
32
+ | `query` | Read | Sync read of workflow state | [signals-queries-updates.md](../topics/signals-queries-updates.md) |
33
+ | `signal` | Write | Async fire-and-forget into workflow | [signals-queries-updates.md](../topics/signals-queries-updates.md) |
34
+ | `update` | Read-write | Sync mutation with result | [signals-queries-updates.md](../topics/signals-queries-updates.md) |
35
+
36
+ **Selection:** Use I/O direction as the decision rule. **Do not** use `query` to modify state — queries must be pure reads. **Do not** use `signal` when you need confirmation — signals have no return value. Prefer `update` over signal-then-query when the caller needs to know the mutation succeeded.
37
+
38
+ ## State and Conditions
39
+
40
+ | Primitive | Purpose | Details |
41
+ |-----------|---------|---------|
42
+ | `state` | Workflow state declaration block | [promises-conditions.md](../topics/promises-conditions.md) |
43
+ | `condition` | Named boolean awaitable | [promises-conditions.md](../topics/promises-conditions.md) |
44
+ | `set` / `unset` | Set condition to true / false | [promises-conditions.md](../topics/promises-conditions.md) |
45
+
46
+ **Selection:** Use `condition` when handlers and the main workflow body need to coordinate on a boolean flag (e.g., "payment received", "approved"). Use local variables for workflow-scoped state that doesn't need cross-handler coordination.
47
+
48
+ ## Activity Options
49
+
50
+ | Primitive | Purpose | Details |
51
+ |-----------|---------|---------|
52
+ | `heartbeat` | Report progress, detect worker death | [activities-advanced.md](../topics/activities-advanced.md) |
53
+ | `async_complete` | Complete from external system | [activities-advanced.md](../topics/activities-advanced.md) |
54
+
55
+ ## Infrastructure
56
+
57
+ | Primitive | Purpose | Details |
58
+ |-----------|---------|---------|
59
+ | `task_queue` | Route work to specific workers | [task-queues.md](../topics/task-queues.md) |
60
+ | `worker` | Defines a reusable type set (which workflows, activities, nexus services run together) | [task-queues.md](../topics/task-queues.md) |
61
+ | `namespace` | Deployment topology — instantiates workers with `task_queue` and options | [task-queues.md](../topics/task-queues.md) |
62
+ | `nexus service` | Typed operation group for cross-namespace calls | [nexus.md](../topics/nexus.md) |
63
+ | `nexus endpoint` | Routes nexus calls to a target task queue | [nexus.md](../topics/nexus.md) |
64
+ | `search_attribute` | Index workflow for queries | Core primitive |
65
+ | `memo` | Attach metadata to workflow | Core primitive |
@@ -0,0 +1,80 @@
1
+ # Project-Discovery Subagent
2
+
3
+ A single reusable primitive: **scan an existing repo on a bounded slice, return a compact summary.** Owned here (design-skill) and shared by every skill that has to understand a repo it didn't write:
4
+
5
+ - **design reverse-engineering** — [reverse-engineering.md](./reverse-engineering.md) B1a bootstrap (no `.twf`) and B1b drift-check.
6
+ - **author-go existing-repo Orient** — `temporal-architect-author-go/SKILL.md` (see `author-go-skill/REVISIONS_001` Group 1).
7
+ - **author-infra** — repo/tooling discovery when that skill lands.
8
+
9
+ Design once, reuse. This spec is the broadened, shared form of the `sdk-explorer` agent sketched in [SUBAGENT_ADOPTION.md](../../temporal-architect-author-go/SUBAGENT_ADOPTION.md) — it answers that doc's open question ("should it also scan the user's existing project code for conventions?") with **yes**: project-convention discovery is this subagent's job, not the orchestrator's.
10
+
11
+ ## Why a subagent
12
+
13
+ Discovery is context-heavy and disposable. Reading a repo's tooling, layout, and registration wiring burns tokens the design conversation needs — and almost none of what it reads belongs in the main context, only the conclusions. Running discovery in an **isolated subagent that returns a summary** is the context-protection move: the orchestrator stays focused on design, the subagent absorbs the noise.
14
+
15
+ ## Trigger discipline
16
+
17
+ Dispatch **deliberately, on a bounded slice** — never reflexively, never "scan the whole repo." The caller names the slice (a domain, a directory, a set of entry points). An unbounded scan defeats the purpose: it floods the subagent's own context and returns a summary too broad to act on. If the slice is unclear, the orchestrator narrows it *with the user* before dispatching.
18
+
19
+ ## Inputs
20
+
21
+ | Input | Description |
22
+ |-------|-------------|
23
+ | Repo root | Absolute path to the project. |
24
+ | Bounded slice | The paths / domain / entry points to scan. Required — no whole-repo scans. |
25
+ | Focus | What the caller needs (e.g. "Temporal registration style", "codegen tooling", "existing `.twf` for this domain"). |
26
+
27
+ ## What it scans
28
+
29
+ - **Build / codegen tooling** — `Makefile`, `buf.yaml` / `buf.gen.yaml`, `//go:generate` directives, `protoc` plugins. Identifies whether code is hand-written or generated, and by what.
30
+ - **Package layout** — directory structure, where workflows/activities live, `proto/` → `gen/` → `lib/` style splits.
31
+ - **Temporal SDK usage** — `go.temporal.io/sdk` imports, `workflow.ExecuteActivity` / `ExecuteChildWorkflow` / Nexus call sites, signal/query/update handlers.
32
+ - **Registration style** — `worker.New` + `RegisterWorkflow`/`RegisterActivity`, generated `RegisterXxxActivities/Workflows` helpers, DI wiring (`fx`), struct-vs-func activities.
33
+ - **`.twf` / `.tf` presence** — existing design files and Terraform/infra files for this slice.
34
+ - **Comment conventions** — impl-link headers and cross-domain stub markers (see [twf-conventions.md](./twf-conventions.md)); these point discovery straight at the implementation.
35
+
36
+ ## Output
37
+
38
+ A **compact structured summary** — conclusions, not raw dumps. Never paste whole files back. Cover:
39
+
40
+ - Tooling: hand-written vs generated; generator stack if any.
41
+ - Layout: where the relevant code lives for the scanned slice.
42
+ - SDK usage: workflows/activities/nexus found, with their call sites.
43
+ - Registration: how workers register the discovered types.
44
+ - Existing `.twf`: present/absent, and what it covers.
45
+ - Impl-links: any comment conventions found, and what they point to.
46
+ - Open questions the caller must resolve.
47
+
48
+ ## Agent definition
49
+
50
+ Copy-pastable subagent prompt (frontmatter style matches `sdk-explorer` in [SUBAGENT_ADOPTION.md](../../temporal-architect-author-go/SUBAGENT_ADOPTION.md)):
51
+
52
+ ```yaml
53
+ ---
54
+ name: project-discovery
55
+ description: Scan an existing repo on a bounded slice for tooling, layout, conventions, and Temporal usage. Returns a compact summary.
56
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch
57
+ model: sonnet
58
+ ---
59
+ ```
60
+
61
+ ```
62
+ You are scanning an existing repository on a BOUNDED SLICE. Do not scan the whole
63
+ repo — stay within the slice the caller named.
64
+
65
+ Inputs:
66
+ - Repo root: <path>
67
+ - Slice: <paths / domain / entry points>
68
+ - Focus: <what the caller needs>
69
+
70
+ Scan for: build/codegen tooling (Makefile, buf.gen.yaml, //go:generate), package
71
+ layout, Temporal SDK usage (workflow/activity/nexus call sites, handlers),
72
+ registration style (worker.New + Register*, generated helpers, fx wiring), .twf/.tf
73
+ presence, and impl-link / cross-domain-stub comment conventions.
74
+
75
+ Return a COMPACT STRUCTURED SUMMARY — conclusions only, never raw file dumps:
76
+ tooling, layout, SDK usage, registration, existing .twf, impl-links, open questions.
77
+
78
+ For SDK-symbol meaning, delegate to the relevant author skill's reference (e.g.
79
+ author-go references read backward) rather than reconstructing it yourself.
80
+ ```
@@ -0,0 +1,53 @@
1
+ # Reverse Engineering: Code → `.twf`
2
+
3
+ The dominant adoption path is not greenfield. Most teams already run Temporal code and want a `.twf` that captures what they have — to review it, refactor it, or hand it to the visualizer. Here the **implementation is the requirement** and the `.twf` is recovered from it.
4
+
5
+ Treat the recovered `.twf` as the **central, living design medium** — the artifact the team will keep editing and reviewing — not a throwaway sketch of the code. That framing sets the bar: capture the design faithfully enough that it's worth keeping.
6
+
7
+ This is a **parallel path** to the greenfield loop, kept separate on purpose. The main [SKILL.md](../SKILL.md) body carries only a thin trigger; the mechanics live here so the fast forward loop never pays for them. Discovery runs in an isolated [project-discovery subagent](./project-discovery-subagent.md) and returns a summary — the context-protection move.
8
+
9
+ ## Trigger discipline
10
+
11
+ Reverse-engineer **deliberately, on a bounded slice** — one domain, one service, one set of entry points at a time. Never scan the whole repo reflexively. A slice keeps both the discovery subagent and the resulting `.twf` reviewable; a whole-repo sweep produces a `.twf` nobody can check.
12
+
13
+ ## Two cases
14
+
15
+ ### B1a — Bootstrap (no `.twf` yet)
16
+
17
+ The repo has Temporal code but no design file. Recover one from scratch:
18
+
19
+ 1. **Discover** — dispatch the [project-discovery subagent](./project-discovery-subagent.md) on the bounded slice. It returns tooling, layout, SDK usage, registration style, and any impl-link comments.
20
+ 2. **Extract** — translate the discovered workflows/activities/nexus into `.twf` (see [Reading strategy](#reading-strategy)).
21
+ 3. **Fidelity check** — confirm the `.twf` matches what the code actually does (see [Fidelity first](#fidelity-first-then-design-review)).
22
+ 4. **Design Review** — only now run the standard [Design Review](../SKILL.md#design-review).
23
+
24
+ Write the [impl-link header](./twf-conventions.md) as you extract, so the new `.twf` records where its implementation lives.
25
+
26
+ ### B1b — Drift / sync (`.twf` exists but is stale)
27
+
28
+ A `.twf` exists but the code has moved on. Two halves, with different readiness:
29
+
30
+ - **Check (available now):** on a bounded slice, compare the `.twf` against current code and report divergences — missing activities, changed boundaries, dropped signals. This needs no new tooling.
31
+ - **Sync (deferred):** mechanically reconciling `.twf` ↔ implementation depends on the future twf↔impl mapping (`dsl/BACKLOG.md` → Reference Annotations / `@ref`). Until that lands, reconcile by hand: re-extract the drifted slice (B1a steps 2-4) and update the `.twf`, treating the code as the source of truth for *behavior* and the existing `.twf` as the source of truth for *intent*.
32
+
33
+ ## Reading strategy
34
+
35
+ Read for **design structure**, not line-by-line behavior:
36
+
37
+ - **Find entry points first** — client-started workflows, schedule-started workflows, Nexus-operation-backing workflows, handler-bearing workflows. These are the roots of the `.twf`.
38
+ - **Follow call sites** — `workflow.ExecuteActivity`, `workflow.ExecuteChildWorkflow`, Nexus operation calls. Each is an `activity` / `workflow` / `nexus` call in TWF.
39
+ - **Ignore the plumbing** — error wrapping, `context` threading, logging, options structs, retry/timeout boilerplate. None of it changes the design shape; capture only the options that express a real decision (a tuned timeout, a capped retry).
40
+ - **Detect parallelism** — `workflow.Go`, selectors (`workflow.NewSelector`), futures held and `.Get` later → `await all` / `await one` / `promise` in TWF. Sequential `.Get` right after the call is just a synchronous call.
41
+
42
+ ### Delegate SDK reading to the author skill
43
+
44
+ Do not reconstruct SDK semantics yourself. The author skills already hold the DSL↔SDK mapping — **read their symbol tables backward.** For Go, use the `temporal-architect-author-go` references (e.g. `activity-call.md`, `workflow-call.md`, `await-all.md`) and, for generated code, its forthcoming `reference/proto-driven.md` Rosetta Stone (generated `XxxActivities` iface, `RegisterXxxActivities`, `XxxFuture`, etc. → the underlying `activity`/`workflow`). Forward mappings (DSL → Go) read in reverse give you Go → DSL for free.
45
+
46
+ ## Fidelity first, then Design Review
47
+
48
+ **Capture what the code does, faithfully — including its anti-patterns.** A wrapper workflow, a monolith, an unbounded loop: extract them *as they are*. The recovered `.twf` must mirror reality before it's worth reviewing; silently "fixing" during extraction produces a `.twf` that describes a system that doesn't exist, and hides the very problems the team needs to see.
49
+
50
+ - **Do not** refactor, rename, or "improve" during extraction.
51
+ - **Intent-fill only genuinely-unimplemented stubs** — a `// TODO` body, a panic-not-implemented, an empty handler. Mark these clearly; everything else is recovered, not invented.
52
+
53
+ Once the `.twf` faithfully reflects the code, run the standard [Design Review](../SKILL.md#design-review). That pass is where anti-patterns get *named and proposed for change* — separately from extraction, so the record of "what exists" stays honest and the "what should change" is an explicit, reviewable diff.
@@ -0,0 +1,43 @@
1
+ # `.twf` Conventions
2
+
3
+ How to lay out `.twf` files and the small set of comment conventions that carry information the grammar doesn't yet express.
4
+
5
+ ## One package for all `.twf`
6
+
7
+ **Recommendation: keep all of a project's `.twf` in one package** (one directory whose files resolve together). Cross-file references — an `activity` defined in one file and called in another, a `nexus service` provided in one file and called in another — resolve only within a shared file set. One package means every reference resolves and `twf check` sees the whole design at once.
8
+
9
+ This decouples `.twf` layout from code layout: the files no longer mirror the directory structure of the implementation. That trade-off is worth it — resolution coverage matters more than co-location — and the [impl-link header](#impl-link-header) below restores the link to the code.
10
+
11
+ ## Comment conventions
12
+
13
+ `.twf` comments (`#`) are free text to the parser, but a small **named set** carries conventional meaning the tooling and the reader rely on. Use these exact forms.
14
+
15
+ ### Impl-link header
16
+
17
+ A top-of-file comment linking a `.twf` to the implementation directory (or directories) it describes:
18
+
19
+ ```twf
20
+ # impl: order-service/workflows, order-service/activities
21
+ workflow ProcessOrder(order: Order) -> (Result):
22
+ activity ChargePayment(order) -> receipt
23
+ close complete(Result{receipt})
24
+
25
+ activity ChargePayment(order: Order) -> (Receipt):
26
+ charge(order.payment)
27
+ ```
28
+
29
+ Because the one-package layout decouples `.twf` from code, the implementation link can no longer be inferred from location — it has to be explicit. The header is that link. It is also a [reverse-engineering](./reverse-engineering.md) aid: the [project-discovery subagent](./project-discovery-subagent.md) *reads* it to jump straight to the code, and extraction *writes* it when recovering a `.twf` from existing code.
30
+
31
+ This is the interim form. The durable, machine-checkable version is per-symbol reference annotations (`@ref`), deferred in `dsl/BACKLOG.md` — when that lands, the header convention gives way to it.
32
+
33
+ ### Cross-domain stub marker
34
+
35
+ A marker on a local stub definition that exists only to satisfy resolution for a symbol actually defined in another `.twf`:
36
+
37
+ ```twf
38
+ # cross-domain stub — defined in payments.twf
39
+ nexus service PaymentService:
40
+ sync ChargeCard(req: ChargeRequest) -> (Receipt)
41
+ ```
42
+
43
+ Defining *one* local `nexus service` turns every other service reference in the file into a hard error — including genuinely external services in other namespaces (see [common-errors.md](./common-errors.md#nexus-resolution-external-warning-vs-local-error)). The stub marker documents that the definition is a local placeholder, not the real owner, so a partial / per-domain file can both call and provide services without tripping the resolution cliff. The marker makes the stub's intent obvious to readers and to the discovery subagent.
@@ -0,0 +1,43 @@
1
+ # Workflow vs Activity Boundary
2
+
3
+ ## Use Activities When
4
+
5
+ **Default granularity: one activity per network call / external interaction.** This is the right default because it makes Temporal's retry, timeout, and backoff land at exactly the unit that can fail independently — one activity per fallible boundary gives clean, granular retry semantics. Start here, then deviate only as an optimization (batching, local activities — see [core-principles.md](./core-principles.md#activities-are-for-io--not-in-memory-work)).
6
+
7
+ - Single atomic operation
8
+ - External system interaction (API, DB, file)
9
+ - Short, predictable completion time (single timeout period)
10
+ - No orchestration logic
11
+
12
+ Conversely, do **not** create an activity for work that touches no external system — reads of already-held data, in-memory derivation, or accumulation are workflow code, not activities.
13
+
14
+ ## Use Child Workflows When
15
+
16
+ - Multiple steps with independent retry/timeout policies
17
+ - Reusable across parent workflows
18
+ - Separate failure boundary needed
19
+ - Very long operations (separate history)
20
+ - Complex enough to warrant own tests
21
+
22
+ **Rule of thumb:** Loops or conditionals inside an activity → should be a workflow.
23
+
24
+ ## Use Nexus When
25
+
26
+ - Crosses namespace or team boundaries (separate deployment lifecycle)
27
+ - Different team owns the target service
28
+ - Target needs independent scaling, versioning, or failure isolation at the organizational level
29
+ - You want a typed API contract between services
30
+
31
+ **Child workflow vs nexus:** Child workflows share a namespace and are tightly coupled to the parent's lifecycle. Nexus calls are loosely coupled — the target is an independent service that may be owned by another team, deployed on a different schedule, or running in a different namespace.
32
+
33
+ ## Common Mistakes
34
+
35
+ **Wrapper workflow:** A child workflow containing a single activity call adds orchestration overhead with no benefit. If there's only one step, use an activity directly.
36
+
37
+ **Monolithic workflow:** All logic in one workflow with hundreds of history events. If a workflow has more than ~10 sequential activity calls, consider decomposing into child workflows.
38
+
39
+ **Activity with orchestration:** If an activity contains retry logic, conditional branching, or calls to other services, it should be a workflow — these are orchestration concerns that benefit from Temporal's durability.
40
+
41
+ See [child-workflows.md](../topics/child-workflows.md) for detailed child workflow patterns and the full decision table.
42
+
43
+ For deployment topology and task queue routing, see [task-queues.md](../topics/task-queues.md).