Skip to content

Go SDK: add the Join helper for parallel branches - #1553

Merged
longquanzheng merged 10 commits into
mainfrom
feat/join-helper-go
Nov 11, 2026
Merged

longquanzheng merged 10 commits into
mainfrom
feat/join-helper-go

Conversation

@longquanzheng

Copy link
Copy Markdown
Contributor

Summary

Adds the Join helper to the Go SDK: a join construct for parallel branches, built only on the existing ChannelMap. No server, IDL, or persistent-format change besides the JoinRound JSON value.

  • Join[T] (DefineJoin[T](name), a sealed ChannelDef listed in PersistenceSchema.Channels): Fork / ForkWithRoundKey start the branches and the join Step with one JoinRound; branches report with EndBranch or ReportBranchResult; the join waits with ForAllBranches, ForFirstBranch, or ForBranches; the next round resets with ForRemainingBranches; results are read with BranchResults / RemainingBranchResults.
  • JoinRound{RoundKey, BranchCount}, JSON {"roundKey","branchCount"} pinned by TestJoinRoundJSONContract.
  • The round key defaults to the split Step's StepExecutionID; ForkWithRoundKey takes an explicit key (for RPCs). Forking the same Join twice in one invocation fails; Fork works only in Execute. Invalid counts fail the WaitFor with explicit errors; ForFirstBranch on a zero-branch round fails (shared rule for all five SDKs).
  • Doc comments with a complete, compiling split, branch, join, reset example and the rules: each branch reports exactly once; receivedCount counts only what the join consumed; a cancelling join's round is fully reset only if an RPC reports the cancelled branches; RPC forks use CancelSteps. "Since 1.41.0." Channel and ChannelMap docs point to Join. sdk-go/README.md gains "Joining parallel branches"; docs/design/plan/go-sdk-rewrite.md lists the API.
  • Flow Definition Graph Go analyzer: DefineJoin is a map channel resource; the For* conditions are wait edges, EndBranch / ReportBranchResult publish edges, the result reads read edges, and Fork builders become fan-out edges (×N for builders appended in a loop), with a fixture test.
  • Changelog entries in docs/reference/go/changelog.mdx (1.41) and sdk-go/CHANGELOG.md.

Why

The owner approved the Join helper design (decisions D1-D6, 2026-11-10). Without it, an application implementing a join counts branches and round keys itself, so 14 Workflow Patterns are rated +/-; with a documented construct in all five SDKs they become +. This is the Go part; Java is #1549, Rust #1550, TypeScript #1552, Python follows. Examples migrate after the SDK release because examples/go pins the released SDK.

doc-map: no change needed: SDK API, analyzer, and reference changelog only; docs, skills, and examples change after all five SDKs ship

Testing

  • sdk-go/integ/join_test.go: every scenario in the design's test table, including a gate Step proving results were committed before the join waited, an RPC read of the pending late result after sibling cancellation, and server acceptance of ForN(key, 0); ^TestJoin passed 3 runs in a row; the full run-e2e-tests.sh passes.
  • go test ./dex, make docsCheck, go vet, make copyright-check; cli GOWORK=off go test ./... and -tags=integration ./internal/command -run 'TestVisualize|TestWeb'; npm run generate:flow-definitions shows no drift; cd docs && npm run check.

Join is a typed view over a ChannelMap of branch results. Fork keys each
round by the split Step's execution ID and counts the branch builders;
ForkWithRoundKey serves RPCs. Branches report with EndBranch or
ReportBranchResult; the join waits with ForAllBranches, ForFirstBranch, or
ForBranches; the next round resets with ForRemainingBranches.

Adds the JoinRound JSON contract test and Join integration tests.
DefineJoin becomes a ChannelMap resource. Join wait conditions draw
wait_condition edges with Join labels, EndBranch and ReportBranchResult
draw publish edges, BranchResults and RemainingBranchResults draw read
edges, and EndBranch is a Dead end decision. Fork and ForkWithRoundKey
movements built by builder functions become fan-out transitions, also
when an RPC returns them in NextSteps.
… Join test

The contract test decodes a JoinRound with an unknown extra field. The
canceled-branch test waits for the split before canceling, keeps the
Flow open, and asserts that branch B's later release stays pending and
that B reports no result.
Join.ChannelMap returns the registered same-name ChannelMap, so RPCs can
read or load a round's pending results by its round key. Applications
never publish to it. The integration tests read pending results through
it, and the contract test checks that it is the registered ChannelMap.
The canceled branch marks itself waiting in its WaitFor commit, and the
test polls that marker before canceling. Key isolation and partial
rounds prove the join did not trigger through ordered RPC reads and the
final Flow result instead of a two-second WaitForStepCompletion timeout.
Move the Join change log entry into the 1.43 section next to the
existing Added entry, link it to the Go API reference, and change the
Join version markers from 1.41.0 to 1.43.0.
@codecov

codecov Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.78992% with 30 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
sdk-go/dex/join.go 73.83% 17 Missing and 11 partials ⚠️
sdk-go/dex/invocation.go 83.33% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@longquanzheng
longquanzheng merged commit 250adbd into main Nov 11, 2026
45 checks passed
@longquanzheng
longquanzheng deleted the feat/join-helper-go branch November 11, 2026 01:56
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.

1 participant