From c4e37fd283adb43d94dea30561d661f8b2cd7054 Mon Sep 17 00:00:00 2001 From: Durable Workflow Date: Tue, 20 Oct 2026 03:58:38 +0000 Subject: [PATCH] Report leased and empty poll statuses like the worker protocol The worker protocol requires poll_status "leased" on a workflow task claim, and published PHP (2.5.14 and 2.8) reports "leased" and "empty" for workflow, activity and query polls alike. Native reported "task" and "timeout". It now reports the same two values. The wake hint task_queue_changed and every refusal status are unchanged. The cross-kind poll contract, which accepted either claim status, now requires "leased". Part of #325. Co-Authored-By: Claude Opus 5.5 --- rust/src/runtime/queries.rs | 4 ++-- rust/src/runtime/store.rs | 4 ++-- rust/tests/execution/cross_kind_poll.rs | 6 +++--- .../conformance/server-parity/cross-kind-poll-contract.mjs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rust/src/runtime/queries.rs b/rust/src/runtime/queries.rs index f91c4e04e..861532c37 100644 --- a/rust/src/runtime/queries.rs +++ b/rust/src/runtime/queries.rs @@ -357,14 +357,14 @@ where loop { let wake = self.wake.notified(); match self.claim_query(&body, config, &proof).await? { - QueryClaim::Task(task) => return Ok(poll_result(Some(task), "task")), + QueryClaim::Task(task) => return Ok(poll_result(Some(task), "leased")), QueryClaim::StaleWorker => { return Ok(poll_result(None, "stale_worker_registration")); } QueryClaim::Empty => {} } if tokio::time::Instant::now() >= deadline { - return Ok(poll_result(None, "timeout")); + return Ok(poll_result(None, "empty")); } if self .poll_queue_changed(&body, "query", &mut cross_kind) diff --git a/rust/src/runtime/store.rs b/rust/src/runtime/store.rs index 6490d0f95..2f25c77c3 100644 --- a/rust/src/runtime/store.rs +++ b/rust/src/runtime/store.rs @@ -939,12 +939,12 @@ where let wake = self.wake.notified(); if let Some(task) = self.claim(&body, kind, protocol_version).await? { return Ok( - json!({"task": task, "poll_status": "task", "server_capabilities": capabilities()}), + json!({"task": task, "poll_status": "leased", "server_capabilities": capabilities()}), ); } if tokio::time::Instant::now() >= deadline { return Ok( - json!({"task": null, "poll_status": "timeout", "server_capabilities": capabilities()}), + json!({"task": null, "poll_status": "empty", "server_capabilities": capabilities()}), ); } if self diff --git a/rust/tests/execution/cross_kind_poll.rs b/rust/tests/execution/cross_kind_poll.rs index 754af04ff..5986b0ce4 100644 --- a/rust/tests/execution/cross_kind_poll.rs +++ b/rust/tests/execution/cross_kind_poll.rs @@ -177,7 +177,7 @@ async fn scoped_hints_require_original_opt_in_and_a_new_ready_identity() { let result = held.await.unwrap(); assert_eq!(result.0, StatusCode::OK, "{scenario}: {}", result.1); assert_eq!( - result.1["poll_status"], "timeout", + result.1["poll_status"], "empty", "{scenario}: {}", result.1 ); @@ -216,7 +216,7 @@ async fn activity_admission_wakes_other_polls_but_same_kind_claim_wins() { json!({"lease_owner":"mixed","activity_attempt_id":activity["activity_attempt_id"],"result":envelope(Payload::Null)})).await.0,StatusCode::OK); let result = held.await.unwrap(); assert_eq!(result.0, StatusCode::OK, "{}", result.1); - assert_eq!(result.1["poll_status"], "task"); + assert_eq!(result.1["poll_status"], "leased"); assert!(result.1["task"].is_object()); runtime.close().await; database.remove().await; @@ -311,7 +311,7 @@ async fn zero_timeout_polls_keep_the_existing_empty_reply() { ) .await; assert_eq!(result.0, StatusCode::OK, "{}", result.1); - assert_eq!(result.1["poll_status"], "timeout"); + assert_eq!(result.1["poll_status"], "empty"); assert_eq!(result.1.get("task"), Some(&Value::Null)); } runtime.close().await; diff --git a/scripts/conformance/server-parity/cross-kind-poll-contract.mjs b/scripts/conformance/server-parity/cross-kind-poll-contract.mjs index 5dceb69a9..adf89f2d3 100644 --- a/scripts/conformance/server-parity/cross-kind-poll-contract.mjs +++ b/scripts/conformance/server-parity/cross-kind-poll-contract.mjs @@ -44,7 +44,7 @@ export function checkCrossKindPollWake(record, profile) { } const sweep = following[sweepIndex]; assert.equal(sweep.http, 200); - assert(['task', 'leased'].includes(sweep.poll_status)); + assert.equal(sweep.poll_status, 'leased', 'a claim reports leased, as the worker protocol requires'); assert.equal(sweep.task_is_null, false); assert.equal(typeof sweep.task_id, 'string'); assert(sweep.task_id.length > 0, 'the ordinary poll separately grants a task');