fix(durable-functions): route sync single-arg orchestrators to core-native (#321)#323
Merged
Merged
Conversation
…ative (#321) A plain sync, single-argument, non-generator orchestrator (ctx) => value is now treated as core-native and receives the core OrchestrationContext directly, fixing #321. Detection no longer falls back to arity for plain sync functions. BREAKING: a classic v3 orchestrator written as a plain non-generator using context.df.* is no longer supported; use a function* generator (unaffected) or the core-native ctx.* API. Documented in the package README and CHANGELOG. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d86f5f69-4e1a-4c1d-b017-5e290c85cc05
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes durable-functions (v4 compat) orchestrator classification so a plain synchronous, single-argument, non-generator handler (ctx) => value is treated as core-native and receives the core OrchestrationContext (fixing #321), rather than being misrouted to the classic { df, log } context via an arity fallback.
Changes:
- Updated
isClassicOrchestratorto classify only sync generators (function*) as classic; plain sync functions now always pass through as core-native. - Updated unit tests to reflect the classic orchestrator “generator” ground truth and added a regression test for #321 asserting identity passthrough for
(ctx) => .... - Documented the resulting breaking change in the package README and CHANGELOG.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/azure-functions-durable/src/orchestration-context.ts | Removes arity fallback for plain sync functions; only function* is treated as classic. |
| packages/azure-functions-durable/test/unit/orchestration-context.spec.ts | Updates classic logger test to use function* and adds #321 regression test for sync single-arg core-native passthrough. |
| packages/azure-functions-durable/README.md | Documents breaking change for plain non-generator “classic” orchestrators. |
| packages/azure-functions-durable/CHANGELOG.md | Records behavior change and breaking-change guidance for consumers. |
…only (#321) Zero-arg plain sync orchestrators keep their prior classic classification; only the sync single-argument (ctx) => value shape is routed to core-native. Behavior change is now provably limited to the single-arg case. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d86f5f69-4e1a-4c1d-b017-5e290c85cc05
…sync non-generators (#321) A classic v3 orchestrator must be a generator (it has to yield durable tasks), so no plain sync non-generator can be a legitimate classic orchestrator regardless of arity. Route all plain sync non-generators to core-native (return false) rather than special-casing zero-arg. The only real orchestrator shape whose behavior changes remains sync single-arg (ctx) => value; zero-arg is degenerate and behaves identically either way. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d86f5f69-4e1a-4c1d-b017-5e290c85cc05
…test (#321) The classic replay-safe-logger test uses a `function*` that intentionally never yields — it logs and returns immediately to exercise the classic log wiring, and the test asserts completion on the first `.next()`. ESLint `require-yield` flags empty generators, so disable that rule on the line, matching existing repo usage (e.g. parent-orchestration-instance.spec.ts). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d86f5f69-4e1a-4c1d-b017-5e290c85cc05
Restore packages/azure-functions-durable/CHANGELOG.md to its main-branch state. CHANGELOG entries are finalized at release time; the breaking-change note remains documented in the package README's "Migrating from durable-functions v3" section, which is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d86f5f69-4e1a-4c1d-b017-5e290c85cc05
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
packages/azure-functions-durable/src/orchestration-context.ts:224
- After this change, plain sync non-generator handlers are always core-native, so a classic orchestrator must be a
function*generator. However, the exportedClassicOrchestratortype still allows a classic handler to be a plain non-generator function (it includes| unknown), which no longer matches runtime behavior and may mislead TS consumers.
* the wrapper delegates to it via `yield*`.
* - A plain SYNC (non-generator) function is CORE-NATIVE regardless of arity and passes through
* unchanged, receiving the core {@link OrchestrationContext}. This fixes #321 (a native
* `(ctx) => ctx.instanceId` previously mis-routed by arity to the classic context). BREAKING: a
* classic v3 orchestrator written as a plain non-generator using `context.df.*` is no longer
kaibocai
approved these changes
Jul 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #321.
What
In the compat
durable-functions(v4) package, a plain synchronous, single-argument, non-generator orchestrator(ctx) => valueis now classified as core-native: it passes throughwrapOrchestratorunchanged and receives the coreOrchestrationContextdirectly.Previously the classifier fell back to arity for plain sync functions — a lone
contextparameter was treated as classic and handed the{ df, log }context. That mis-routed core-native handlers like(ctx) => ctx.instanceId:ctx.instanceIdwasundefinedand members such asctx.callActivity/ctx.newGuid()threw. That is the bug reported in #321.Change (minimal)
src/orchestration-context.ts—isClassicOrchestrator: the plain-sync branch now returnsfalse(core-native) instead ofreturn handler.length <= 1. Only a sync generator (function*) is classic. Updated thewrapOrchestratordoc comment to match.test/unit/orchestration-context.spec.ts— the "replay-safe logger on the classic context" test used a plain non-generator classic orchestrator; converted it to the standardfunction*form (same intent). Added a [durable-functions v4] isClassicOrchestrator can't distinguish a plain sync single-arg core-native orchestrator from a classic one #321 regression test asserting the sync single-arg native handler passes through unchanged (identity).README.md/CHANGELOG.md— documented the breaking change.Behavior matrix
function*(ctx)classic (usescontext.df.*)async function*(ctx)async (ctx) => …(ctx, input) => …(arity 2)(ctx) => …sync single-argA classic v3 orchestrator written as a plain non-generator function using
context.df.*in this exact shape (sync, single-arg, non-generator) no longer receives the classic context. Convert it to the standard classic generator form (function*, unaffected) or to the core-nativectx.*API. Standardfunction*classic orchestrators — the normal v3 shape — are unaffected. Documented under "Migrating from durable-functions v3" in the package README and in the CHANGELOG.Validation
npm run build:core→ exit 0npm run build -w durable-functions→ exit 0