Repository navigation
feat(durable): add waitForApproval and the approvals client - #315
Merged
Merged
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Retry a 404 while Volcano records a just-started execution, and treat a 409 approval_closed as registered so the callback resolves as expired. Bound all registration attempts by one 30-second deadline. Refuse details that are not JSON, and requests over 64 KiB with room for the longest callback id, before the callback opens. Count name, title, description, and comment lengths in code points, as Volcano does. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # openapi/openapi.yaml # src/durable.ts # src/generated/openapi.d.ts
The retry check read the clock, then each attempt read it again to size its timeout. A millisecond between the two could leave a negative timeout, which AbortSignal.timeout throws on, so a 503 was reported as Volcano being unreachable. Compute the remaining time once after the backoff and give it to the attempt; with none left, surface the failure already seen. Co-authored-by: Cursor <cursoragent@cursor.com>
Match the decision rule the Python SDK follows: status decides, and must be approved or denied; approved derives from it; comment defaults to ''; decidedBy and decidedAt are null when missing or malformed. Any string passed as decidedAt before. It now has to be an RFC 3339 date-time on a real calendar day, the form Volcano writes. Co-authored-by: Cursor <cursoragent@cursor.com>
The approval tests stood a double in for the runtime whose waitForCallback always ran the submitter, so nothing showed that a replay skips registration, that a callback timeout reaches the SDK as CallbackTimeoutError, or that the execution ARN the runtime is given reaches the registration body. Run those three through the runtime's local test runner. Pin @aws/durable-execution-sdk-js 2.3.1, the release Volcano installs into durable functions, and @aws/durable-execution-sdk-js-testing 1.3.0 as dev dependencies. Both declare Node 22; the tests pass on Node 20.20.2, which CI also runs. The package still declares the runtime only as an optional peer, and the packaging test now checks that the example app, which consumes the SDK, does not resolve it. The runtime's declarations do not compile under exactOptionalPropertyTypes, so the runner is loaded untyped behind the repo's opaque stub. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # openapi/openapi.yaml # pnpm-workspace.yaml # src/generated-runtime/generated/client.js # src/generated/client.ts # src/generated/openapi.d.ts
This was referenced Oct 7, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
subnetmarco
commented
Oct 8, 2026
subnetmarco
left a comment
Member
Author
There was a problem hiding this comment.
Reviewed at 314d66f. I found no P1 or P2 issues. Checked against the real runtime (2.3.1), not just the stand-in:
- Registration happens once across replays, including after a decision and after an expiry.
- When every attempt fails, the execution ends
FAILEDafter about 28 s instead of hanging. - Only a real
CallbackTimeoutErrorbecomesexpired, and that stays true on replay.
The 64 KiB check measures UTF-8 bytes of the exact body sent, and the retry list and deadline match Python. Only the durable entry touches process.env or the runtime, and the OpenAPI is byte-identical to Hosting's bundle. pnpm test passed 2540 tests, and the type checks, build, and lint passed too.
Release order: Hosting's and the CLI's E2E fixtures pin ^1.16.0, so this needs to be released before those PRs merge, not after (details on Kong/volcano-hosting#1678).
Co-authored-by: Cursor <cursoragent@cursor.com>
Names are the runtime's operation name, so they hold to printable ASCII and 237 characters. NUL is refused before the callback opens. A refusal keeps its status and code in the message, since the runtime rethrows it with the message alone. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # package.json # src/generated-runtime/generated/model/index.js # src/generated/client.ts # src/generated/model/index.ts # test/fixtures/published-api.d.ts
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.
Tracking
Tracks: https://github.com/Kong/volcano-hosting/pull/1678
Summary
ctx.waitForApproval(name, { title, description?, details?, timeout? })in durable functions. It registers the approval with Volcano inside the callback submitter, so a replay never registers twice, and resolves to{ approved, status, comment, decidedBy, decidedAt }. A callback timeout resolves tostatus: 'expired'.approval_not_readywithin one 30 s deadline, with each attempt bounded by min(10 s, remaining). The request is validated against the 64 KiB body limit before the callback opens.namemust be printable ASCII of at most 237 characters, since it is the runtime's operation name, and NUL is refused anywhere in the text ordetails. A registration refusal keeps its status and code in the message, because the runtime rethrows it with the message alone.volcano.durable.approvals:list,get,stats,approve,deny. A project access token is refused deciding with a permission error.status;comment,decidedBy, anddecidedAtread as empty when malformed, so a recorded decision never fails a replay.decidedAtmust be an RFC 3339 date-time, matching Python.@aws/durable-execution-sdk-js2.3.1 (the release Volcano installs) and its testing package are dev dependencies, so approvals are tested on the real runtime. The runtime stays an optional peer; published dependencies are unchanged.Verification
pnpm check:openapi,pnpm lint,pnpm test:types,pnpm build,pnpm test:package,pnpm check:unused: passpnpm test: 126 suites, 2528 tests passpnpm test:toolingfails one case ("native reporter preserves cancelled case") on Node 24.10 only; it passes on Node 22 and is unrelated to this change.Cross-language impact
Classify this change: public facade / wire contract / shared behavior.
tests/sdk-contract/features/durable-approvals.featurewith JS, Python, and Ruby bindings in https://github.com/Kong/volcano-hosting/pull/1678.docs/durable-functions.mdandREADME.mdhere; Python and Ruby in their PRs; Hosting'sdocs/public/functions/durable-approvals.md.Merge order
404.localmode-e2e. That suite needs a nightly local-mode image with approvals, which exists only once Hosting merges. Hosting's merge queue runs its CLI E2E against CLImain, so the CLI goes first.