Skip to content

Add workflow.SignalWithStartWorkflow via system Nexus#2416

Draft
dplyukhin wants to merge 20 commits into
mainfrom
go-deferred-encoding
Draft

Add workflow.SignalWithStartWorkflow via system Nexus#2416
dplyukhin wants to merge 20 commits into
mainfrom
go-deferred-encoding

Conversation

@dplyukhin

@dplyukhin dplyukhin commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

What

Adds workflow.SignalWithStartWorkflow, backed by the built-in system Nexus
WorkflowService. The bindings are generated by nex-gen from a checked-in WIT and
re-exposed on the workflow package.

How

The nex-gen-generated api.go targets the public workflow/temporal/client
packages, all of which depend on internal. A generated package re-exported by
workflow would form an import cycle (workflow -> generated -> workflow).

internal/cmd/nexussystemgen runs nex-gen and rewrites the output via the Go
AST
to target go.temporal.io/sdk/internal directly (the public packages are
thin aliases of internal, so this is identity-preserving), emitting
internal/nexussystem and the generated workflow/system_nexus_gen.go
forwarder. The resulting graph (workflow -> internal/nexussystem -> internal,
and workflow -> internal) is acyclic.

For the system endpoint, prepareNexusOperationParams encodes the envelope as
proto-binary (via the proto payload converter) rather than through the
workflow data converter — the default converter would produce json/protobuf,
but temporal-api-go's visitor requires binary/protobuf and reads the
messageType metadata to decode it. The envelope is not run through any codec;
api-go's visitor descends into the envelope's inner payloads so codecs / external
storage apply there. NewNexusClient now permits the system endpoint/service
pair despite the reserved __temporal_ prefix.

internal/extstore moves off the vendored internal/temporalapi/sdk/v1 to
go.temporal.io/api/sdk/v1, which now publishes ExternalStorageReference (per
that vendored package's own README migration note); this avoids a duplicate
proto-registration panic against the updated api-go.

Dependencies

⚠️ Temporary local replace

This branch includes replace go.temporal.io/api => ../temporal-api-go so it can
build against the unreleased api-go visitor change. This must be removed and
replaced with a released go.temporal.io/api version before merge.

Test plan

  • go build ./...
  • go test ./internal/... (full internal suite, plus internal/extstore,
    internal/nexussystem)
  • New tests: internal/nexussystem/system_nexus_integration_test.go
    (end-to-end: request -> proto-binary envelope -> api-go visitor descends
    into inner payloads) and internal/system_nexus_client_test.go
    (NewNexusClient system-pair exemption)

Regeneration

NEX_GEN_BIN=<bin> DESCRIPTORS=<set.pb> make -C internal/nexussystem generate

Comment thread internal/nexussystem/Makefile Outdated
@@ -0,0 +1,33 @@
# Regenerates the system Nexus client bindings (api.go, model_overrides.go) and

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think we just want it as part of the root make file.

Comment thread internal/internal_utils.go Outdated
Comment thread internal/workflow.go Outdated
Comment thread internal/cmd/nexussystemgen/main.go Outdated
@dplyukhin dplyukhin force-pushed the go-deferred-encoding branch 2 times, most recently from 07bf3e3 to 7257e80 Compare July 1, 2026 14:20
dplyukhin added 20 commits July 6, 2026 15:42
Generate Go bindings for the system Nexus WorkflowService from a checked-in WIT
and re-expose SignalWithStartWorkflow on the workflow package.

The nex-gen-generated api.go targets the public workflow/temporal/client
packages, which all depend on internal; a generated package re-exported by
workflow would form an import cycle. internal/cmd/nexussystemgen runs nex-gen and
rewrites the output (via the Go AST) to target go.temporal.io/sdk/internal
directly, emitting internal/nexussystem and the generated
workflow/system_nexus_gen.go forwarder. The resulting graph (workflow ->
internal/nexussystem -> internal, and workflow -> internal) is acyclic.

The request is serialized proto-binary into the Nexus operation Input; api-go's
payload visitor descends into the envelope's inner payloads (codecs/external
storage apply to the inner payloads, not the envelope). NewNexusClient now
permits the system endpoint/service pair despite the reserved __temporal_ prefix.

internal/extstore moves off the vendored internal/temporalapi/sdk/v1 to
go.temporal.io/api/sdk/v1, which now publishes ExternalStorageReference; this
avoids a duplicate proto-registration panic against the updated api-go.

NOTE: includes a temporary 'replace go.temporal.io/api => ../temporal-api-go'
pending a release of the api-go payload-visitor change.
The api-go payload visitor decodes system Nexus envelopes from the payload's
"messageType" metadata and requires binary/protobuf encoding. The default data
converter encodes proto messages as json/protobuf, so explicitly encode the
system Nexus envelope with the proto (binary) converter in
prepareNexusOperationParams when the client targets the system endpoint. The
envelope is not run through the workflow data converter or any codec; the inner
payloads are handled by api-go's visitor.
Update Go version to 1.25.4, update dependencies including
go.temporal.io/api to v1.63.1-0.20260630215314-778d41d2e499,
and add nexus-proto-annotations dependency. Also wrap defer
statements to suppress unused error results from Close and
RemoveAll calls.
@dplyukhin dplyukhin force-pushed the go-deferred-encoding branch from b03cbaa to 6a76a7b Compare July 6, 2026 21:12
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