Repository navigation
feat(durable): add durable approvals commands - #282
Conversation
`volcano [cloud] durable approvals` lists, reads, counts, approves and denies the approvals a workflow opens with `ctx.waitForApproval`. Approve and deny show the title and confirm unless `--yes`; every command takes `--json`. A 403 for a project access token says a person has to decide. A 409 reads the approval again and says who decided it and when, or when it expired. Repeating the decision an approval already has succeeds and changes nothing, as the API does. The vendored contract gains the approvals paths and schemas from the hosting bundle, and the client is regenerated from it. The cloud and local-mode E2E round trips pin `@volcano.dev/sdk` ^1.16.0, the first release with `waitForApproval`. Co-authored-by: Cursor <cursoragent@cursor.com>
The cloud round trip now mints a project access token and checks it can read a pending approval but not approve it. Co-authored-by: Cursor <cursoragent@cursor.com>
- Map the API's real project-token refusals ("cannot decide durable
approvals" and "project access token is read-only") to the human-only
message on any credential.
- Send stats' window with both ends from one clock reading, so
--since 366d fits the API's limit, and refuse a longer window before
the request.
- Report a pending approval past its deadline as expired instead of
prompting for it or surfacing the API's 409.
- Strip control characters from workflow-supplied text in human output;
--json stays as the API sent it. stripControl moves from internal/docs
to theme.StripControl so both share it.
- Show list's --since without a default in the docs table, and check the
readable list output in the local-mode E2E as the cloud E2E does.
Co-authored-by: Cursor <cursoragent@cursor.com>
…red from Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
approve and deny compared a pending approval's expires_at with the local clock and refused before asking the API, so a machine whose clock runs fast turned away a decision the API would have taken. The decision is now always sent, and the API's 409 approval_expired is answered with the same "expired at" message, using the code the API error now carries. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
subnetmarco
left a comment
There was a problem hiding this comment.
Reviewed at ee48168. The CLI matches Hosting's contract: the spec is identical to Hosting's bundle, and regenerating client.gen.go gives the same bytes. Adding code to the typed error doesn't change Error() or any existing command's output. A prompt with no TTY fails instead of hanging, and list pages like the other list commands. Build, vet, tests, and lint are clean.
localmode-e2e won't turn green when Hosting merges. Today's 404 hides a second failure. Both E2E fixtures pin @volcano.dev/sdk ^1.16.0, which isn't on npm (latest 1.15.1), so durable deploy --all can't install dependencies. check / localmode-e2e is also required by release-please.yml, so overriding it once leaves later PRs and release PRs red. I'd release the SDKs first, then this PR, then Hosting (details on Kong/volcano-hosting#1678).
The other comments are P3s.
Co-authored-by: Cursor <cursoragent@cursor.com>
list --since sends only the start, so the API's clock ends the window; stats leaves its longest window to the API. The refusal also says to unset VOLCANO_TOKEN. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # docs/README.md # internal/apiclient/client.gen.go # openapi/openapi.yaml
Co-authored-by: Cursor <cursoragent@cursor.com>
|
On the review summary: the merge order in the description is now the one you suggested (SDK releases, then this PR, then Hosting, then web and skills). |
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Tracking
Summary
volcano durable approvals list | get | stats | approve | deny, for approvals durable workflows wait onlistdefaults to pending and filters by--function,--execution,--status, and--since;statscounts outcomes, approval rate, and time to decision over--since(default 30d). Both send only the window's start, so the API's clock ends it, and the API enforces the longest windowapproveanddenytake--comment, confirm unless--yes, and print the decision. Repeating the decision already made exits 0; a conflicting decision or an expired approval exits non-zero with what happenedvolcano loginwith VOLCANO_TOKEN unset, or decide in the dashboard"code, so a 409approval_expiredreads as "expired at …"docs/durable-functions.mdVerification
go build ./...,go vet ./...,go test ./...go tool golangci-lint run --new-from-rev=origin/main ./...: 0 issuesmake openapi-generated-checkTestAPIE2ECloudDurableApprovals(tests/e2e/api, against staging) and the local-mode round trip (tests/e2e/localmode). The project-token refusal is also covered from Hosting'stests/e2e/cli.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.