Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 4 additions & 3 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
name: temporal-developer
description: Develop, debug, and manage Temporal applications across Python, TypeScript, Go, and Java. Use when the user is building workflows, activities, or workers with a Temporal SDK, debugging issues like non-determinism errors, stuck workflows, or activity retries, using Temporal CLI, Temporal Server, or Temporal Cloud, or working with durable execution concepts like signals, queries, heartbeats, versioning, continue-as-new, child workflows, or saga patterns.
description: Develop, debug, and manage Temporal applications across Python, TypeScript, Go, Java and .NET. Use when the user is building workflows, activities, or workers with a Temporal SDK, debugging issues like non-determinism errors, stuck workflows, or activity retries, using Temporal CLI, Temporal Server, or Temporal Cloud, or working with durable execution concepts like signals, queries, heartbeats, versioning, continue-as-new, child workflows, or saga patterns.
version: 0.2.0
---

# Skill: temporal-developer

## Overview

Temporal is a durable execution platform that makes workflows survive failures automatically. This skill provides guidance for building Temporal applications in Python, TypeScript, Go, and Java.
Temporal is a durable execution platform that makes workflows survive failures automatically. This skill provides guidance for building Temporal applications in Python, TypeScript, Go, Java and .NET.

## Core Architecture

Expand Down Expand Up @@ -79,8 +79,9 @@ Once you've downloaded the file, extract the downloaded archive and add the temp
1. First, read the getting started guide for the language you are working in:
- Python -> read `references/python/python.md`
- TypeScript -> read `references/typescript/typescript.md`
- Java -> read `references/java/java.md`
- Go -> read `references/go/go.md`
- Java -> read `references/java/java.md`
- .NET (C#) -> read `references/dotnet/dotnet.md`
2. Second, read appropriate `core` and language-specific references for the task at hand.

## Primary References
Expand Down
1 change: 1 addition & 0 deletions references/core/determinism.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Each Temporal SDK language provides a different level of protection against non-
- TypeScript: The TypeScript SDK runs workflows in an isolated V8 sandbox, intercepting many common sources of non-determinism and replacing them automatically with deterministic variants.
- Java: The Java SDK has no sandbox. Determinism is enforced by developer conventions — the SDK provides `Workflow.*` APIs as safe alternatives (e.g., `Workflow.sleep()` instead of `Thread.sleep()`), and non-determinism is only detected at replay time via `NonDeterministicException`. A static analysis tool (`temporal-workflowcheck`, beta) can catch violations at build time. Cooperative threading under a global lock eliminates the need for synchronization.
- Go: The Go SDK has no runtime sandbox. Therefore, non-determinism bugs will never be immediately appararent, and are usually only observable during replay. The optional `workflowcheck` static analysis tool can be used to check for many sources of non-determinism at compile time.
- .NET: The .NET SDK has no sandbox. It uses a custom TaskScheduler and a runtime EventListener to detect invalid task scheduling. Developers must use Workflow.* safe alternatives (e.g., Workflow.DelayAsync instead of Task.Delay) and avoid non-deterministic .NET Task APIs.

Regardless of which SDK you are using, it is your responsibility to ensure that workflow code does not contain sources of non-determinism. Use SDK-specific tools as well as replay tests for doing so.

Expand Down
203 changes: 203 additions & 0 deletions references/dotnet/advanced-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# .NET SDK Advanced Features

## Schedules

Create recurring workflow executions.

```csharp
using Temporalio.Client.Schedules;

var scheduleId = "daily-report";
await client.CreateScheduleAsync(
scheduleId,
new Schedule(
Action: ScheduleActionStartWorkflow.Create(
(DailyReportWorkflow wf) => wf.RunAsync(),
new(id: "daily-report", taskQueue: "reports")),
Spec: new ScheduleSpec
{
Intervals = new List<ScheduleIntervalSpec>
{
new(Every: TimeSpan.FromDays(1)),
},
}));

// Manage schedules
var handle = client.GetScheduleHandle(scheduleId);
await handle.PauseAsync("Maintenance window");
await handle.UnpauseAsync();
await handle.TriggerAsync(); // Run immediately
await handle.DeleteAsync();
```

## Async Activity Completion

For activities that complete asynchronously (e.g., human tasks, external callbacks).
If you configure a `HeartbeatTimeout` on this activity, the external completer is responsible for sending heartbeats via the async handle.
If you do NOT set a `HeartbeatTimeout`, no heartbeats are required.

**Note:** If the external system that completes the asynchronous action can reliably be trusted to do the task and Signal back with the result, and it doesn't need to Heartbeat or receive Cancellation, then consider using **signals** instead.

```csharp
using Temporalio.Activities;
using Temporalio.Client;

[Activity]
public async Task RequestApprovalAsync(string requestId)
{
var taskToken = ActivityExecutionContext.Current.Info.TaskToken;

// Store task token for later completion (e.g., in database)
await StoreTaskTokenAsync(requestId, taskToken);

// Mark this activity as waiting for external completion
throw new CompleteAsyncException();
}

// Later, complete the activity from another process
public async Task CompleteApprovalAsync(string requestId, bool approved)
{
var client = await TemporalClient.ConnectAsync(new("localhost:7233"));
// Retrieve the task token from external storage (e.g., database)
var taskToken = await GetTaskTokenAsync(requestId);
Comment thread
donald-pinckney marked this conversation as resolved.

var handle = client.GetAsyncActivityHandle(taskToken);

// Optional: if a HeartbeatTimeout was set, you can periodically:
// await handle.HeartbeatAsync(progressDetails);

if (approved)
await handle.CompleteAsync("approved");
else
// You can also fail or report cancellation via the handle
await handle.FailAsync(new ApplicationFailureException("Rejected"));
}
```

## Worker Tuning

Configure worker performance settings.

```csharp
var worker = new TemporalWorker(
client,
new TemporalWorkerOptions("my-task-queue")
{
// Workflow task concurrency
MaxConcurrentWorkflowTasks = 100,
// Activity task concurrency
MaxConcurrentActivities = 100,
// Graceful shutdown timeout
GracefulShutdownTimeout = TimeSpan.FromSeconds(30),
}
.AddWorkflow<MyWorkflow>()
.AddAllActivities(new MyActivities()));
```

## Workflow Init Attribute

Use `[WorkflowInit]` on a constructor to run initialization code when a workflow is first created.

**Purpose:** Execute some setup code before signal/update happens or run is invoked.

```csharp
[Workflow]
public class MyWorkflow
{
private readonly string _initialValue;
private readonly List<string> _items = new();

[WorkflowInit]
public MyWorkflow(string initialValue)
{
_initialValue = initialValue;
}

[WorkflowRun]
public async Task<string> RunAsync(string initialValue)
{
// _initialValue and _items are already initialized
return _initialValue;
}
}
```

Constructor and `[WorkflowRun]` method must have the same parameters with the same types. You cannot make blocking calls (activities, sleeps, etc.) from the constructor.

## Workflow Failure Exception Types

Control which exceptions cause workflow failures vs workflow task retries.

**Default behavior:** Only `ApplicationFailureException` fails a workflow. All other exceptions retry the workflow task forever (treated as bugs to fix with a code deployment).
Comment thread
donald-pinckney marked this conversation as resolved.

**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,
new TemporalWorkerOptions("my-task-queue")
{
// These exception types will fail the workflow execution (not just the task)
WorkflowFailureExceptionTypes = new[] { typeof(ArgumentException), typeof(InvalidOperationException) },
}
.AddWorkflow<MyWorkflow>()
.AddAllActivities(new MyActivities()));
```

## Dependency Injection

The .NET SDK supports dependency injection via the `Temporalio.Extensions.Hosting` package, which integrates with .NET's generic host.

### Worker as Generic Host

```csharp
using Temporalio.Extensions.Hosting;

public class Program
{
public static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(ctx =>
ctx.
AddScoped<IOrderRepository, OrderRepository>().
AddHostedTemporalWorker(
clientTargetHost: "localhost:7233",
clientNamespace: "default",
taskQueue: "my-task-queue").
AddScopedActivities<MyActivities>().
AddWorkflow<MyWorkflow>())
.Build();
await host.RunAsync();
}
}
```

### Activity Dependency Injection

As shown in the host setup above, activities can be registered with `AddScopedActivities<T>()`, `AddSingletonActivities<T>()`, or `AddTransientActivities<T>()`. Activities registered this way are created via DI, allowing constructor injection:

```csharp
public class MyActivities
{
private readonly ILogger<MyActivities> _logger;
private readonly IOrderRepository _repository;

public MyActivities(ILogger<MyActivities> logger, IOrderRepository repository)
{
_logger = logger;
_repository = repository;
}

[Activity]
public async Task<Order> GetOrderAsync(string orderId)
{
_logger.LogInformation("Fetching order {OrderId}", orderId);
return await _repository.GetAsync(orderId);
}
}
```

**Note:** Dependency injection is NOT available in workflows — workflows must be self-contained for determinism.
Loading