Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5243c4a
Add .NET reference files for temporal-developer skill
donald-pinckney Mar 16, 2026
6f5e7dc
Fix .NET alignment issues from self-review
donald-pinckney Mar 16, 2026
d338213
Fix .NET correctness issues from verification pass
donald-pinckney Mar 16, 2026
d80bd35
Update supported language references to include .NET
donald-pinckney Mar 16, 2026
0114238
Edits to advanced features
donald-pinckney Apr 1, 2026
2ee9c02
edits to determinism protection, and move the .editorconfig section
donald-pinckney Apr 1, 2026
3709a85
missed one
donald-pinckney Apr 1, 2026
b41ecc6
edit determinism.md
donald-pinckney Apr 1, 2026
7ae7afb
edit error-handling.md
donald-pinckney Apr 1, 2026
4f19f25
edit gotchas.md
donald-pinckney Apr 1, 2026
c51d32b
edit patterns.md
donald-pinckney Apr 1, 2026
3aa4ce7
edit versioning.md
donald-pinckney Apr 1, 2026
2a38c83
edit observability.md
donald-pinckney Apr 1, 2026
93388e5
fix metrics
donald-pinckney Apr 1, 2026
54ce199
self-review round 1
donald-pinckney Apr 1, 2026
b4c958e
minor correctness fixed
donald-pinckney Apr 2, 2026
9be5c85
Merge branch 'main' into add-dotnet
donald-pinckney Apr 17, 2026
476f53b
Update references/dotnet/patterns.md
donald-pinckney Apr 17, 2026
58bbf52
address comments, clarify reference to earlier code snippet
donald-pinckney Apr 17, 2026
467fd27
clarify that operations are forbidden IN WORKFLOWS
donald-pinckney Apr 17, 2026
014eb91
cleanup workflow cancellation handling example
donald-pinckney Apr 17, 2026
76aa90a
add task token retrieval comment
donald-pinckney Apr 17, 2026
744cf8d
update .net requirements
donald-pinckney Apr 17, 2026
58f8972
Fix propagation of workflow cancellation
donald-pinckney Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix .NET alignment issues from self-review
- dotnet.md: Reduce Determinism Rules section to brief cross-reference
  (was duplicating determinism.md content)
- patterns.md: Add ParentClosePolicy to Child Workflows example
- gotchas.md: Add missing "Heartbeat Timeout Too Short" subsection
- versioning.md: Add missing Key Concepts, Deployment Strategies,
  Query Filters, PINNED/AUTO_UPGRADE guidance, CLI examples
- advanced-features.md: Add worker-level heading for exception types

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
  • Loading branch information
donald-pinckney and claude committed Apr 1, 2026
commit 6f5e7dceffb562b1e32d36e9826cb4451da3b9ec
2 changes: 2 additions & 0 deletions references/dotnet/advanced-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ Control which exceptions cause workflow failures vs workflow task retries.

**Tip for testing:** Set `WorkflowFailureExceptionTypes` to include `Exception` so any unhandled exception fails the workflow immediately rather than retrying the workflow task forever. This surfaces bugs faster.

### Worker-Level Configuration

```csharp
var worker = new TemporalWorker(
client,
Expand Down
18 changes: 3 additions & 15 deletions references/dotnet/dotnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,21 +125,9 @@ MyTemporalApp/

## Determinism Rules

The .NET SDK has **no sandbox** like Python or TypeScript. Developers must avoid non-deterministic operations manually.

**Do NOT use in workflows:**
- `Task.Run` — use `Workflow.RunTaskAsync`
- `Task.Delay` / `Thread.Sleep` — use `Workflow.DelayAsync`
- `Task.WhenAny` — use `Workflow.WhenAnyAsync`
- `Task.WhenAll` — use `Workflow.WhenAllAsync`
- `ConfigureAwait(false)` — use `ConfigureAwait(true)` or omit
- `DateTime.Now` / `DateTime.UtcNow` — use `Workflow.UtcNow`
- `Random` — use `Workflow.Random`
- `Guid.NewGuid()` — use `Workflow.NewGuid()`
- `System.Threading.Mutex` / `Semaphore` — use `Temporalio.Workflows.Mutex` / `Semaphore`
- Iterating `Dictionary<TKey, TValue>` (unordered) — use `SortedDictionary` or sort first

See `references/dotnet/determinism.md` and `references/dotnet/determinism-protection.md` for detailed rules.
The .NET SDK has **no sandbox** like Python or TypeScript. Developers must avoid non-deterministic operations manually. Many standard .NET `Task` APIs use `TaskScheduler.Default` implicitly, which breaks determinism.

See `references/dotnet/determinism.md` for the full list of forbidden operations, safe alternatives, and best practices. See `references/dotnet/determinism-protection.md` for details on the runtime detection mechanism.

## Common Pitfalls

Expand Down
24 changes: 24 additions & 0 deletions references/dotnet/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,30 @@ public async Task ProcessLargeFileAsync(string path)
}
```

### Heartbeat Timeout Too Short

```csharp
// BAD: Heartbeat timeout shorter than processing time
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.ProcessChunkAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(30),
HeartbeatTimeout = TimeSpan.FromSeconds(10), // Too short!
});

// GOOD: Heartbeat timeout allows for processing variance
await Workflow.ExecuteActivityAsync(
(MyActivities a) => a.ProcessChunkAsync(),
new()
{
StartToCloseTimeout = TimeSpan.FromMinutes(30),
HeartbeatTimeout = TimeSpan.FromMinutes(2),
});
```

Set heartbeat timeout as high as acceptable for your use case — each heartbeat counts as an action.

## Testing

### Not Testing Failures
Expand Down
8 changes: 7 additions & 1 deletion references/dotnet/patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,13 @@ public class ParentWorkflow
{
var result = await Workflow.ExecuteChildWorkflowAsync(
(ProcessOrderWorkflow wf) => wf.RunAsync(order),
new() { Id = $"order-{order.Id}" });
new()
{
Id = $"order-{order.Id}",
// Control what happens to child when parent completes
// Terminate (default), Abandon, RequestCancel
ParentClosePolicy = ParentClosePolicy.Abandon,
});
results.Add(result);
}
return results;
Expand Down
89 changes: 87 additions & 2 deletions references/dotnet/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ public async Task<string> RunAsync(Order order)

After all workflows with the deprecated patch marker have completed, remove the `DeprecatePatch()` call entirely.

### Query Filters for Finding Workflows by Version

Use List Filters to find workflows with specific patch versions:

```bash
# Find running workflows with a specific patch
temporal workflow list --query \
'WorkflowType = "OrderWorkflow" AND ExecutionStatus = "Running" AND TemporalChangeVersion = "add-fraud-check"'

# Find running workflows without any patch (pre-patch versions)
temporal workflow list --query \
'WorkflowType = "OrderWorkflow" AND ExecutionStatus = "Running" AND TemporalChangeVersion IS NULL'
```

## Workflow Type Versioning

For incompatible changes, create a new Workflow Type instead of using patches:
Expand Down Expand Up @@ -122,10 +136,32 @@ var worker = new TemporalWorker(
.AddAllActivities(new PizzaActivities()));
```

Update client code to start new workflows with the new type:

```csharp
// Old workflows continue on PizzaWorkflow
// New workflows use PizzaWorkflowV2
var handle = await client.StartWorkflowAsync(
(PizzaWorkflowV2 wf) => wf.RunAsync(order),
new(id: $"pizza-{order.Id}", taskQueue: "pizza-task-queue"));
```

Check for open executions before removing the old type:

```bash
temporal workflow list --query 'WorkflowType = "PizzaWorkflow" AND ExecutionStatus = "Running"'
```

## Worker Versioning

Worker Versioning manages versions at the deployment level, allowing multiple Worker versions to run simultaneously.

### Key Concepts

**Worker Deployment**: A logical service grouping similar Workers together (e.g., "loan-processor"). All versions of your code live under this umbrella.

**Worker Deployment Version**: A specific snapshot of your code identified by a deployment name and Build ID (e.g., "loan-processor:v1.0" or "loan-processor:abc123").

### Configuring Workers for Versioning

```csharp
Expand All @@ -144,22 +180,71 @@ var worker = new TemporalWorker(
.AddAllActivities(new MyActivities()));
```

**Configuration parameters:**
- `UseWorkerVersioning`: Enables Worker Versioning
- `DeploymentOptions`: Identifies the Worker Deployment Version (deployment name + build ID)
- Build ID: Typically a git commit hash, version number, or timestamp

### PINNED vs AUTO_UPGRADE Behaviors

**PINNED**: Workflows stay locked to their original Worker version.
**PINNED Behavior**

Workflows stay locked to their original Worker version:

```csharp
[Workflow(VersioningBehavior = VersioningBehavior.Pinned)]
public class StableWorkflow { /* ... */ }
```

**AUTO_UPGRADE**: Workflows can move to newer versions. Still needs patching for compatibility.
**When to use PINNED:**
- Short-running workflows (minutes to hours)
- Consistency is critical (e.g., financial transactions)
- You want to eliminate version compatibility complexity
- Building new applications and want simplest development experience

**AUTO_UPGRADE Behavior**

Workflows can move to newer versions:

```csharp
[Workflow(VersioningBehavior = VersioningBehavior.AutoUpgrade)]
public class UpgradableWorkflow { /* ... */ }
```

**When to use AUTO_UPGRADE:**
- Long-running workflows (weeks or months)
- Workflows need to benefit from bug fixes during execution
- Migrating from traditional rolling deployments
- You are already using patching APIs for version transitions

**Important:** AUTO_UPGRADE workflows still need patching to handle version transitions safely since they can move between Worker versions.

### Deployment Strategies

**Blue-Green Deployments**

Maintain two environments and switch traffic between them:
1. Deploy new code to idle environment
2. Run tests and validation
3. Switch traffic to new environment
4. Keep old environment for instant rollback

**Rainbow Deployments**

Multiple versions run simultaneously:
- New workflows use latest version
- Existing workflows complete on their original version
- Add new versions alongside existing ones
- Gradually sunset old versions as workflows complete

### Querying Workflows by Worker Version

```bash
# Find workflows on a specific Worker version
temporal workflow list --query \
'TemporalWorkerDeploymentVersion = "my-service:v1.0.0" AND ExecutionStatus = "Running"'
```

## Best Practices

1. **Check for open executions** before removing old code paths
Expand Down