This skill should be used when the user asks about "workflow signal", "workflow query", "workflow update", "send signal", "query workflow state", "SignalWithStart", "workflow communication", or needs guidance on message passing patterns in Temporal.
From timelordnpx claudepluginhub therealbill/mynet --plugin timelordThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Patterns for communicating with running workflows in Temporal.
| Feature | Direction | Mutation | Sync |
|---|---|---|---|
| Signal | External → Workflow | Yes | No |
| Query | External → Workflow | No | Yes |
| Update | External ↔ Workflow | Yes | Yes |
Asynchronous messages sent to workflows. Fire-and-forget semantics.
func OrderWorkflow(ctx workflow.Context, order Order) error {
var cancelRequested bool
// Register signal handler
cancelChan := workflow.GetSignalChannel(ctx, "cancel-order")
// Handle signals in selector
selector := workflow.NewSelector(ctx)
selector.AddReceive(cancelChan, func(c workflow.ReceiveChannel, more bool) {
var signal CancelSignal
c.Receive(ctx, &signal)
cancelRequested = true
})
// Process with signal checking
for !cancelRequested {
selector.Select(ctx)
// Process order steps...
}
if cancelRequested {
return compensate(ctx, order)
}
return nil
}
From SDK:
// Get client
c, _ := client.Dial(client.Options{})
// Send signal
err := c.SignalWorkflow(ctx, workflowID, runID, "cancel-order", CancelSignal{
Reason: "Customer requested",
})
From CLI:
temporal workflow signal \
--workflow-id order-123 \
--name cancel-order \
--input '{"reason": "Customer requested"}'
Start workflow or signal if already running:
workflowOptions := client.StartWorkflowOptions{
ID: "order-123",
TaskQueue: "orders",
}
we, err := c.SignalWithStartWorkflow(ctx,
workflowID,
"add-item", // signal name
ItemSignal{ID: "456"}, // signal data
workflowOptions,
OrderWorkflow,
initialOrder,
)
Buffered signals with processing:
func ProcessingWorkflow(ctx workflow.Context) error {
var items []Item
itemChan := workflow.GetSignalChannel(ctx, "add-item")
doneChan := workflow.GetSignalChannel(ctx, "done")
done := false
for !done {
selector := workflow.NewSelector(ctx)
selector.AddReceive(itemChan, func(c workflow.ReceiveChannel, more bool) {
var item Item
c.Receive(ctx, &item)
items = append(items, item)
})
selector.AddReceive(doneChan, func(c workflow.ReceiveChannel, more bool) {
c.Receive(ctx, nil)
done = true
})
selector.Select(ctx)
}
return processItems(ctx, items)
}
Synchronous read-only inspection of workflow state.
func OrderWorkflow(ctx workflow.Context, order Order) error {
var status string = "pending"
var progress int = 0
// Register query handler
err := workflow.SetQueryHandler(ctx, "get-status", func() (OrderStatus, error) {
return OrderStatus{
Status: status,
Progress: progress,
}, nil
})
if err != nil {
return err
}
// Workflow logic updates status
status = "processing"
progress = 50
// ...
status = "completed"
progress = 100
return nil
}
From SDK:
response, err := c.QueryWorkflow(ctx, workflowID, runID, "get-status")
if err != nil {
return err
}
var status OrderStatus
err = response.Get(&status)
From CLI:
temporal workflow query \
--workflow-id order-123 \
--type get-status
| Do | Don't |
|---|---|
| Return computed state | Modify workflow state |
| Keep handlers fast | Perform I/O operations |
| Return serializable data | Return channels/contexts |
| Handle missing state | Panic on edge cases |
Synchronous request-response mutations (Temporal 1.21+).
func OrderWorkflow(ctx workflow.Context, order Order) error {
var items []Item
// Register update handler with validator
err := workflow.SetUpdateHandlerWithOptions(ctx, "add-item",
func(ctx workflow.Context, item Item) (AddItemResult, error) {
items = append(items, item)
return AddItemResult{
ItemCount: len(items),
Total: calculateTotal(items),
}, nil
},
workflow.UpdateHandlerOptions{
Validator: func(ctx workflow.Context, item Item) error {
if item.Quantity <= 0 {
return errors.New("quantity must be positive")
}
return nil
},
},
)
if err != nil {
return err
}
// Wait for completion signal
workflow.GetSignalChannel(ctx, "complete").Receive(ctx, nil)
return processOrder(ctx, items)
}
From SDK:
handle, err := c.UpdateWorkflow(ctx, client.UpdateWorkflowOptions{
WorkflowID: workflowID,
UpdateName: "add-item",
Args: []interface{}{item},
WaitForStage: client.WorkflowUpdateStageCompleted,
})
if err != nil {
return err
}
var result AddItemResult
err = handle.Get(ctx, &result)
From CLI:
temporal workflow update \
--workflow-id order-123 \
--name add-item \
--input '{"id": "item-456", "quantity": 2}'
| Use Update When | Use Signal When |
|---|---|
| Need confirmation | Fire-and-forget OK |
| Need return value | No response needed |
| Validation required | Simple notification |
| Sync semantics needed | Async acceptable |
func ApprovalWorkflow(ctx workflow.Context, request Request) error {
var approved bool
var decision Decision
approveChan := workflow.GetSignalChannel(ctx, "approve")
rejectChan := workflow.GetSignalChannel(ctx, "reject")
// Set query for status
workflow.SetQueryHandler(ctx, "status", func() string {
if approved {
return "approved"
}
return "pending"
})
// Wait for decision
selector := workflow.NewSelector(ctx)
selector.AddReceive(approveChan, func(c workflow.ReceiveChannel, _ bool) {
c.Receive(ctx, &decision)
approved = true
})
selector.AddReceive(rejectChan, func(c workflow.ReceiveChannel, _ bool) {
c.Receive(ctx, &decision)
approved = false
})
selector.Select(ctx)
if approved {
return executeRequest(ctx, request)
}
return workflow.NewApplicationError("Request rejected", "REJECTED")
}
func LongRunningWorkflow(ctx workflow.Context) error {
var results []Result
// Query handler returns current results
workflow.SetQueryHandler(ctx, "get-results", func() []Result {
return results
})
// Process items, updating results
for i := 0; i < 100; i++ {
result := processItem(ctx, i)
results = append(results, result)
}
return nil
}
Signals are fire-and-forget. Handle errors in workflow:
selector.AddReceive(signalChan, func(c workflow.ReceiveChannel, more bool) {
var signal MySignal
c.Receive(ctx, &signal)
if err := validateSignal(signal); err != nil {
// Log error, don't return - signal is already received
workflow.GetLogger(ctx).Error("Invalid signal", "error", err)
return
}
// Process valid signal
})
Return errors from query handlers:
workflow.SetQueryHandler(ctx, "get-data", func(key string) (Data, error) {
data, ok := dataMap[key]
if !ok {
return Data{}, fmt.Errorf("key not found: %s", key)
}
return data, nil
})
Use validators to reject invalid updates:
workflow.SetUpdateHandlerWithOptions(ctx, "update-quantity",
func(ctx workflow.Context, qty int) error {
// Handler runs after validation passes
quantity = qty
return nil
},
workflow.UpdateHandlerOptions{
Validator: func(ctx workflow.Context, qty int) error {
if qty < 0 {
return errors.New("quantity cannot be negative")
}
if qty > maxQuantity {
return fmt.Errorf("quantity exceeds max: %d", maxQuantity)
}
return nil
},
},
)
For detailed patterns, consult:
references/signal-patterns.md - Advanced signal handlingreferences/update-patterns.md - Update implementation examples