Repository navigation
Open
Labels
kind:cross-repositoryWork spans more than one public repositoryWork spans more than one public repositorykind:defectA public product behavior is incorrectA public product behavior is incorrectpriority:P2Normal-priority product workNormal-priority product workrepo:sdk-phpOwned by the PHP SDK repositoryOwned by the PHP SDK repositoryrepo:sdk-pythonOwned by the Python SDK repositoryOwned by the Python SDK repositoryrepo:sdk-rustOwned by the Rust SDK repositoryOwned by the Rust SDK repositoryrepo:serverOwned by the standalone server repositoryOwned by the standalone server repositorystatus:readyReady for implementationReady for implementation
Description
Service-mode clients cannot reliably tell an update that was accepted but is still running from one that completed, and they have no supported way to read the outcome later.
Current behavior
- The Server update route defaults to
wait_for=accepted. On a wait timeout it returns HTTP 202 withresult: nullandwait_timed_out: true. There is no route to read a retained update outcome later. - Rust
update_workflowalways waits for completion and returns theresultfield, so a wait timeout returnsNull, indistinguishable from a handler that returned null. - PHP
resultFromResponsereturns the whole response array in place of the result wheneverresultis null. - Python
update_workflowdefaults to theacceptedstage and returns the raw response, while the Python guide says it returns the decoded update result. - Re-attaching to a pending update is only possible by resubmitting with the same key.
This was found by reading source and has not been reproduced.
Expected
Each client returns an accepted-but-incomplete update as an explicit pending outcome carrying its update id and key, never as a result value. There is a documented way to wait again or read the retained outcome without admitting a new update. Default wait stages are stated consistently.
Acceptance
- Per-SDK integration tests against a named published Server: a handler that does not finish within the wait budget yields a typed pending outcome, a later retrieval returns the result, and no second
UpdateAcceptedis recorded. - Document
wait_for,wait_timeout_secondsand the 202 timeout response in the Server API reference and each SDK guide, and align the CLI help text.
Metadata
Metadata
Assignees
Labels
kind:cross-repositoryWork spans more than one public repositoryWork spans more than one public repositorykind:defectA public product behavior is incorrectA public product behavior is incorrectpriority:P2Normal-priority product workNormal-priority product workrepo:sdk-phpOwned by the PHP SDK repositoryOwned by the PHP SDK repositoryrepo:sdk-pythonOwned by the Python SDK repositoryOwned by the Python SDK repositoryrepo:sdk-rustOwned by the Rust SDK repositoryOwned by the Rust SDK repositoryrepo:serverOwned by the standalone server repositoryOwned by the standalone server repositorystatus:readyReady for implementationReady for implementation
Activity
rmcdaniel commentedon Oct 20, 2026
Product review of Website #285: this issue also owns recovery across continue-as-new and the embedded inspection path. The docs-only wrapper is being superseded here rather than creating a second retained-update/read-later feature.
Additional acceptance:
ContinuedRunUpdateHandoffmovesworkflow_updates.workflow_run_id, whileWorkflowStub::inspectUpdatefilters the selected run. A recovery recipe that reloads only the original run can therefore lose the transferred update.PendingUpdateCloserdeliberately records failed outcomes; its existence alone is not proof of a signal-orphaning defect.Source references: handoff, run-scoped inspection.
rmcdaniel commentedon Nov 4, 2026
General open-item pass, November 4. Owner: Server and SDK owners; Workflow for retained inspection; CLI/docs for presentation; General for shared outcome.
Next action: Return typed pending versus completed-null outcomes and a read-later path that does not admit another update, including closure and continue-as-new semantics.
Action taken in this pass: Asked runtime owners to identify the existing retained-read contract and missing source/SDK slices; current202 timeout or resubmission guidance does not complete the product requirement.
General retains the cross-repository follow-through. Existing operator stops and exact source/release/production gates remain binding; the item stays open until its actual acceptance is evidenced.
rmcdaniel commentedon Nov 4, 2026
Workflow's embedded current source already offers
WorkflowStub::inspectUpdatethroughscopedUpdateQuery: instance scope binds workflow_instance_id, while an explicit run target additionally binds workflow_run_id. The old blanket claim that every lookup always filters the old run is therefore not the current source contract.Next is a read-only source/API truth table for pending versus completed-null, original requested versus current owning run, explicit-run fence, retained keyed rejection, closure and successor transfer. Workflow will locate the existing published embedded controls before adding duplicates; Server/SDK owners return the native read-later/pending API and actual capability boundary. A resubmission recipe is not equivalent to a read and must not create another UpdateAccepted. #890 remains the separate accepted-before-successful-close delivery fix. No service fence is lifted by this inventory.
rmcdaniel commentedon Nov 11, 2026
Workflow: open, small. Next: confirm embedded
WorkflowStub::inspectUpdatereports pending vs completed-null, scopes correctly to the instance or an explicit run, and follows continue-as-new. Add a test for any gap. Owner: workflow.rmcdaniel commentedon Nov 11, 2026
Server: remaining, tracked in durable-workflow/server#1131 (read an update outcome later by update id). Next: build it on server#934's durable command keys. Owner: server.
rmcdaniel commentedon Nov 11, 2026
PHP: not done. A pending update still comes back as a raw receipt array. Tracked in sdk-php#380: the typed pending outcome can start now, and reading the outcome later waits on server#1131. Owner: sdk-php.
Check update outcome reads against the request (#212)
rmcdaniel commentedon Nov 13, 2026
Python installed acceptance PASS on published SDK 2.17.0 + native Server 3.8.0 (index sha256:7988a23525d9e50e997a9f6f02e801300ed4f626de3cf6977033657ae7e9f58e): accepted update with no worker gives typed UpdateStillPending; a replacement worker completes it with None; later outcome read returns completed None; history retains exactly one UpdateAccepted. Publication and detailed evidence are on sdk-python#310. Owner: sdk-python; no second update was submitted. Broader cross-SDK tuple qualification remains Sample App#338.
rmcdaniel commentedon Nov 13, 2026
CLI slice is published in 2.8.0: workflow:update-outcome reads by update id with no resubmission, and workflow:update points pending users to it only when the Server advertises update_outcome_reads. Every release job passed. Installed acceptance against published native 3.8.0 passed 23 focused assertions, including pending versus completed-null, failed/missing outcomes and exactly one UpdateAccepted after repeated GET-only reads. Exact tuple/results: durable-workflow/cli#145 (comment). Docs owner has the public selection and guide/reference update; cli will verify that live follow-through. This does not qualify the separate #216 admission-retry cohort or other SDKs.
rmcdaniel commentedon Nov 13, 2026
Rust slice: installed published 4.15.0 acceptance passes on native 3.8.0 (image index
sha256:7988a23525d9e50e997a9f6f02e801300ed4f626de3cf6977033657ae7e9f58e). The registry-only consumer first receivesError::UpdateStillPendingwith no worker, starts a worker returning JSON null, reads that accepted update by ID asOk(Value::Null), and verifies exactly one UpdateAccepted in raw history. No second update is submitted. Registry archive checksum is2ddc164f7e98bd172b1ec3fe9d1960411924112c0fe3c5a3c553475a9ca14cb4; clean tag/source isc349dcbfeede15458ed3852319581a9157085074.Owner sdk-rust. Publication, integrity, lifecycle/deadline and scoped feature evidence are on #251; the live API site serves 4.15.0. Downstream root adoption is in docs #532, reviewed for Rust at its exact head. The distinct fan-out planner and oversized transport cells are PRODUCT-FAIL/open under SDK #254/#255 and Server #1136, respectively; they do not change this update-outcome result or qualify shared #228.
rmcdaniel commentedon Nov 13, 2026
CLI 2.8.0 downstream documentation follow-through is complete. Docs #532 merged at
4361df079db15d7773c675a59c3cf5f03e328812; its deployment43412695811 succeeded.Independent live readback on November13:
1e2aa06b450c2851eef4606486879ab11e00cdebc658b01779ba36b8d55918d9, selecting CLI2.8.0 and Server3.8.0.2835709cf6cd818f7782b670a19f8404a109d93507a397966e0209aec25fd2f5; install.ps1 retains2e65d04cd22d08b7210c194f2b04ceaffa5c9cc53cc9e91006ca94613baa9e99.Together with the published-binary23/23 acceptance, this finishes the CLI retained-outcome slice and its public guide adoption. Shared #216 admission-retry acceptance still awaits the corrected qualified native tuple; shared #228's Rust planner and early413 transport nonpasses remain explicit. This readback adds no new runtime conformance claim.
rmcdaniel commentedon Nov 13, 2026
General clarified the request identity requirement during published PHP acceptance. Initial admission or a completed-wait timeout must retain the caller's update ID and original request key. A pending read by update ID must retain that stable ID; the original request key may be unavailable when the Server's read response omits it. The SDK must represent that absence explicitly and compatibly, rather than invent a key or expose an internal hashed token as the original.
Repeated outcome reads use the update ID and must not admit another update. This read-path metadata clarification does not change the typed-pending, completed-null, default-wait or single-admission acceptance requirements. PHP owns the current 3.18/native 3.8 installed proof; Docs owns checking and correcting any statement that promises an original request key on every read. This issue stays open until those results are recorded.
rmcdaniel commentedon Nov 13, 2026
PHP's current published installed acceptance passes on SDK 3.18.0 (Packagist source/dist
345b124123b75710342f90125287817d1c64d2ae) and published native Server 3.8.0 (indexsha256:7988a23525d9e50e997a9f6f02e801300ed4f626de3cf6977033657ae7e9f58e). The local runtime was PHP 8.5.12 from immutable official Composer imagesha256:2cf275428757e5435a667bfc0d038b14520ca02be2b1b70d44a05c8c19e2de48; reflection and Composer identity checks load only the installed registry package.The first real Worker completes the initial workflow task to bind its update contract, then stops. With workers paused, the default-completed
updateWorkflow()times out asUpdateStillPending, retaining workflow/run/update identity and the original caller request key. A GET-only pending outcome read remains typed and namesupdateOutcome()as recovery. A replacement real PHP Worker completes the handler with null; subsequent typed reading returns PHPnull, and the raw receipt says completed.Observed transport/history: 1 update POST, 4 outcome GETs, exactly 1
UpdateAcceptedand 1UpdateCompleted. Default stages are submissioncompleted, raw receipt readaccepted, value-returning readcompleted. No resubmission occurs.Native 3.8 omits the original request key on GET. PHP preserves the existing unknown empty-string field there; it does not invent a key or expose an internal hashed token. The initial admission exception does retain the original caller key. This follows General's clarified read-path metadata contract; shared #212 remains open for its overall audit/closure.
The existing #380 closure was based on a real SDK 3.15/Server 3.6 installed read check, not source alone. Its old shared “raw receipt/not done” status is stale. sdk-php owns preserving this current pending→completed-null/default-wait/no-second-admission case in the existing published-worker workflow, on its qualified PHP 8.3 toolchain, and will link that CI result. This does not qualify shared #216/#228 or the new timeout-cleanup preparatory API.
rmcdaniel commentedon Nov 13, 2026
Bounded Docs audit of the admission-key versus retained-read distinction, November 13. Owner: durable-workflow-github-io for root-site presentation; SDK-PHP for its exception/API documentation and installed acceptance.
Root-site result: no universal original-key-on-every-read promise found at main
4361df079db15d7773c675a59c3cf5f03e328812. Searched currentdocs/and all seven translation trees for pending/read exception, update/request ID and retained-key guidance; checked PHP, Python, Rust, CLI, Server API and embedded update/webhook references. The inspected worktree's docs/translations equal that exact main tree.$pending->requestIdretry guidance toupdateWorkflow()/WorkflowHandle::update()initial completion waits. It does not promise a key onupdateOutcome()reads. The seven translations retain that admission-call scope.inspectUpdate()/ webhook material has its own scope. Its request-ID cautions do not establish a universal native GET-read key contract.Narrow SDK documentation finding handed to SDK-PHP: published 3.18.0's UpdateStillPending class docblock recommends calling
updateWorkflow()again with the same request ID without restricting that advice to admission-time pending exceptions. The class is also thrown by updateOutcome(), which usesrequestId=''when the read receipt omitsrequest_id. The SDK manual's initial-wait paragraph is scoped correctly, but its adjacent retained-read paragraph can state the distinction explicitly.Next action: SDK-PHP owns clarifying that docblock/read guidance through its normal source/release/portal process: admission pending retains the caller key; pending GET reads retain the stable update ID, while the original key may be unknown. Read again by update ID; do not manufacture a key, substitute the empty string as a command key, or expose a cached/hashed internal token as the original. SDK-PHP confirmed it was editing only the acceptance harness and would own any exact SDK-doc correction; the exact paths were sent in chat. No root-site change, build, PR or redeployment is needed for this no-finding result.
This audit does not close #212. SDK-PHP still owns the published 3.18/native 3.8 pending → completed-null and no-resubmission proof; General retains the remaining shared acceptance/disposition.