Skip to content

Add Standalone Activities support to Temporal Nexus Operation Handler#748

Open
Quinn-With-Two-Ns wants to merge 7 commits into
temporalio:mainfrom
Quinn-With-Two-Ns:NEXUS-389
Open

Add Standalone Activities support to Temporal Nexus Operation Handler#748
Quinn-With-Two-Ns wants to merge 7 commits into
temporalio:mainfrom
Quinn-With-Two-Ns:NEXUS-389

Conversation

@Quinn-With-Two-Ns

@Quinn-With-Two-Ns Quinn-With-Two-Ns commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What was changed

Add Standalone Activities support to Temporal Nexus Operation Handler

Why?

Allow calling Standalone Activities as a Nexus Operation

Checklist

  1. Closes

  2. How was this tested:

  1. Any docs updates needed?

Note

Medium Risk
Touches experimental Nexus/standalone-activity paths and start/cancel RPC wiring; behavior changes for workflow outbound links (moved from helper to client) but coverage includes integration tests.

Overview
Adds standalone activity backing for experimental Nexus TemporalOperationHandler, alongside existing workflow-run operations.

Handlers can call ITemporalNexusClient.StartActivityAsync (lambda or by name) and get an async activity-execution operation token (type t=2). NexusActivityStartHelper mirrors workflow start plumbing: default task queue from the operation, Nexus completion callbacks with Nexus-Operation-Token, inbound links, and request ID for idempotent retries. StartActivityOptions gains internal fields wired through StartActivityExecution (on-conflict attach, callbacks, links).

Cancel dispatch now peeks token type via ParseTokenType, then cancels the workflow or standalone activity; CancelActivityExecutionAsync is overridable like workflow cancel.

Shared link/callback logic moves to NexusOperationStartHelper; workflow start docs now say task queue is optional. Outbound Nexus links after start are set in TemporalClient from server resp.Link (workflow falls back to a synthetic WorkflowExecutionStarted link on older servers). Activity proto ↔ Nexus link conversion is added in ProtoLinkExtensions.

Reviewed by Cursor Bugbot for commit 8ad5083. Bugbot is set up for automated code reviews on this repo. Configure here.

@Quinn-With-Two-Ns Quinn-With-Two-Ns requested a review from a team as a code owner June 16, 2026 16:47

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f807fc513

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Temporalio/Nexus/TemporalOperationHandler.cs Outdated
Comment thread src/Temporalio/Nexus/NexusActivityStartHelper.cs
Comment thread src/Temporalio/Client/TemporalClient.Activity.cs

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 8520a4b. Configure here.

EventRef = new() { EventId = 1, EventType = EventType.WorkflowExecutionStarted },
}.ToNexusLink();
NexusOperationExecutionContext.Current.HandlerContext.OutboundLinks.Add(nexusLink);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signal-with-start drops Nexus links

Medium Severity

Outbound Nexus links for child workflow starts are only appended after a plain StartWorkflowExecution call. The SignalWithStartWorkflowExecution path returns without the same logic, so Nexus handlers that start workflows with StartSignal no longer populate OutboundLinks on the operation start response.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8520a4b. Configure here.

@jmaeagle99 jmaeagle99 self-assigned this Jun 22, 2026
options.RequestId = nexusStartContext.RequestId;

// Do the start call
var handle = await client.StartActivityAsync(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Go implementation, we check for timeout presence to raise a handler error and surface the validation error. Is that necessary here as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SDK already does some of this validation, Started a discussion in slack on where we want to do the validation.

Comment thread src/Temporalio/Nexus/TemporalOperationHandler.cs Outdated

@jmaeagle99 jmaeagle99 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly rename requests and I think there is opportunity for unifying the token types or at least refactoring to be more symmetrical and untied from handles (which can all be deferred).

Comment thread src/Temporalio/Nexus/NexusActivityExecutionToken.cs Outdated
Comment thread src/Temporalio/Nexus/NexusOperationStartCommon.cs Outdated
Comment thread src/Temporalio/Nexus/NexusOperationStartCommon.cs Outdated
Comment thread src/Temporalio/Nexus/NexusWorkflowRunHandle.cs Outdated
Comment thread src/Temporalio/Nexus/NexusActivityStartHelper.cs Outdated
Comment thread src/Temporalio/Nexus/NexusOperationStartCommon.cs Outdated
try
{
token = NexusWorkflowRunHandle.ParseToken(context.OperationToken);
tokenType = NexusWorkflowRunHandle.LoadTokenType(context.OperationToken);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the token parsing and related logic could use a refactor such that we use a discriminated union, something like:

[JsonPolymorphic(TypeDiscriminatorPropertyName = "t")]
[JsonDerivedType(typeof(WorkflowExecutionToken), 1)]
[JsonDerivedType(typeof(ActivityExecutionToken), 2)]
internal abstract record NexusOperationToken(string Namespace, int? Version);

Then when parsing the STJ, you'll get the exact token class out rather than having to pick out the t field yourself and then construct each type separately.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be fine with deferring a refactor of this along with https://github.com/temporalio/sdk-dotnet/pull/748/changes/BASE..8520a4bcc34de23a824cf065f2e377f5f615a09b#r3477893944 as a separate change.

{
NexusWorkflowRunHandle.WorkflowRunOperationTokenType =>
CancelWorkflowRunAsync(
case NexusWorkflowRunHandle.WorkflowRunOperationTokenType:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the naming of the this token and helper methods vs the new one for standalone activities, they feel very asymmetrical when they really share a lot in common. For example, I think that the workflow run operation token should be separated from the handle notion and standard on either the "run" or "execution" name for both token types.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be fine with deferring a refactor on this along with https://github.com/temporalio/sdk-dotnet/pull/748/changes/BASE..8520a4bcc34de23a824cf065f2e377f5f615a09b#r3477886756 as a separate change.

Comment thread src/Temporalio/Client/TemporalClient.Workflow.cs
@jmaeagle99

Copy link
Copy Markdown
Contributor

I assume that ITemporalNexusClient is not meant for customers to actually implement but just for us to provide easy wire up of Temporal operations as Nexus operations. If that's the case, then the API breaks are fine. Run dotnet pack -c Debug /p:ApiCompatGenerateSuppressionFile=true from the repo root to update the API compat suppression file.

/// <returns>Nexus link.</returns>
public static NexusLink ToNexusLink(this Api.Common.V1.Link.Types.Activity act)
{
var builder = new UriBuilder

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants