Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
name: temporal-developer
description: This skill should be used when the user asks to "create a Temporal workflow", "write a Temporal activity", "debug stuck workflow", "fix non-determinism error", "Temporal Python", "Temporal TypeScript", "Temporal Go", "Temporal Golang", "workflow replay", "activity timeout", "signal workflow", "query workflow", "worker not starting", "activity keeps retrying", "Temporal heartbeat", "continue-as-new", "child workflow", "saga pattern", "workflow versioning", "durable execution", "reliable distributed systems", or mentions Temporal SDK development.
description: This skill should be used when the user asks to "create a Temporal workflow", "write a Temporal activity", "debug stuck workflow", "fix non-determinism error", "Temporal Python", "Temporal TypeScript", "Temporal Go", "Temporal Golang", "Temporal .NET", "Temporal C#", "workflow replay", "activity timeout", "signal workflow", "query workflow", "worker not starting", "activity keeps retrying", "Temporal heartbeat", "continue-as-new", "child workflow", "saga pattern", "workflow versioning", "durable execution", "reliable distributed systems", or mentions Temporal SDK development.
version: 0.1.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, and Go.
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 .NET.

## Core Architecture

Expand Down Expand Up @@ -93,6 +93,7 @@ Once you've downloaded the file, extract the downloaded archive and add the temp
- Python -> read `references/python/python.md`
- TypeScript -> read `references/typescript/typescript.md`
- Go -> read `references/go/go.md`
- .NET (C#) -> read `references/dotnet/dotnet.md`
2. Second, read appropriate `core` and language-specific references for the task at hand.


Expand Down
1 change: 1 addition & 0 deletions references/core/determinism.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Each Temporal SDK language provides a protection mechanism to make it easier to
- Python: The Python SDK runs workflows in a sandbox that intercepts and aborts non-deterministic calls early at runtime.
- 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.
- 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
202 changes: 202 additions & 0 deletions references/dotnet/advanced-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# .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"));
var taskToken = await GetTaskTokenAsync(requestId);

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).

**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

Activities registered with `AddScopedActivities<T>()` or `AddSingletonActivities<T>()` 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