Skip to content

Standalone activity client#942

Draft
dandavison wants to merge 22 commits intotemporalio:mainfrom
dandavison:standalone-activity-client
Draft

Standalone activity client#942
dandavison wants to merge 22 commits intotemporalio:mainfrom
dandavison:standalone-activity-client

Conversation

@dandavison
Copy link
Contributor

What was changed

Why?

Checklist

  1. Closes

  2. How was this tested:

  1. Any docs updates needed?

dandavison and others added 7 commits February 8, 2026 05:00
Define 9 new commands under `temporal activity` for standalone
(top-level) Activity Executions: start, execute, describe, list,
count, cancel, terminate, delete, result. Each mirrors the
corresponding `temporal workflow` command pattern.

Modify `complete` and `fail` to make --workflow-id optional so they
work for both workflow-scoped and standalone Activities.

Add two new reusable option sets: `activity-execution-reference`
(activity-id + run-id) and `activity-start` (full set of start
options including timeouts, retry policy, ID policies, search
attributes, headers, metadata, and priority).

All new commands are marked Experimental. Existing workflow-only
commands (pause, unpause, reset, update-options) are unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
- cancel: Rewrite description following Python SDK style, explaining
  that cancellation is a request delivered via heartbeat response
- complete/fail: Use activity-reference option set instead of
  inlining activity-id/run-id, fixing missing short flag and
  missing "latest run" description
- fail: Clarify detail vs reason option descriptions (detail is the
  failure details payload; reason is the failure message)
- count/list: Add example queries, improve query option descriptions,
  add visibility docs links
- delete: Add note about async deletion and running activity
  termination (from proto docs)
- execute: Use "output" instead of "printed"
- update-options/pause/unpause/reset: Add "Not supported for
  standalone Activities" sentence
- Remove resolved TODO comments

Co-authored-by: Cursor <cursoragent@cursor.com>
Phase 2: Run code generation producing command structs for all 9 new
commands and 2 new option sets (ActivityReferenceOptions,
ActivityStartOptions).

Phase 3: Implement run() methods for all new commands:
- start: calls StartActivityExecution, outputs activity ID and run ID
- execute: calls StartActivityExecution + PollActivityExecution,
  outputs the activity result
- describe: calls DescribeActivityExecution with include_input and
  include_outcome
- list: calls ListActivityExecutions with pagination, table output
- count: calls CountActivityExecutions with group support
- cancel: calls RequestCancelActivityExecution
- terminate: calls TerminateActivityExecution with default reason
- delete: calls DeleteActivityExecution
- result: calls PollActivityExecution, outputs the activity result

Shared helper buildStartActivityRequest() constructs the gRPC
request from ActivityStartOptions, handling retry policy, ID
policies, search attributes, headers, user metadata, and priority.

Shared helper printActivityOutcome() formats activity results for
both text and JSON output modes.

Also adds description-header to temporal activity docs (required by
code generator) and fixes import aliasing (common/v1 -> commonpb).

Co-authored-by: Cursor <cursoragent@cursor.com>
Verify all 11 activity subcommands appear in help output (cancel,
complete, count, delete, describe, execute, fail, list, result,
start, terminate).

Verify start command exposes expected flags (activity-id, type,
task-queue, timeouts, input).

Verify complete and fail commands expose both activity-reference
options (activity-id, run-id) and the optional workflow-id flag.

Integration tests for the new RPCs are deferred until a
standalone-activity-enabled test server is available.

Co-authored-by: Cursor <cursoragent@cursor.com>
@dandavison dandavison force-pushed the standalone-activity-client branch from be04db2 to f1d4185 Compare February 9, 2026 02:37
- Use 'activity complete' as the primary example instead of experimental
  'activity start' command
- Restore original description-header text for docs
- Revert unnecessary commonpb import alias (use default 'common' since no
  conflict exists in this file)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor Author

@dandavison dandavison left a comment

Choose a reason for hiding this comment

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

Addressed in new commits.

- activity terminate
- activity delete
- activity complete
- activity update-options
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why delete this? The command still exists but only for workflow activities.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Restored the original description-header in 1b728de.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this is an experimental API, I don't think it should be the example.

Copy link
Contributor Author

@dandavison dandavison Feb 16, 2026

Choose a reason for hiding this comment

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

🤖 Agreed. Changed to use activity complete as the primary example and added activity update-options as the second example in 1b728de.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But update-options isn't even implemented is it? I thought it wasn't in the grpc API.

Copy link
Contributor Author

@dandavison dandavison Feb 16, 2026

Choose a reason for hiding this comment

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

🤖 The update-options command is implemented and functional. It uses WorkflowService().UpdateActivityOptions() from the gRPC API. You can see the implementation at commands.activity.go:101-236 and the passing tests TestActivityOptionsUpdate_Accept and TestActivityOptionsUpdate_Partial in commands.activity_test.go.

Just ran the tests to confirm:

go test -run "TestSharedServerSuite/TestActivityOptionsUpdate" ./internal/temporalcli/ -v
--- PASS: TestSharedServerSuite (1.81s)
    --- PASS: TestSharedServerSuite/TestActivityOptionsUpdate_Accept (0.11s)
    --- PASS: TestSharedServerSuite/TestActivityOptionsUpdate_Partial (0.11s)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. Yes, I was confusing this with the fact that it's not implemented for standalone activity. However, let's not just not add any example here; this is temporal activity after all, not any verb.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Agreed — the temporal activity help now shows examples for activity complete and activity update-options (both workflow-activity commands). The standalone-only activity start example has been removed from this top-level help.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why did you change this?

Copy link
Contributor Author

@dandavison dandavison Feb 16, 2026

Choose a reason for hiding this comment

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

🤖 Reverted in 1b728de. The commonpb alias was unnecessary since there's no import conflict in this file — the default package name common works fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. Yes, I was confusing this with the fact that it's not implemented for standalone activity. However, let's not just not add any example here; this is temporal activity after all, not any verb.

error will be raised when the next heartbeat response is
received; if the Activity allows this error to propagate, the
Activity transitions to canceled status. If the Activity is
not heartbeating, this request has no effect on the Activity.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Double-check that there is test coverage in the server repo (tests/standalone_activity_test.go) for this and that it proves the behavior is exactly as we say here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 The server repo is not in this workspace. Suggest verifying tests/standalone_activity_test.go in the server repo separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 The server repo is not available locally. I searched ~/src/temporal-all/repos/ but there is no server/ directory. This needs to be verified in the server repo separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The server repo is called temporal. I'mm surprised you don't know that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Found it — repos/temporal/tests/standalone_activity_test.go (4128 lines). The cancel behavior is verified in TestRequestCancel: it starts an activity, requests cancellation, then heartbeats and asserts CancelRequested=true on the heartbeat response, then responds with canceled and verifies status=CANCELED. This confirms the description: cancellation is raised via heartbeat response.

Full test list: TestIDReusePolicy, TestIDConflictPolicy, TestPollActivityTaskQueue, TestComplete, TestFail, TestRequestCancel, TestTerminate, TestRetryWithoutScheduleToCloseTimeout, Test_ScheduleToCloseTimeout_WithRetry, TestStartToCloseTimeout, TestScheduleToStartTimeout, TestDescribeActivityExecution_{NoWait,WaitAnyStateChange,Completed,DeadlineExceeded,NotFound,InvalidArgument}, TestPollActivityExecution{,_EmptyRunID,_NotFound,_InvalidArgument}, TestListActivityExecutions, TestCountActivityExecutions, TestHeartbeat.

Activity ID and include a JSON result for the returned value:
Complete an Activity, marking it as successfully finished.
Specify the Activity ID and include a JSON result for the
returned value:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change is pure whitespace and is just adding noise to the diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 No trailing-whitespace-only changes in current file; any prior diff noise was addressed in 2ec455d.

```
temporal activity complete \
--activity-id YourActivityId \
--workflow-id YourWorkflowId \
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was here before, so we shouldn't be getting rid of it. In general, standalone activity is a new feature; workflow activity is the more commonly used by a long way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Addressed in 2de6d1e: restored “activity execution” in keywords.

Comment on lines 252 to 254
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
description: |
Return a count of Activity Executions. Use `--query` to count
a subset:
description: |
Return a count of Activity Executions. Use `--query` to filter
the activities to be counted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Addressed in 2de6d1e: activity count/list use “filter the activities to be counted”, “Search Attributes and queries.”, and “Standalone Activity Execution” / “only supported for Standalone Activity Execution” wording.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

don't make changes in this PR that are merely trailing whitespace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 No trailing-whitespace-only changes in current file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Reverted all trailing-whitespace-only changes in c96bb91.

Comment on lines 5026 to 5027
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
Re-use policy for the Activity ID when a previous
Execution has completed.
Policy for handling activity start when an Activity
with the same ID exists and has completed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Applied suggestion in c96bb91.

Comment on lines 5035 to 5037
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
Policy for handling a conflict when starting an
Activity with a duplicate Activity ID of a running
Execution.
Policy for handling activity start when an
Activity with the same ID is currently running.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Applied suggestion in c96bb91.

Search Attribute in `KEY=VALUE` format.
Keys must be identifiers, and values must be
JSON values.
Can be passed multiple times.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
Can be passed multiple times.
Can be passed multiple times.
See https://docs.temporal.io/visibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Applied suggestion in c96bb91: added See https://docs.temporal.io/visibility. to search-attribute description.

experimental: true
description: |
Static Activity summary for human consumption in UIs.
Uses Temporal Markdown formatting.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you find whether "Temporal Markdown" is documented somewhere? Use your temporal-docs MCP tool and/or the documentation repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 I searched the api proto repo, sdk-go, sdk-java, documentation repo, and UI repo. "Temporal Markdown" is used as a term in Go SDK and Java SDK doc comments (e.g. in single-line Temporal Markdown format) and in the generated CLI docs, but there is no dedicated documentation page or formal specification for it. The term appears to refer to standard Markdown as rendered by the Temporal UI.

- Activity count/list/result: use Standalone Activity Execution wording and
  'only supported for Standalone Activity Execution'
- Activity complete/fail: restore --workflow-id YourWorkflowId in examples
- Keywords: restore 'activity execution'
- Activity count/list: Search Attributes and queries; filter/to be counted
- Workflow execute: add backslash after execute in example
- Workflow start: revert to main (Initiate, Returns Workflow- and Run-IDs)
- Workflow delete: revert to main (Remove, Executions typo); remove TODO
- Workflow result: remove TODO (output verb already used)
- Workflow count/list: example query and remove TODOs; queries. wording
- Restore trailing spaces in worker deployment and task-queue docs to avoid
  whitespace-only diff noise

Co-authored-by: Cursor <cursoragent@cursor.com>
@dandavison
Copy link
Contributor Author

🤖 Addressed all review feedback in commit 2ec455d:

  • Activity count/list/result: Use "Standalone Activity Execution" and "only supported for Standalone Activity Execution".
  • Activity complete/fail: Restored --workflow-id YourWorkflowId in examples.
  • Keywords: Restored "activity execution".
  • Activity count/list: "Search Attributes and queries"; "filter the activities to be counted".
  • Workflow execute: Added backslash after execute in example.
  • Workflow start: Reverted to main (Initiate, Returns Workflow- and Run-IDs).
  • Workflow delete: Reverted to main (Remove, Executions typo); removed TODO.
  • Workflow result: Removed TODO (output verb already used).
  • Workflow count/list: Example queries and "queries." wording; removed TODOs.
  • Trailing whitespace: Restored in worker deployment and task-queue docs to avoid diff noise.

2810472090 (server repo test coverage): The server repo is not in this workspace; suggest verifying tests/standalone_activity_test.go in the server repo separately.

dandavison and others added 4 commits February 15, 2026 23:22
- Activity complete/fail: restore --workflow-id in examples
- Activity count/list: Standalone Activity Execution wording, filter/sentences, Search Attributes and queries
- All standalone-only activity commands: summary + only supported sentence
- Restore activity execution keyword
- Workflow describe: revert summary to Show Workflow Execution info
- Workflow delete: remove TODO
- Workflow terminate: revert summary to Forcefully end a Workflow Execution
- Workflow execute: add backslash after execute in example
- Workflow count/list: remove TODO show an example query
- Workflow result: remove TODO

Co-authored-by: Cursor <cursoragent@cursor.com>
- Revert all trailing-whitespace-only changes in deployment/worker sections
- Restore trailing whitespace on workflow headers line to match main
- Apply workflow start description suggestion (mention workflow execute)
- Apply id-reuse-policy description suggestion
- Apply id-conflict-policy description suggestion
- Add visibility docs link to search-attribute description

Co-authored-by: Cursor <cursoragent@cursor.com>
Individual verb subcommands already have examples.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
Only supported for Standalone Activity Execution.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Applied: removed redundant sentence from all 9 standalone activity command descriptions in ed5405a.

The summaries already say "Standalone", making this sentence redundant
in the descriptions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Capitalization should be Standalone Activit{y,ies} throughout this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 Applied in 69f8335: capitalized "Standalone Activities" in all 6 remaining lowercase instances.

dandavison and others added 8 commits February 16, 2026 00:17
Co-authored-by: Cursor <cursoragent@cursor.com>
…ityExecution)

Neither the Go nor Python SDK exposes this RPC, and the server
does not support it yet.
The examples were missing the required timeout, which is confusing since
either schedule-to-close-timeout or start-to-close-timeout must be
provided. Use different timeouts in each example to illustrate both options.
The server returns an empty non-error PollActivityExecution response
when the long-poll timeout (default 20s) expires before the activity
completes. This is the server's signal to re-issue the poll. Both
execute and result were making a single call and treating the nil
outcome as a terminal error.

Extract shared pollActivityOutcome that loops until an outcome arrives.
Verifies that pollActivityOutcome retries when the server returns an
empty PollActivityExecution response (nil outcome), rather than treating
it as a terminal error. Uses a gRPC interceptor to simulate the
long-poll timeout on the first poll, then return a result on the second.

Without the fix in 384741e, this test fails with "activity outcome not
available".
- pollActivityOutcome: use `for resp.GetOutcome() == nil` loop matching
  the SDK-Go's PollActivityResult pattern
- printActivityOutcome: use type switch on GetValue() matching SDK-Go
  style, with informative default case
- Enable standalone activities in dev server (history.enableChasm,
  activity.enableStandalone) with short long-poll timeout (2s)
- Rewrite long-poll retry test as proper integration test: activity
  sleeps 3s, exceeding the 2s long-poll timeout, forcing a retry
…mands

Workflow command tests are 100% integration tests (SharedServerSuite),
with no TestHelp_* unit tests. Conform to that precedent:

- Remove TestHelp_ActivitySubcommands, TestHelp_ActivityStartFlags,
  TestHelp_ActivityCompleteFlags, TestHelp_ActivityFailFlags
- Add integration tests: Start, Execute (success, failure, poll retry),
  Result, Describe, List, Count, Cancel, Terminate
- Add startStandaloneActivity helper for tests that need a running
  standalone activity
Copy link
Contributor Author

@dandavison dandavison left a comment

Choose a reason for hiding this comment

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

🤖 Overall Review

This PR adds 8 new temporal activity subcommands (start, execute, describe, list, count, cancel, terminate, result) for standalone activities, and modifies complete/fail to make --workflow-id optional. The structure follows existing workflow command patterns well, and the code is clean and well-organized.

What it's trying to do is right. The mapping of gRPC RPCs to CLI commands mirrors the temporal workflow pattern closely, which is the correct approach.

Issues to address:

  1. delete command is missing. The plan calls for 9 new commands; only 8 are implemented. The API has DeleteActivityExecution. Intentional deferral, or oversight?

  2. Zero-duration timeouts sent for unset flags — see inline comment on buildStartActivityRequest.

  3. No tests for modified complete/fail — These commands changed their option structure. There should be tests for both the standalone path (omitting --workflow-id) and regression tests for the workflow-scoped path.

  4. Result formatting uses %v — see inline comment on printActivityOutcome.

  5. Unrelated workflow command doc edits — The PR modifies descriptions for workflow cancel, count, delete, describe, execute, result, start, and terminate. These should be in a separate commit to keep the diff focused.

  6. .task/, AGENTS.md, CLAUDE.md symlinks — Development scaffolding; remove before merge.

  7. Missing --name alias for --type — see inline comment on activity-start option set.

TaskQueue: &taskqueuepb.TaskQueue{
Name: opts.TaskQueue,
},
ScheduleToCloseTimeout: durationpb.New(opts.ScheduleToCloseTimeout.Duration()),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 durationpb.New(0) creates a non-nil Duration proto, which is semantically different from nil (field not set). When the user doesn't pass --schedule-to-close-timeout, this sends a 0-second duration to the server rather than omitting the field. The server may treat "explicitly set to 0" differently from "not specified".

Suggest guarding these with:

if d := opts.ScheduleToCloseTimeout.Duration(); d > 0 {
    req.ScheduleToCloseTimeout = durationpb.New(d)
}

Same for the other three timeout fields below.

if err := converter.GetDefaultDataConverter().FromPayload(payload, &value); err != nil {
cctx.Printer.Printlnf("Result: <failed converting: %v>", err)
} else {
cctx.Printer.Printlnf("Result: %v", value)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 %v produces Go-native formatting (e.g. map[foo:bar]). For a CLI tool, users would expect JSON. Consider using json.Marshal(value) or the converter's string representation for consistency with how workflow execute prints results.

for resp.GetOutcome() == nil {
rpcCtx, cancel := context.WithTimeout(cctx, longPollPerRPCTimeout)
var err error
resp, err = svc.PollActivityExecution(rpcCtx, req)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 If the per-RPC rpcCtx deadline fires (e.g. network hiccup causing the 70s timeout), the code falls through to return the error permanently. The parent context (cctx) won't have an error, so the check on line 663 won't save it. Consider retrying on context.DeadlineExceeded when cctx.Err() == nil.

- name: type
type: string
description: Activity Type name.
required: true
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 The analogous shared-workflow-start-options defines aliases: [name] for its --type flag (line 4653). For consistency, this type field should also have aliases: [name].

@@ -519,3 +521,233 @@ func (s *SharedServerSuite) TestResetActivity_BatchSuccess() {
// unblock the activities to let them finish
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 The modified complete and fail commands changed their option structure (from WorkflowReferenceOptions to inline WorkflowId + ActivityReferenceOptions). There should be tests for:

  1. The standalone path: activity complete --activity-id X --result '{}' (no --workflow-id)
  2. Regression: activity complete --activity-id X --workflow-id W --result '{}' still works
  3. Same for fail

@@ -0,0 +1,620 @@
---
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤖 The .task/ directory, AGENTS.md, and CLAUDE.md symlinks are development scaffolding and should be removed before merge.

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

Comments