This repository provides the Temporal .NET SDK for authoring Workflows, Activities, and Nexus Operations. Use this document as your quick reference when submitting pull requests.
- Run tests with
dotnet test. You do not need to rundotnet buildseparately first —dotnet testbuilds the projects it needs. To run a single test, usedotnet test --filter "FullyQualifiedName=Temporalio.Tests.Client.TemporalClientTests.ConnectAsync_Connection_Succeeds". To see logs, add--logger "console;verbosity=detailed". - Many tests are integration tests that run against a Temporal Server. They use a real Temporal
Server or the time-skipping test server that is lazily downloaded and run as a sub-process on first
use (see
Temporalio.Testing.WorkflowEnvironment). No separate server setup is required for most tests. - Declaration lists in config files are kept alphabetized, case-insensitively and ignoring any
quoting. This covers
Directory.Packages.props, eachItemGroupinDirectory.Build.props,.config/dotnet-tools.json, the[tasks.*]blocks inmise.toml, and the Dependabotignorelist. Insert new entries in order rather than appending them. - The build treats warnings as errors (
TreatWarningsAsErrors) and enables the full analyzer set (AnalysisMode=AllEnabledByDefault) plus StyleCop. A build that produces analyzer warnings will fail. Fix the underlying issue rather than suppressing it, unless a suppression is already the established pattern in.editorconfig. - It is EXTREMELY IMPORTANT that any added comments should explain why something needs to be done, rather than what it is. Comments that simply state a fact easily understood from type signatures or other context should NEVER be added. Always prefer to avoid a comment unless it truly is clarifying something nonobvious.
- Always make every attempt to avoid explicit sleeps in test code. Instead rely on synchronization
techniques (
WaitConditionAsync,TaskCompletionSource, channels, etc.) or the time-skipping test environment. - The SDK multi-targets several frameworks (.NET Framework >= 4.6.2, .NET Core >= 3.1, .NET Standard
= 2.0). Be mindful that newer BCL APIs may not exist on older targets; the
.editorconfigdisables several analyzers for this reason. Code must compile on all targets. - Do not interrupt builds or tests unless they are taking more than 5 minutes. The first test run downloads the test server, and some Workflow/integration tests are slow; it is possible to introduce test hangs.
- Do not extract functions for relatively simple helpers that are only used in one location.
Building requires a recent .NET 10 SDK, Rust (cargo on the PATH), and the Protobuf compiler
(protoc on the PATH), since the native bridge is built from the sdk-core Rust submodule. Clone
the repository recursively so the submodule is present.
The following are enforced for each pull request (see README.md):
dotnet build # build all projects (also builds the Rust bridge DLL)
dotnet format --verify-no-changes # ensure code is formatted (StyleCop + .editorconfig rules)
dotnet test # run unit and integration tests
dotnet pack -c Debug # validate the public API surface (runs in the Pack target)To run the tests as an in-proc program (helpful for debugging native pieces and seeing full stdout/stderr):
dotnet run --project tests/Temporalio.Tests # all tests
dotnet run --project tests/Temporalio.Tests -- --help # see runner optionsAPI documentation is generated with docfx via mise run docs.
The following environment variables override the test environment to run against an external server:
TEMPORAL_TEST_CLIENT_TARGET_HOST– must be set for any of the variables below to applyTEMPORAL_TEST_CLIENT_NAMESPACE– required if the above is setTEMPORAL_TEST_CLIENT_CERT/TEMPORAL_TEST_CLIENT_KEY– optional mTLS cert/key (both or neither)
-
Format and build (with analyzers clean) before submitting.
-
Ensure all tests pass locally.
-
For any user-facing change, add a high-level entry to the
## [Unreleased]section ofCHANGELOG.md, following the guidance at the top of that file. Internal-only changes (refactors, tests, CI, docs) do not need an entry. -
Keep commit messages short and in the imperative mood.
-
Provide a clear PR description outlining what changed and why.
-
Reviewers expect new features or fixes to include corresponding tests when applicable.
-
Public API changes are validated against the previously published package via
EnablePackageValidation, which runs duringdotnet pack. Most additions (new types, or new members on a class) are compatible and need no action, but some additions are breaking — e.g. adding a member to an existing public interface or an abstract member to a class breaks implementers. Any breaking change (a removed or altered surface, or one of these breaking additions) will fail validation; regenerate the baseline suppressions rather than hand-editing them:mise run apicompat:baseline
Then review the diff to
src/Temporalio/CompatibilitySuppressions.xmland keep only the intended entries — this repo setsApiCompatPermitUnnecessarySuppressions=false, so stale suppressions are themselves an error. New public APIs require documentation comments (GenerateDocumentationFileis on).
Reviewers will look for:
- All builds, tests, lints, and formatting passing in CI.
- Note that some tests cause intentional exceptions/panics. That does not mean the test failed. Only consider tests reported as failed by the harness to be a real problem.
- New tests covering behavior changes.
- Clear and concise code following existing style (StyleCop +
.editorconfig). - Documentation comments and
CHANGELOG.mdupdates for any public API changes.
src/– libraries and tooling projects.src/Temporalio/– the main SDK. Notable areas:Client/– clients for communicating with Temporal clustersWorker/– the Worker that runs Workflows, Activities, and Nexus OperationsWorkflows/– Workflow authoring API (attributes,Workflowstatic, determinism support)Activities/– Activity authoring API and execution contextConverters/– data/payload conversionNexus/– Nexus Operation supportRuntime/– runtime, telemetry, and loggingTesting/–WorkflowEnvironmenttest server supportExceptions/– Temporal exception typesApi/– generated protobuf types (do not edit by hand; see "Regenerating protos")Bridge/– C# interop layer over the Rust core;Bridge/sdk-coreis the Rust submodule
src/Temporalio.Api.Generator/– tool that regenerates theTemporalio.Api.*proto typessrc/Temporalio.ApiDoc/– docfx config for API docssrc/Temporalio.Extensions.DiagnosticSource/–System.Diagnostics.Metricssupportsrc/Temporalio.Extensions.Hosting/– dependency injection and generic-host Worker supportsrc/Temporalio.Extensions.OpenTelemetry/– OpenTelemetry tracing support
tests/– test projects.tests/Temporalio.Tests/– the main test suite (unit + integration)tests/Temporalio.SimpleBench/,tests/Temporalio.SmokeTest*/– benchmarks and smoke tests
Directory.Build.props– shared MSBuild propertiesDirectory.Packages.props– central package versions (this repo uses central package management)..config/dotnet-tools.json– pinned .NET codegen tools (ClangSharpPInvokeGenerator, docfx).mise.toml– pinnedprotocandnex-gen, plus thegen/docstasks CI runs..editorconfig– analyzer/StyleCop rule configuration and Temporal-specific overrides.README.md,CONTRIBUTING.md– contributor and development guide.bin/,obj/,target/– compiled output. You never need to look in here.
- The native Rust DLL is built automatically when the project is built.
protocmay need to be on thePATHfor the Rust DLL to build. - Workflow code must be deterministic. The README's "Workflow Logic Constraints" section (including
".NET Task Determinism") explains the rules and the Workflow-specific
.editorconfigoverrides — read it before changing Workflow internals. - Generated code is regenerated with mise tasks, which install their pinned
tools on first run:
mise run genfor everything, ormise run gen:api(Temporalio.Api.*protobuf types),mise run gen:nexus(system Nexus service bindings), andmise run gen:interop(bridge interop layer from the C header) individually. Regenerate and commit the output rather than hand-editing generated files; CI runsmise run genand fails on any diff.gen:interoponly works on Windows since its generator ships Windows-only nativelibclang. src/Temporalio/Bridge/sdk-coreis a git submodule pointing atsdk-rust; change it via the submodule, not by editing files in place.