Skip to content

Nexus: raw ActivityClient starts inherit request ID and links - #2369

Open
tekkaya wants to merge 3 commits into
mainfrom
nexus-saa-gap-fixes
Open

Nexus: raw ActivityClient starts inherit request ID and links#2369
tekkaya wants to merge 3 commits into
mainfrom
nexus-saa-gap-fixes

Conversation

@tekkaya

@tekkaya tekkaya commented Sep 1, 2026

Copy link
Copy Markdown

Summary

TemporalNexusClient.startActivity() guards the one Activity start that can complete a Nexus operation, but a synchronous handler that starts additional Activities must call the raw ActivityClient. Those starts previously omitted the inbound Nexus request ID and links, so a Nexus-task redelivery could start a duplicate Activity instead of resolving to its original run.

Raw Activity starts now inherit the handler’s request ID and inbound links. Completion callbacks remain limited to the guarded start, since only that start can complete the operation.

Aligns with the sdk-go PR #2633 and sdk-java PR #3048.

Changes

  • Add an internal ambient Nexus Activity-start context for the duration of a startOperation handler invocation.
  • Make raw ActivityClient starts inherit the inbound Nexus request ID and links.
  • Forward server-provided Activity response links to the operation’s outbound links.
  • Leave guarded-start completion-callback behavior unchanged.

Testing

  • Added a redelivery test showing that a raw Activity start runs exactly once after a retryable handler failure.
  • Added link-propagation coverage for standalone Nexus and Workflow callers.
  • Existing multiple-start and retry coverage continues to pass.

@tekkaya
tekkaya force-pushed the nexus-saa-gap-fixes branch 4 times, most recently from 4d0af10 to 5eb639f Compare September 1, 2026 04:45
@tekkaya
tekkaya marked this pull request as ready for review September 1, 2026 16:12
@tekkaya
tekkaya requested review from a team as code owners September 1, 2026 16:12
@tekkaya

tekkaya commented Sep 1, 2026

Copy link
Copy Markdown
Author

Note: Cloud Integ Tests seems like failing since PR #2367 is merged

* @internal
* @hidden
*/
export interface NexusActivityStartContext {

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.

This shouldn't be specific to Activity start. This is the context around a StartNexusOperation task and should reflect that rather than being activity specific.

Let's also move it to packages/nexus/context.ts

@tekkaya tekkaya Sep 1, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

StartNexusOperationTaskContext or NexusOperationTaskContext or NexusStartOperationTaskContext any other suggestions for the naming? I am leaning towards NexusStartOperationTaskContext to align with NexusStartOperationInput existing NexusCancelOperationInputtypes

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Let's also move it to /packages/nexus/context.ts

nexus package already depends on client, so moving this into packages/nexus/context.ts and having ActivityClient read it back would create a real circular package dependency. Kept it in client, exported only through internal.ts, for that reason.

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.

Thinking about the name made me realize we actually already have a type with these exact fields that is passed to all handlers: TemporalStartOperationContext. This is the type that should be used I think.

Comment thread packages/client/src/nexus-activity-start-context.ts Outdated
Comment thread packages/client/src/activity-client.ts Outdated
Comment thread packages/client/src/activity-client.ts Outdated

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.

Let's take a look at making these changes via an internal ActivityClientInterceptor that is appended at the end of the interceptor chain (so user interceptors run first). That way we can encapsulate this Nexus context specific changes away from this general client implementation. The interceptor can add or modify the internal options and then hopefully we won't need any changes here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That makes a lot of sense. I'll go with that direction.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Updated ptal @VegetarianOrc

@tekkaya
tekkaya force-pushed the nexus-saa-gap-fixes branch 2 times, most recently from b30b066 to 6e78a9a Compare September 2, 2026 04:37
TemporalNexusClient.startActivity() guards the one Activity start that
can complete a Nexus operation, but a synchronous handler starting
additional Activities has to call the raw ActivityClient directly.
Those raw starts previously carried none of the Nexus-aware wiring, so
the caller had no link to them and a Nexus-task redelivery would
double-execute the Activity instead of resolving to its original run.

Raw Activity starts now inherit the handler's request ID (redelivery
resolves to the original run) and inbound links (preserving the
caller-to-Activity relationship), via a new ambient
NexusActivityStartContext installed by the worker for the duration of
each startOperation invocation. Completion callbacks remain limited to
the guarded start, since only that start can complete the operation.

Aligns with the approved sdk-go fix (temporalio/sdk-go#2633) and
sdk-java's converged design (temporalio/sdk-java#3048).
- Removed short explanatory comments in ActivityClient flagged as
  unnecessary ("Raw starts inherit...", "Reuse the handler request
  ID...") and the "mirroring the pattern used by..." aside in the
  ambient context module.
- Renamed NexusActivityStartContext -> NexusStartOperationTaskContext
  (and its accessors) since the context is scoped to the Nexus
  startOperation task, not specifically to Activity starts, and isn't
  populated for cancelOperation tasks.
Per review feedback, encapsulate the Nexus-specific behavior away from
the general ActivityClient implementation: a raw Activity start (one
that bypasses TemporalNexusClient.startActivity()'s guarded helper)
now gets its Nexus request ID and inbound links from an internal
ActivityClientInterceptor, appended after any user-provided interceptors
on the Worker's Client. ActivityClient itself is unchanged.

The interceptor reads HandlerContext.nexusStartOperationContext, a new
field on @temporalio/nexus's existing per-task ambient context, set for
the duration of a startOperation task. This reuses the context/AsyncLocalStorage
@temporalio/nexus already has, rather than introducing a second one.
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.

2 participants