Repository navigation
Description
Delivered in Native 3.3.0 and PHP SDK 3.11.0
General reconciled the original acceptance against maintained backend controls and the exact published producer/consumer tuple. Acceptance, publication and scope limits. No benchmark speedup or installed SQL-count measurement is claimed. Earlier NONPASSs remain attributed.
Original requirement and historical evidence
External performance report, topic 9a. The reported timings and proposed fixes have not been independently reproduced. The supplied patches are reference drafts for review and current-source qualification.
Problem
A workflow task completion that schedules an activity is followed by the
same worker's activity poll. That's a second locked transaction, just to
claim the activity the completion created. Temporal calls avoiding it
eager activity dispatch.
Proposed contract
POST /api/worker/workflow-tasks/{task_id}/completeaccepts an optional
activity_pollobject. It holds the worker's next activity poll request,
with the same fields and rules asPOST /api/worker/activity-tasks/poll,
includingpoll_request_id.- The completion's transaction claims the scheduled activity for that
request ID, inside a savepoint, so a failed claim leaves the completion
intact. - The response carries
activity_poll: {"leased": true|false}. - The worker's poll under the same request ID replays the receipt without
the lock. - A poll the rules refuse, or one from a draining or fenced-out worker, is
left to the worker's own poll, which answers the refusal. - Advertised as the
workflow_task_completion_activity_pollserver
capability.
Reported benchmark evidence
Reporter-supplied cumulative measurements, not an isolated-effect or independently verified performance claim: 200 sequentially started workflows, five sequential 20ms activities or five local activities each, six PHP worker processes, mean of three repetitions, one machine, fresh SQLite/MySQL8.0/PostgreSQL16 databases with default durability. "Five" and "local" below mean last-close wall time in seconds; S/M/P means SQLite/MySQL/PostgreSQL. Preserve regressions and noise in the reported results.
| five | local | |
|---|---|---|
| slowest backend | 21.48 → 19.16 | 5.71 → 4.87 |
Acceptance
- With a carried poll, the scheduled activity is leased in the
completion's transaction, and the replayed poll answers that lease. - A refused, draining or fenced poll leaves the completion recorded and
the activity ready. - Old workers that send no
activity_pollbehave as before. worker-protocol-api.openapi.yamldocuments the capability, the
activity_pollrequest field and the response field, as main documents
claimed_task_ready_age. The reference commit doesn't yet.
Implementation constraints
Carry the poll only when an activity slot is available; eager dispatch must not claim work the worker cannot start.
Dependencies
No new producer dependency is required for source investigation.
Related existing work: #325, #1030.
Draft patches and validation boundary
Current main was independently read as b8f4dd13623ce645de77ba5bd89ae8dd8eea2f7c. The supplied draft series targets 3d4d4afaf2b24aaa5443eac154e253b011924738; patches are cumulative reference material, not a current-main application or test result. Review and rebase on maintained source, preserve all existing tests and published compatibility, then establish causal controls and actual conformance before shipping. No patch was applied or executed during intake.
Known draft gap / required control: The original supplied draft was reported not to compile against the ready-age claim shape. The updated report now supplies a cumulative rebase onto b8f4dd1 and reports that every commit builds and the tip passes SQLite/MySQL/PostgreSQL suites, with carried claims using claim_in for ready ages. This is reporter-attributed evidence, not an independently reproduced maintainer result. Preserve ready_origin_at/ready_age_ms and qualify the maintained-source integration. The complete versioned OpenAPI/capability update and the separately recorded same-worker/error-classification controls remain required.
The following sanitized unified diffs identify proposed reference material:
0010-t09-Carry-the-next-activity-poll-on-the-workflow-task-co.patch— 12,120 bytes, SHA-256e9b5a930108a4437694184338d3a93b995f8110c114d66b5b2fc9f6e0c4db8cf.
Current implementation: PR1073 is normally merged as 54af464004d85759e16e939124059449c8052758 / tree 784e5506945c4a6f2072d5005e806e8d8d68f4dd. Its accepted source has62 successful PR checks/one declared skip and48 successful merge-group jobs, with OpenAPI revision63 and the shared carried-poll vectors. The accepted SDK328 source handoff replaces the withdrawn b631 handoff. The automatic main attempts remain NONPASS/runner-blocked after two locked-archive HTTP504 setup failures; this is not a product assertion or published result. No further same-head retry is selected. Next owner/action: Server continues the ordered P0 source train, then owns the coherent published producer and consumer/runner qualification tuple; SDK-PHP owns its accepted consumer source. This issue remains open for published-artifact acceptance. The reported benchmark measurements remain unverified.
Updated reference, November 6
The revised source report changes only its shared introduction; all 24 issue sections are unchanged. The archive adds a Server series rebased onto b8f4dd13623ce645de77ba5bd89ae8dd8eea2f7c. The sanitized rebased topic9a diff is 13,109 bytes / SHA-256 6da0d07ac1d9d50851f98e930978470a4b0e634df81af343c81bda4d2adaaa54. Earlier attached diffs and their review findings remain historical evidence; this rebase alone does not close this issue or establish a build, performance, security or compatibility result.
Activity
rmcdaniel commentedon Nov 6, 2026
Sanitized reference diff
0010-t09-Carry-the-next-activity-poll-on-the-workflow-task-co.patch, part 1/1.This is an unverified, cumulative draft from the external report. Mail author/date/commit-message metadata was removed; the unified-diff hunks were retained. No patch was applied or executed, and no build/test or compatibility result is implied. The known gaps and acceptance criteria in this issue remain mandatory.
Complete sanitized patch: 12,120 bytes, SHA-256
e9b5a930108a4437694184338d3a93b995f8110c114d66b5b2fc9f6e0c4db8cf. This part: 12,120 bytes, SHA-256e9b5a930108a4437694184338d3a93b995f8110c114d66b5b2fc9f6e0c4db8cf. Parts concatenate in order; a part boundary is between complete file diffs.rmcdaniel commentedon Nov 6, 2026
Security static review found two blocking controls missing from the reference implementation.
The carried poll identity is independent of the completed lease.
complete_workflowremovesactivity_poll, while the completion guard later proves the task owner frombody.lease_owner.admit_piggybacked_activity_pollinstead trustspoll.worker_id. The authenticated principal is tenant/role scoped, not bound to a worker ID. A completion owned by worker A can therefore cause the transaction to lease an activity and write a replay receipt for registered worker B, violating the stated same-worker and available-slot contract. Requireactivity_poll.worker_id == completion.lease_ownerbefore admission and revalidate it inside the transaction with the task/fence. Add a cross-worker refusal control proving the completion behavior remains defined while no foreign lease or poll receipt is written.claim_piggybacked_activitycatches everyErrfromclaim_in, rolls back the savepoint, and returnsOk(None).claim_incan fail on backend, decoding, stored-shape, budget, and invariant errors as well as documented poll refusals. Those unexpected faults must not be converted into an apparently successful completion with an omitted poll result. Downgrade only an explicit, closed refusal class; propagate or explicitly surface all unexpected failures. Add fault-injection controls for database and malformed stored-state errors.This is a static finding only. The supplied patch was not applied or executed.
rmcdaniel commentedon Nov 6, 2026
Sanitized rebased reference for report topic 9a:
0010-t09-Carry-the-next-activity-poll-on-the-workflow-task-co.patch.The supplied revision targets Server
b8f4dd13623ce645de77ba5bd89ae8dd8eea2f7c. It is cumulative external reference material, not independently qualified against today's maintained source. Mail headers and commit-message/application instructions were removed; the unified diff is retained. The reporter now claims every rebased commit builds and the tip passes SQLite/MySQL/PostgreSQL tests. Those are attributed claims, not a new maintainer test result. Existing acceptance criteria, security findings and completed implementation receipts remain authoritative. Earlier attached patches are retained as historical references.Sanitized patch: 13,109 bytes, SHA-256
6da0d07ac1d9d50851f98e930978470a4b0e634df81af343c81bda4d2adaaa54. Original-base sanitized identity:e9b5a930108a4437694184338d3a93b995f8110c114d66b5b2fc9f6e0c4db8cf. No patch was applied or executed during this update.rmcdaniel commentedon Nov 6, 2026
Producer checkpoint for the dependent carried-poll SDK work: Server still owns #1060/#1061/#1062/#1067. Current accepted main is
7fbf5124; the active source train is #1072 for #1055/#1056, now5d3eb6a0after its first real-backend corrections. The carried-poll capabilities and versioned reply schemas are not implemented or advertised in that source. There is no new authoritative producer/receipt tuple for a consumer to infer yet.The following accepted constraints remain mandatory for the producer contract, not inferred support:
leased:false.workflow_pollin that case means it was not attempted, not that no workflow task exists. Exact omitted/member behavior for every other variant still needs the versioned producer schema.leasedflag is not that final receipt contract.The next Server-owned contract checkpoint will bind the complete closed request/reply fields, limits, error dispositions and versioned schema to the actual implementation and its producer/consumer qualification. Dependent SDK source can follow those exact bytes when supplied; no duplicate producer implementation, guessed enabled field, native tuple, runtime or publication admission follows from this status record. The existing finite Security findings on these issues remain part of the implementation work.
rmcdaniel commentedon Nov 6, 2026
Producer implementation is now active on draft #1073, initially
e83d6f498f30be611a032192770ecdebf28d1aa1, based on accepted mainc3bbfa5f. #1072 actual-main acceptance and cleanup are complete; this is the sole hosted source train.The first activity-carrier slice binds the poll worker to the completing owner before admission and again under the retained fence, checks the opposite execution slot, uses the existing savepoint/claim path, and distinguishes a closed soft-refusal list from visible database/decoder/budget/invariant faults. It is not yet advertised or an accepted consumer contract. Versioned OpenAPI, real backend/HTTP controls and replay vectors are being completed in the user's order #1060 → #1061 → #1062 → #1067 on this draft.
Server owns the next exact contract/schema checkpoint for sdk-php #328–#331. Normal replay must preserve original lease/session/deadline authority; a fresh response server_time does not renew it, and the consumer must charge the original completion request's transit. Missing workflow_poll delivers no workflow lease and must not be interpreted as an empty queue or disabled capability. No published tuple, release, installed behavior or source PASS is inferred from the draft.
rmcdaniel commentedon Nov 6, 2026
Security follow-up on the updated rebased reference
0010-t09-Carry-the-next-activity-poll-on-the-workflow-task-co.patch(13,109 B / SHA-2566da0d07ac1d9d50851f98e930978470a4b0e634df81af343c81bda4d2adaaa54): both blockers in 6692713547 remain.The rebase supplies the maintained ready-age call shape/capability context, but it still admits and fences the carried poll from
poll.worker_idwithout requiring equality to the completing workflow task'slease_owner. A completion owned by worker A can still allocate/replay for registered worker B. It also still catches everyErr(_)fromclaim_in, rolls back the savepoint and returnsOk(None), collapsing unexpected backend/decoding/stored-state/budget/invariant faults into an apparently successful completion.The ready-age compile-context update is useful reference material but does not replace the same-worker in-transaction equality control, closed soft-refusal error class, cross-worker no-write proof or injected-fault propagation controls. Static review only; no patch was applied or executed.
rmcdaniel commentedon Nov 6, 2026
A concrete maintained-source boundary matters for the draining/refencing controls.
Store::workflow_task_guardcallslease_owner_fresh, which requires an active, fresh registration. A steadily draining owner therefore already receivesstale_worker_registrationfor its workflow completion, with or without a carrier. The new same-worker constraint prevents using another active worker's poll to work around that parent guard.The producer contract preserves the parent completion guard. Soft carrier declines leave an otherwise valid completion intact; they cannot authorize a stale, expired, mismatched or draining parent's completion. The controls will separately cover carrier admission declines, a changed retained poll fence, occupied opposite slots and unchanged parent-guard refusal. A draining poll is never a new-lease authority, and no steady-state draining completion PASS is claimed.
This is a source reconciliation on #1073, not permission to widen completion authority through an optional request member. OpenAPI and the SDK handoff will state the distinction explicitly. Database, decoder, budget and invariant faults remain visible errors, not soft declines. No capability contract or runtime tuple is accepted from this finding alone.
rmcdaniel commentedon Nov 7, 2026
Accepted producer checkpoint: PR1073, accepted head
511bd6fd, normally merged54af4640. Its contract is included in the accepted 4fb runtime and now the immutable published native 3.2.0 source/tag3c6f1eda9c6d2ebc0ee5bec8ccf431900052be9e. The earlier reference-draft same-worker/error findings are superseded by that implementation and its actual SQLite/PostgreSQL/MySQL/MariaDB Source controls. They are not remaining implementation holds on the accepted producer.The authoritative OpenAPI schema 63 and closed replay vectors retain protocol 1.20. Schema SHA-256 is
d6c726a7f97417494d9897ae1101ac7300ba6414174041dea875ae01e5052391; vectors SHA-25612ca5db29d96b5e0f03852b698ca1d15bd796c07486a1af85484d4e4dfc45e9b.Capability
workflow_task_completion_activity_polladmits the optionalactivity_polltransport carrier on workflow completion. Its worker must equal the completing lease owner before admission and again under the transaction/retained registration fence. An occupied workflow or validation slot, including unknown expiry, declines eager dispatch without creating an activity lease/attempt/receipt. The valid completion remains recorded on closed soft refusal; an unexpected claim/backend fault propagates and rolls back. Commit uncertainty retains its visible storage error and original completion/poll recovery identities. Duplicate completion does not dispatch again.The actual publisher input is now available: owner terminal PASS, independent Security publication PASS, index
ghcr.io/durable-workflow/server@sha256:6916f6f3bb1ee58d071f10f888068fd321cd1576fb70dc97ce8e9e61da493d62, exact producer/native runner 3c6. This issue stays open for actual published qualification. Server next owns the existing corpus/four-cell Compose receipt packet 325/6705614909. General next binds the actual published SDK package and SDK runner plus the bounded invocation to this producer; SDK-PHP owns its existing consumer/harness execution and result. The shared consumer handoff is SDK293/6705418888. Installed clock/transit/buffer-age, missing-key capacity, lost-answer/original-ID recovery, expiry/refence and selected load acceptance remain open. No tentative/withdrawn head, Source fixture or older tuple supplies that result.rmcdaniel commentedon Nov 9, 2026
General reconciled the carried-poll and workflow-clock delivery against the original acceptance criteria while both repository agents are Off.
The producer is published Native Server 3.3.0, source
2a15179f29801eec9f2a2d4e6a9c5f93233f104e, indexsha256:cb4b11bd6235a551cf51e7dc6342504fbbcef19959618b376fb5648ca00fc388. The consumer is normal published PHP SDK 3.11.0, sourcefbc64ef98c27be467d75a29a29bfb0ee4e80dba7.Feature-specific acceptance is covered by the maintained source controls. The carried-poll runtime tests, HTTP execution tests, workflow replay/history-budget tests and versioned OpenAPI are byte-identical between the qualified integrated source
5f937ec8(run 42448138571) and the 3.3.0 cut. The source lanes execute the registered controls on SQLite, MySQL, MariaDB and PostgreSQL; the accepted #1073 source also has 62 successful checks and its declared skip.Published compatibility is now verified with the unchanged full PHP replay matrix: 42531701411, 13 executed scenarios passed, zero skips, exact Server 3.3.0 and normal Packagist SDK 3.11.0, distinct clients, restarted worker, completed-history replay/query, in-flight signal/restart and nondeterminism refusal. Captured audit. Its runner bootstrap fix has complete ordinary main custody: signed reviewed tree, 71 successful main checks/eight expected skips and topic absence verified. The published product bytes and feature-specific controls are unchanged.
The source controls establish the transaction/receipt/answer invariants. The published matrix establishes producer/consumer compatibility; it is not an installed SQL-count or benchmark experiment. No speedup is claimed. #1073's earlier archive-acquisition NONPASSs and the first published replay's interpreter-bootstrap NONPASS remain attributed.
Acceptance:
qualify_carried_activity_pollschecks one completion transition, receipt replay with no new transition and equality of the embedded answer with its ordinary replay except the clock. Refused/draining/fenced/occupied-slot and fault controls preserve the stated completion boundary; the HTTP vector test covers lost completion acknowledgement and original lease recovery. The capability, optional request and response are versioned in OpenAPI. No-carrier requests retain their ordinary path. Published SDK 3.11.0 contains the accepted #328 consumer.The separate condition-wait durable-workflow/sdk-php#269 final-audit C2/C5 follow-up remains open; it is not a carried-poll or workflow-clock acceptance claim. The original reporter benchmark remains unverified.