Skip to content

Add cooperative cancellation to the PHP SDK - #91

Draft
rmcdaniel wants to merge 86 commits into
mainfrom
feat/cooperative-cancellation
Draft

rmcdaniel wants to merge 86 commits into
mainfrom
feat/cooperative-cancellation

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Cooperative cancellation source candidate

Owning acceptance: durable-workflow/.github#136. Default protocol 1.19 remains published. Candidate protocol 1.20 is unfinished, unfrozen and unpublished.

Current PHP source: 667c11dd4d095d94feed73bd3b9f1cd1416bc973. The Client and receipt implementation are unchanged from qualified source c99564d.

This draft implements cooperative requests, immutable context and lineage, child and activity policies, deterministic cancellation clocks, local callback supervision, and canonical scope authoring/replay. Capability checks refuse operations that a Worker or SDK cannot support. Callback supervision requires Unix CLI with pcntl/posix, bounded transport and callbacks that open fresh connections in their own process. External effects still need application idempotency, cooperation or reconciliation.

Canonical scope preparation and delivery

The internal client proves a single-call boundary with empty frozen member lists from complete original-claim history. It checks the authored scope tree, accepted request, lineage, v5 preparation, captured authority ceiling and committed delivery. Delivery requires a previously verified preparation. Changed preparation identity or deadline is refused even if a new response and history agree with each other.

Both phases, one transient lost-reply reconciliation and every history page share the caller's original monotonic budget. Original task, run, owner, claim epoch, namespace and authored operation range remain bound. A missing nullable operation address is refused explicitly. Pending activity stop, explicit refusal, incomplete history, repeated cursor or exhausted budget aborts without a delivery claim.

Preparation may be inspected before delivery. Ordinary cleanup replay still requires committed delivery. These receipt facts grant no callback authority. The Worker scope execution profile stays disabled and advertises no scope execution capability.

The internal Replayer separately supports an already-committed empty projection and sequential local cleanup in that delivered scope. Cleanup receipts retain all eight canonical identity, membership and budget fields, including the original preparation and captured authority ceiling. Cold replay retains the same boundary and fractional clock. Heartbeats cannot extend the ceiling, and an unaffected parent retains its own state.

Current source verification

PHP 8.4.26 passes 1790 tests / 8485 assertions, with zero errors/failures and 37 opt-in connected skips. All 1788 preceding case identities remain once, with two new connected scope cases. At unchanged receipt source c99564d, affected scope receipt, opening, replay and cleanup checks pass 240 / 636, without skips, including 77 receipt guard/fault cases. Static analysis and dependency boundary checks pass.

All 108 installed package versions and source/dist references match the unchanged original lock. The published Worker corpus consumer and the Native-generated committed-scope fixture are unchanged. Tests cover complete paging, canonical preparation/delivery, lost replies, replacement claims, borrowed proofs, changed deadlines and malformed history.

Current ordinary CI passes at the exact source across the PHP 8.1–8.4/framework matrix, regression corpus, packaging, docs, static analysis and boundary checks. Hosted PHP 8.3 matches local counts and every case identity. Artifact 11323056465 is retained until January 3, 2027. Ordinary CI skipped the connected job.

The first local archive omitted the ignored lockfile and resolved a fresh dependency set. Dependency verification rejected that as baseline qualification. The original lock was restored, both negative controls repeated, and the final full/focused/static/boundary runs passed at its exact references.

Connected scope controls qualification

Source qualification passes at the current head with the same exact Server/Native/Python/Rust/CLI tuple listed below: 36 scenarios / 2002 assertions, zero errors/failures/skips, plus memo restart 1 / 67. All 34 preceding connected cases remain present once. Artifact 11323212087 retains full source provenance, XML, histories and transport receipts until January 3, 2027. Hosted stack/image teardown succeeded.

Both new scope cases pass actual Server preparation/delivery and original-claim paging. Ordinary and lost accepted replies retain one original Native request, preparation and delivery event, context and +30-second deadline. Both read 14 single-event history pages. Neither releases the claim nor creates another task. The repeated mixed-language root cascade passes 237 assertions in 23.05 seconds.

Request admission uses an explicit Native fixture in the disposable testing application, restricted to its synthetic workflow IDs. Server has no public per-scope request API yet. The fixture runs as UID/GID 1000 with bounded process time and a read-only source mount. These passing receipt cases grant no scope execution capability. Live scoped callbacks, cleanup and replacement remain separate gates.

Earlier connected root qualification

Receipt-repair source bdfc5871629de5fe1bda5edd6272347240731679 passes connected qualification: 34 scenarios / 1880 assertions, zero errors/failures/skips, plus memo restart 1 / 67. Artifact 11321860246 is retained until January 3, 2027.

That exact tuple uses Server 92e9f12866be51e86d35b043597af3e705ff8122, Native 6fcba5c64e9c5f673969b04906a35717e4b83421, Python 3c77a01059964cd093f4d7214f1917741818c97a, Rust 08824f2950460ec590e2f16272adc6ddf9a890ed and CLI 933c554f8bffa2bf7d188ced712b69b20844ac2d. The mixed-language root cascade passes 237 assertions in 22.15 seconds. Failed first receipt write and lost accepted reply preserve one original Native history identity, request and deadline, with genuine callback stop. Discovery/reporting share five seconds, capped by the original cleanup deadline.

The original intermittent remote WAIT_CANCELLATION_COMPLETED stall remains unexplained. Passing receipt fault tests and later connected runs do not establish its original cause. The missed Python recovery claim and Server MySQL interruption canary also remain unresolved.

Remaining acceptance

Qualify populated selective projections, groups, descendants, live scoped cleanup and replacement across PHP/Python/Rust before enabling scope execution. Finish operator inspection and fair competitive qualification, freeze the specification, then publish and verify the exact artifacts.

The final demonstration requires a PHP parent, Python child, Rust remote activity and PHP local activity. One root identity and original +30-second deadline must survive duplicate requests, activity stop without application heartbeats, stale publication, cleanup SIGKILL and replacement replay. All runs must reach Cancelled before that deadline, with one API/CLI/Waterline cascade view.

This PR and shared #136 remain open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The PHP SDK request foundation is now in draft PR #91 at 44397843e4d86c0ca840bd1f40dd970c6a1a77a6.

It requires explicit Server capability discovery, validates the workflow/run acknowledgment and preserves the original request ID, deadline and opaque history refresh token. PHP exposes current-run and explicitly selected-run handle operations using its existing handle convention. Default protocol remains 1.19, and the PHP worker does not advertise cooperative cancellation support.

Local qualification on PHP 8.4.26 passes the full 804-case source suite with 4,745 assertions and one existing skip, plus static analysis. The new request tests cover 26 cases and 72 assertions. The normal supported PHP and framework CI is running against the latest draft head.

Fresh development installation identified an independent phpDocumentor dependency advisory. PR #92 fixed it with one advisory-specific resolution exception that remains visible to audits. Its normal CI, documentation, clean runtime dependency audit and unchanged blocking of the older Flysystem advisory were verified before merge.

Next for PHP: consume canonical request/delivery history, replay cancellation at the recorded authored boundary, integrate lease-fenced delivery and history refresh, then qualify shutdown and cold replacement against the same Server candidate. Rust support and exact published artifact qualification remain required before the shared capability is enabled.

@rmcdaniel

rmcdaniel commented Oct 1, 2026 •

Copy link
Copy Markdown
Member Author

PHP request and canonical history foundation verified

Exact source: 037438d0a366f0f3db141ce077703084bd39c643, including merged main's development-install fix. Normal CI and both public boundary checks pass. PHP 8.1 through 8.4, static analysis, regression corpus, API docs, framework compatibility, consumer installation and target qualification all pass.

Local qualification at this source on PHP 8.4.26 passes:

  • 75 focused cooperative request/history cases with 170 assertions.
  • Full source suite: 853 cases, 4,843 assertions, one existing skip, no failures.
  • PHPStan and git diff --check.

The tests exercise capability refusal before mutation, selected-run and namespace binding, scoped control credentials, duplicate requests retaining Server identity/deadline, malformed acknowledgment rejection, immutable canonical request/delivery state, wrong-run and changed-deadline rejection, reordered/duplicate markers, valid and invalid parallel/selection operation ranges, and preservation of earlier committed results, failures and selection winners.

The local runner used an isolated path consumer for the SDK, PHPUnit 11.5.56 and PHPStan 2.2.16, with the repository's PHPUnit configuration and test namespace bootstrap. The ordinary CI uses the normal contributor composer install, composer test and composer analyse commands. No advisory blocking was disabled in that consumer. The documentation-only scoped exception was separately qualified and merged through PR #92, with the full advisory still reported by audits and no runtime advisories.

This remains a draft. The canonical reader is not yet connected to replay, and the PHP worker does not advertise this capability. Next is to inject cancellation at the recorded authored boundary, commit delivery through the live task lease, reload canonical paged history and qualify cleanup shielding, worker lifetime and cold replacement against the connected Server candidate. Rust parity and exact published artifact qualification remain required before shared capability activation. Published protocol defaults remain 1.19.

Cleanup is complete. Both clean task worktrees, all task dependency/build/cache/diagnostic directories, the temporary security negative-check consumer, and the local PHP tool image and its task layers have been removed. No task container or volume remains. No paid infrastructure was used or created.

@rmcdaniel

Copy link
Copy Markdown
Member Author

PHP replay and cleanup shielding implemented

Exact candidate source: c33b4d8a7446e8712e404e26090a191a2ec9d4b1 in draft SDK PR #91.

Canonical delivery now throws WorkflowCancelled at the recorded authored call, with the original request ID. A pending observation returns a delivery intent without throwing into workflow code or executing that call. Replay checks the call kind, parallel span, selection handle operation range, recorded operation identity and shielding. It rejects a removed or changed committed boundary.

WorkflowContext::cancellationShield() supports nested durable cleanup and restores its depth after exceptions. Saga compensation runs inside that shield. Completed results, earlier failures and committed selection winners retain their original behavior. Condition wait reopens retain their physical sequence so delivery and subsequent cleanup replay at the correct calls.

Local qualification on PHP 8.4.26, PHPUnit 11.5.56 and PHPStan 2.2.16:

  • 20 new focused replay cases, 48 assertions, pass.
  • Full ordinary composer test: 873 cases, 4,891 assertions, one existing skip, no failures.
  • Static analysis and git diff --check pass.
  • Regression corpus grows from 26 to 27 replay fixtures. The new timer-delivery fixture fails on main f425185ef26730f4973c8dfab1ec35ce88626a82 and passes on the candidate through the official Worker, including inline and paginated history. Validator reports consumer: worker, base fail and candidate pass.

The selection test starts with the retained runtime-produced selection fixture, preserves the committed winner, removes the pending loser's completion and verifies that cancellation targets its earlier operation range while cleanup follows the delivery slot. The local activity check proves that a pending request does not execute the callback.

This remains source work in a draft. Claim/heartbeat negotiation, fenced delivery transport, canonical history refresh, local activity interruption, shutdown and real connected PHP Server qualification remain required. Default protocol remains 1.19 and the PHP worker does not advertise cooperative support. Rust parity and exact published artifact conformance remain required before the shared capability is enabled.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Latest PHP source foundation verified

Exact source: 34c717f092620ca11b301718db84e59bfcf37dec in draft PHP PR #91. Normal CI and both public boundary checks pass. This includes all supported PHP and framework cells, static analysis, API docs, consumer installation, regression corpus and target qualification.

The delivery client uses worker credentials, the selected namespace, current task/owner/attempt and the complete authored call range. Malformed acknowledgments and changed request, sequence, kind or operation range are rejected. Source qualification may select protocol 1.20 explicitly through the Client constructor. Default protocol remains 1.19, and the managed PHP worker does not advertise cooperative support.

Local PHP 8.4.26 qualification passes 114 focused cooperative cases with 257 assertions and the complete ordinary source suite of 892 cases with 4,930 assertions, one existing skip and no failures. PHPStan and git diff --check pass. The replay corpus's new timer delivery fixture fails on main and passes on this candidate through the official Worker, for inline and paginated history.

Replay/cleanup behavior and the 20 new replay cases are detailed in the preceding report. No SDK release or published capability claim is made from this source work.

Next: integrate immutable claim/heartbeat observations, fenced delivery and canonical paged history refresh into Worker. Qualify in-flight local activities with and without user heartbeats, preserved earlier local reports, cleanup deadlines, shutdown and cold replacement against the same Server candidate. PHP synchronous callbacks require an explicit lifetime solution before the worker can advertise support. Rust parity and exact published artifact conformance remain required.

The clean task worktree, dependency/build/cache data and PHP tool image plus its task layers have been removed. No task container, volume or paid infrastructure was created or retained.

@rmcdaniel

Copy link
Copy Markdown
Member Author

PHP Worker integration and connected qualification

Exact PHP source: a245a4fec26e23ed157960e9ad95b75018d4035e in draft PR #91. Exact Server source: 2e2d6b31df981e98c1a054a8953e9efe95d17e6e, built with published Workflow 2.3.0. Qualification selects protocol 1.20 explicitly. Ordinary PHP workers remain at protocol 1.19 with cooperative cancellation disabled.

Ordinary CI, both public boundary checks and the complete connected MySQL run pass at this head, including target branch qualification. The connected job verifies the exact Server commit, runs real SDK worker processes against its isolated MySQL/Redis stack, and removes its containers, network and local images afterward.

  • Six cooperative cases pass with 142 assertions: a waiting timer, SIGKILL/cold replacement, a request before claim, a discarded successful delivery response, a local callback observed at its user heartbeat, and a late unencodable local result discarded after callback return.
  • Duplicate requests retain the original request ID and deadline. Each case proves one canonical request and authored delivery, one shielded cleanup completion using that original ID, and a cancelled run without an application failure or timer firing. A request before claim schedules no timer.
  • The existing persisted memo/cold-worker restart case also passes, with 67 assertions. Exact Avro bytes and decoded integer/float/binary types survive replacement without a duplicate memo event. JSON object key order is excluded from the envelope comparison while exact members and types remain required.
  • Raw JUnit artifact 11140887753 contains both suites and expires October 8 at 04:10:53 UTC. These measured results and source identities remain retained in this owning record.

Worker now observes immutable task/heartbeat requests, renews the exact lease fence, performs delivery through worker credentials, reloads every canonical history page through the Server-issued opaque token and proves the matching committed delivery before workflow cleanup. Lost or malformed replies cannot manufacture delivery. Earlier local reports commit before later cancellation. Unsafe claims are abandoned without an application failure event.

Real connected history exposed a start-prefix defect: new histories begin with StartAccepted, WorkflowStarted. Canonical refresh and cold sticky-cache recovery now accept and preserve that prefix. Separate focused regressions fail on prior 2dadc40a6d65af71aad0b3ecfa3a0a674192c80b and pass on the candidate. The authored timer-delivery corpus fixture fails on main and passes through the official Worker for inline and paginated history; replay fixtures grow from 26 to 27. Local PHP 8.4.26 checks at 00aa0a2 pass 929 cases, 5,095 assertions and seven opt-in runtime skips, plus PHPStan and 30 focused Worker cases/138 assertions. Current-head CI qualifies the final test-only envelope correction across all supported PHP/framework cells.

This remains a draft and source qualification. The no-user-heartbeat callback case proves rejection after a bounded callback returns, not interruption during an arbitrary blocking callback. Next PHP action is a deliberate execution-lifetime solution and real in-flight checks for cancellation, lease loss, shutdown, grace/deadline expiry and SIGKILL replacement, plus remote activity fencing. Rust parity and exact published Server/SDK conformance remain required before the shared capability is published or enabled by default. Existing terminal cancel/terminate behavior stays intact.

Local task evidence is retained here before removing the clean worktree, dependencies, test/build caches, counterfactual files and tool image. No local Server stack, paid infrastructure or customer state was created for this qualification.

@rmcdaniel

Copy link
Copy Markdown
Member Author

October 1 source baseline at e558391dde4c3dcd4e6a9adeea833e05b536f988

Ordinary CI, both public boundary checks and explicit connected qualification pass at this exact head. The connected job passes six cooperative cases with 143 assertions and the persisted memo/cold-restart case with 67 assertions against Server 2e2d6b31df981e98c1a054a8953e9efe95d17e6e, built with published Workflow 2.3.0. Stack and image teardown succeeds. Raw JUnit artifact 11141691529 is retained through October 8 at 04:40:55 UTC.

This head contains the internal process-execution foundation and bounded worker I/O. Eight real-process cases cover typed IPC, a callback blocked without user heartbeats, rejection before result encoding, original failure metadata, partial writes, inherited destructor isolation and SIGKILL of the owning worker. Four transport cases cover unchanged caller configuration, rejecting an adapter that cannot bound requests, opaque PSR-18 compatibility and a real stalled HTTP body. Local PHP 8.4.26 passes 941 cases with 5,175 assertions and seven existing runtime skips. Twelve focused cases pass with 80 assertions. Static analysis passes.

The executor is not connected to Worker at this baseline. The connected local case observes a heartbeat or rejects the result after callback return. The next implementation connects local execution to real lease observations, original cleanup deadlines, user heartbeat accounting and shutdown, then reruns connected qualification using a callback that stays blocked for 60 seconds. Remote activity fencing/lifetime, graceful shutdown, active-task cold replacement, Rust parity and exact published-tuple conformance remain required. Defaults remain protocol 1.19 with cooperative support disabled.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The connected run at 6396384732beb91b7d29fe2a558abf7b2fbdcaae is not a pass. Run 36817037879 passes the first four timer/request cases, but both local cases fail during fixture shutdown. The new process-check loop reused $pid, replacing the owning Worker PID with the callback PID. Its finalizer then waited for a process it did not own and left the Worker polling. The log also records a poll admission refusal. Stack/image teardown succeeds, and JUnit artifact 11142221037 expires October 8 at 04:55:38 UTC.

dc1edf27367c917c504a14523761a1f23818fd22 fixes the fixture by using a separate activity PID variable. Product source is unchanged from the locally qualified 953-case commit. Both public boundaries pass. Ordinary CI and new connected qualification are running at the corrected exact head and the same pinned Server source. Require the complete connected outcome, memo restart and teardown before accepting this phase.

@rmcdaniel

Copy link
Copy Markdown
Member Author

PHP local activity lifetime qualified at dc1edf27367c917c504a14523761a1f23818fd22

Ordinary CI, both public boundary checks and explicit connected qualification pass at this exact head, including target qualification. The connected job passes six cooperative cases with 158 assertions and memo/cold restart with 67 assertions against Server 2e2d6b31df981e98c1a054a8953e9efe95d17e6e, built with published Workflow 2.3.0. Raw JUnit artifact 11141539999 is retained through October 8 at 05:02:15 UTC. Task stack and image teardown succeeds.

The executor is now connected to local activity handling. The owning Worker remains available while a callback blocks, renews the actual task lease, preserves the original request/deadline, checks execution timeouts and controls result encoding. User heartbeat details return to the owner through typed IPC and retain their own progress accounting. A relay stops the callback process group if the owner disappears. The owner rejects an unsafe result before it can become a local report.

Both connected local cases use a 60-second callback, with and without user heartbeats. They require cancellation and canonical cleanup in less than ten seconds, verify callback/relay process absence and check that no late return marker appears. History proves one original request, one delivery at the local activity call and one shielded cleanup result, without a failed application event. Waiting timers, cold replacement while waiting, duplicate immutable deadlines and discarded successful delivery replies also pass. The separate persisted memo case preserves Avro bytes/types and one memo event.

Local PHP 8.4.26 at product-source commit 6396384 passes 953 cases/5,292 assertions/seven existing runtime skips, static analysis and the local boundary. The corrected head changes only the connected fixture's PID variable. Worker cases cover blocked lease loss/shutdown, cleanup-deadline expiry, execution and heartbeat timeouts, retry-backoff cancellation, original failure/retry classification, invalid metadata and storage refusal. Real-process cases cover owner SIGKILL, typed/large IPC, partial writes and inherited destructor isolation. The earlier fixture failure and correction remain recorded.

Explicit cooperative PHP callbacks require Unix CLI with pcntl/posix, bounded transport and connections opened in the callback process. Captured memory changes do not update the owning Worker. Process termination does not undo external effects, so retries must remain safe. Ordinary workers keep their existing protocol 1.19 behavior.

This remains a source-qualified draft. Next: finish PHP remote activity lifetime/fencing, graceful shutdown, active-task cold replacement and external-payload discovery/transfer I/O bounds. Preserve terminal cancel/terminate behavior. Rust parity and exact published-tuple conformance remain required before release or default activation.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Bounded payload I/O implemented, October 1

Current source is e0711a4 and remains a draft. Cooperative worker requests now share a monotonic budget across payload-policy discovery, uploads, the ordinary API call and response hydration. Polling retains its offered wait plus five seconds, followed by one five-second hydration budget shared by all references. Transfers cannot restart that budget, and late replies are rejected. Whole-second transport timeouts can overshoot the deadline by less than one second before that final rejection.

Optional bounded binary interfaces preserve ordinary transport signatures and settings. Cooperative registration rejects adapters that expose legacy binary I/O without bounded counterparts. The default cooperative transport requires Guzzle with cURL, forces complete-response mode even when the caller enables streaming, and uses Guzzle's finite temporary sink. Upload replies are capped at 64 KiB, successful downloads still require the exact declared size and metadata, and the Client preserves namespace, role and digest validation. Temporary storage spills to disk instead of retaining an unbounded transfer in memory. Large transfers must finish within the worker budget, so a slow link may be refused rather than pinning task ownership. Ordinary workers keep their existing settings and protocol1.19.

Local PHP8.4.26 qualification passes 966 cases / 5,387 assertions / eight opt-in runtime skips, static analysis, the dependency boundary, Compose configuration and git diff --check. Focused regressions use real TCP for stalled download bodies, continuous trickling, stalled upload responses, oversized downloads/replies and successful binary/JSON transfers. Shared-budget cases reject late discovery/upload/hydration before another request or partial task escapes. A settings/role case preserves the caller's ordinary streaming and timeout configuration.

Ordinary public CI and explicit connected source qualification are running on this exact head. The connected suite now includes a unique namespace and a task-owned shared payload volume. An actual cooperative Worker must hydrate and complete a value above the ordinary 2 MiB request limit, with the stored result reference, bytes and digest checked. No connected pass is claimed yet. Existing Server/Python evidence remains retained. Next resolve these exact checks, then continue remote activity lifetime/fencing, graceful shutdown and active-task cold replacement. Rust parity and exact published-tuple conformance remain required before release/default activation. No Cloud deployment or stable release occurred.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Payload bounds qualified on exact PHP source, October 1

Exact PHP head e0711a45668b5552640ef6188916d75c45cf650c passes ordinary CI, both public boundaries and explicit connected qualification, including target qualification. This remains source qualification for the draft, with ordinary defaults unchanged.

Against exact Server 2e2d6b31df981e98c1a054a8953e9efe95d17e6e, built from published Workflow 2.3.0 / bce4365d76b3e5ff3a9f765f8a7831e6997e5da7, PHP8.3.35 passes seven cooperative cases / 167 assertions, and memo/cold restart passes one case / 67 assertions. The cooperative suite takes17.865 seconds. The new actual Worker round trip above the ordinary2 MiB limit takes2.089 seconds and verifies decoded result identity plus the runtime's stored external reference, exact byte count and SHA256. It uses a unique namespace and a task-owned shared payload volume. Existing cancellation/cold-replay cases continue to pass.

Commands are the repository's opt-in ci.yml dispatch with cooperative_qualification=true and the exact server_commit, followed by vendor/bin/phpunit tests/Integration/CooperativeCancellationTest.php --log-junit cooperative-results.xml and the same command for PortableMemoRestartTest.php. Raw JUnit artifact11142114561 expires October8 at05:26:27 UTC. The connected job's stack, images and payload-volume teardown passes at05:26:40 UTC. Target qualification job110232694541 passes.

Local PHP8.4.26 passes 966 cases / 5,387 assertions / eight opt-in runtime skips, static analysis, the dependency boundary, Compose configuration and git diff --check. Real TCP cases prove bounded stalls, continuous trickling, oversized responses and valid binary/JSON transfers. Shared-budget cases reject late discovery/upload/hydration before another request or a partial task escapes. The ordinary caller's namespace, role, streaming and timeout settings are preserved.

Cooperative control I/O now shares one monotonic five-second budget across discovery, payload upload, the API response and hydration. Long polls retain their offered wait, followed by one five-second hydration budget shared by references. Guzzle's cURL complete-transfer mode and finite temporary sinks close the per-read timeout gap. Optional bounded binary interfaces preserve ordinary transport signatures. Whole-second transport limits may overshoot the final fractional second before the budget check rejects a late reply. Slow large transfers can be refused, and custom adapters/handlers must honor their bounded capability. No new dependency, published capability, stable release or Cloud deployment is claimed.

Next: PHP remote activity lifetime/fencing, graceful shutdown and active-task cold replacement, then Rust parity and exact published-tuple conformance before release/default activation. Preserve terminal cancel/terminate behavior, user-heartbeat timeout semantics and at-least-once external effects. Existing Python remote proof fences heartbeat and late publication after canonical delivery. It does not prove an owning Worker can supervise a blocked remote callback without user heartbeats, so carry that distinction into the next shared-contract review.

@rmcdaniel

Copy link
Copy Markdown
Member Author

PHP candidate scope authoring

Starting PHP source: d40fce7.
Native: 36e32ccf2f832c9b253badce83d426e30292b71b.
Server: 785093feff1b44e92126be6f350c0c9bdb834886.

Implement the next source component in SDK PR #91 and shared issue #136:
durably acknowledge an authored scope before entering its body, replay the
original opening and nested parent/shield identity, and preserve the immediate
scope of deferred operations after the lexical body exits. Commit a pending
ordinary prefix on the original workflow claim before opening the scope.

Reject changed or malformed scope trees, command-sequence collisions, unknown
membership, and reparented operations before effects. Retain the existing
unqualified-worker refusal. Candidate authoring does not qualify scoped
cancellation delivery or selective callback supervision. Until those consumers
exist, scoped local callbacks and cancellation-bearing scoped histories must
remain explicit refusals. Do not advertise scope support, freeze protocol 1.20,
publish packages, merge the feature, or close #136.

Qualification: unchanged dependency lock, meaningful baseline failures, focused
authoring/replay/claim tests, full SDK suite and static analysis, ordinary CI and
connected exact-source Server/Native tests when the component is coherent.
Use cached Docker tooling as UID/GID 1000. No provider resources, deployment,
private Actions or paid experiment. Keep readable results in GitHub and raw
files here. Remove task containers and installed dependency/cache state at the
component handoff. Preserve the active feature worktree.

@rmcdaniel

rmcdaniel commented Oct 4, 2026 •

Copy link
Copy Markdown
Member Author

Request and capacity observations qualified

PHP source 28ede57 adds UTC request
start/end/error, poll identity, elapsed time and changed workflow-capacity/task
state to the isolated Python fixture. These new observations omit HTTP headers
and bodies. Existing SDK debug task observations remain enabled. SDK retries,
timeouts, production code,
authoring tests and scenario assertions/deadlines are unchanged.

Ordinary CI
and connected CI
pass at that exact head. Connected results are 32 tests / 1737 assertions,
zero errors/failures/skips, plus 1/67 for the separate cold-worker memo check.
All 32 connected jobs pass. The 32 scenario identities match the earlier
32/1756 run. Five cases have different assertion totals, with assertions made
on repeated history observations while polling. The mixed +30-second cleanup
case keeps its 217 assertions. Normal authoring retains both original scope
identities, 13 history-page reads and one side effect across cold replacement.

The exact other sources remain Server 785093feff1b44e92126be6f350c0c9bdb834886,
Native 36e32ccf2f832c9b253badce83d426e30292b71b,
Python 4a33482644b59bda1608406a4ce0534635338155,
Rust 08824f2950460ec590e2f16272adc6ddf9a890ed and
CLI 933c554f8bffa2bf7d188ced712b69b20844ac2d. XML and public raw artifacts
retain the full source provenance and request/capacity observations.

In this successful execution the Python child claims its cancellation task and
receives a delivered receipt. The added observations can distinguish a future
pending request from held workflow capacity. They do not explain or fix the
earlier missed claim in 37205983406. That remains a publication gate.

#136 stays open, PR 91 stays draft, protocol 1.20 stays unfrozen and published
defaults remain 1.19. Next implement scoped delivery/replay and selective
supervision across SDKs, preserving the unresolved recovery finding through
competitive and exact published-artifact qualification. All task resources are
removed. No deployment or provider allocation occurred.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Refreshed exact-source qualification passed after incorporating the independently
published Python HTTP-timeout fix. #136 remains open and protocol 1.20 unfrozen.

  • PHP connected source run:
    32 tests / 1742 assertions, zero errors, failures or skips. Separate memo
    restart: 1 test / 67 assertions, also passing.
  • Python cooperative source run:
    54 connected passes and one CLI-only skip. Ordinary Python source CI also
    passed.
  • PHP source remains 28ede574ff8ec6d14e24d4a3b3b2a2f1d88aeb3d.
    Python source is now 3c77a01059964cd093f4d7214f1917741818c97a.
    Server 785093feff1b44e92126be6f350c0c9bdb834886, Native
    36e32ccf2f832c9b253badce83d426e30292b71b, Rust
    08824f2950460ec590e2f16272adc6ddf9a890ed and CLI
    933c554f8bffa2bf7d188ced712b69b20844ac2d remain unchanged.

The mixed PHP parent → Python child → Rust remote activity plus PHP local
activity case passed its 237 assertions. Root request
01M43R618ZA71MT7SCV4EY1GYS was accepted at 15:23:09.471325Z, retaining the
original 15:23:39.471325Z deadline. PHP and Rust callback stops were observed
before that deadline, both runs ended Cancelled, and parent cleanup completed
at 15:23:26.974756Z, 17.503431 seconds after the original request.

The real cleanup SIGKILL and replacement retain the same delivery boundary.
Original PID 35303 and replacement 35421 both replay the deterministic
at-delivery budget 26.855458. The API and CLI cascade view records both runs
under one root budget, completed cleanup, the recovery attempt and callback
stop evidence. Inspection is complete, untruncated and has no findings.
The test also checks duplicate identity/deadline and stale attempt fencing.

Scope authoring still has 13 real history page requests, one side effect and a
cold replacement owner. Its explicit scoped-delivery qualification flag is
false. PHP selective scoped supervision and scoped delivery/replay remain
required before claiming that capability.

The Python timeout fix is delivered as ordinary protocol-1.19 SDK 2.3.9 in
sdk-python#93, including
Sample App follow-through.
This successful source run does not establish the cause of the earlier
unclaimed ready task or prove that defect fixed. That investigation remains
explicitly unresolved.

Next cancellation gate: implement and qualify scoped delivery and selective
callback supervision, finish the primary competing-system comparisons, then
freeze and publish the exact contract and rerun the mixed recovery scenario
with published artifacts before closing #136.

Raw connected artifacts are retained by the linked GitHub runs for 90 days.
Private local copies, JUnit, exact source provenance, the cascade API/CLI
snapshot and worker observations have retention review on 2027-01-02.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Selective callback supervisor foundation

Candidate 718f7228cb203dac9a88abb24432498114fb0ec5 adds a distinct internal
scoped stop observation. The concurrent executor joins and acknowledges only
that member, continues supervising surviving callbacks, and lets their results
commit before returning the original scoped observation. Whole-run cancellation,
lost authority and an unrecorded stop receipt still stop the hosting group.

Eight added real-process cases cover stops before fork, before callback entry,
during blocking work and before publication, all members stopping, failed stop
receipt, a later whole-run request and actual owner SIGKILL after partial stop.
The SIGKILL case verifies physical survivor exit without inventing its receipt.
No application heartbeat is used. All four sibling-preservation cases fail
against the unchanged executor and pass with the repair.

Ordinary CI
passes the PHP 8.1–8.4 and framework matrix, static analysis, corpus, packaging,
docs and boundaries. Hosted PHP 8.3 and local PHP 8.4 report 1529 tests / 7379
assertions
, zero errors/failures and 33 connected skips. The complete executor
class passes 21 / 279. The original local lock is unchanged.

The exact-source connected rerun
is running against the previously qualified Server/Native/Python/Rust/CLI tuple.
Its result is pending. It rechecks existing root cancellation and recovery,
not authored scope delivery.

This is the process supervisor component. The prepared callback control path
does not yet produce this scoped observation, and authored local scope execution
and cancellation-bearing scoped histories remain refused. Next connect verified
Server scope-fence observations without changing the worker's root request, then
implement scoped delivery, cleanup clocks and cold replay. No scope capability
is advertised. #136 stays open, this PR stays draft and 1.20 stays unfrozen.

@rmcdaniel

rmcdaniel commented Oct 4, 2026 •

Copy link
Copy Markdown
Member Author

Committed single-call scope replay is pushed at
8d1380a61e3151252cad1a274e0740b54d671819.

The internal Replayer consumes the original unscheduled activity, timer,
condition or child boundary only after validating its authored tree, accepted
request, v5 preparation and matching delivery. Native-generated fixtures cover
ordinary and parent-shielded scopes. Cold replay keeps the original request,
metadata, deadline and fractional clock. Leaving the scope restores the parent's
cancellation state, and the unaffected parent can finish its own operation.

Local PHP 8.4 passes 1630 tests / 8059 assertions, zero errors/failures and
33 connected skips. The focused new class passes 66 / 307, no skips.
Static analysis and the public boundary check pass. All 108 installed package
versions/references match the unchanged lock.

Ordinary CI
and connected source CI
both pass on this head. Hosted PHP 8.3 passes 1630 / 8059 with 33 connected
skips. Connected checks pass 32 / 1738 without skips, plus memo restart
1 / 67. Artifact
11309742529
retains the provenance and raw evidence. Connected CI pins Native
6d484654c7a966770cd6bbce90d9007a1c75e0ba, Server
785093feff1b44e92126be6f350c0c9bdb834886, Python
3c77a01059964cd093f4d7214f1917741818c97a, Rust
08824f2950460ec590e2f16272adc6ddf9a890ed and CLI
933c554f8bffa2bf7d188ced712b69b20844ac2d.

The mixed root cascade passes 237 assertions. Parent cleanup completes in
17.044548 seconds, before the original +30-second deadline. Root request
01M43Y2V874SQA1KDF38ZKK28H retains requested-at
2026-10-04T17:06:16.454965Z and deadline
2026-10-04T17:06:46.454965Z. Original/replacement PHP workers replay delivery
01m43y2y7j2rmjfsaq4fvv0wjf, sequence 1 / span 2, with the same
26.949076 seconds remaining. Both runs reach Cancelled and API/CLI cascade
inspection is complete and untruncated, with no cascade findings.

This profile covers workflow-local cleanup with empty frozen projections.
Pending delivery, populated projections and group/selection boundaries remain
refused. The Worker keeps this path disabled. Native currently rejects new
operations inside a prepared scope, so a PHP shield cannot authorize new durable
cleanup commands there. Next: establish explicit scoped cleanup authority under
the original budget, connect canonical preparation/delivery on the live claim,
then qualify selective projection replay and replacement against Server.

The missed Python recovery claim still needs an explanation or fix. These
passing root-cascade checks do not qualify live scoped cleanup. Protocol 1.20
remains unfrozen, #91 remains
draft, and published mixed-language qualification, Waterline inspection and fair
competitive evidence remain required before shared #136 closes.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Prepared scoped local cleanup is pushed at
f84b8087bf0afa263b78fe3eef845ae9800b2cb2.

The internal replay profile binds shielded sequential cleanup to the original
scope, request, preparation, delivery and captured deadline. It validates all
eight receipt fields before callback admission/control. New deadlines, borrowed
membership, extra authority and stale receipts are refused. Native-generated
fixtures cover watchdog replacement, duplicate admission and stale publication.
A real callback and relay stop and join without application heartbeats. Cold
replay retains the same proof and deterministic clock while the parent retains
its own cancellation state.

Ordinary CI
passes the PHP 8.1–8.4/framework matrix, corpus, packaging, docs, static analysis
and boundary checks. Local PHP 8.4 and hosted PHP 8.3 both pass 1679 tests /
8171 assertions
, zero errors/failures, with the same 33 opt-in connected skips.
Every one of the 1630 preceding cases and all 49 new cases appears exactly once.
The unexpired PHP 8.3 artifact
retains its raw XML until 2027-01-03. Focused scope cleanup plus existing corpus
checks pass 54 / 125 without skips. All installed PHP and Native references
match their unchanged locks.

The new golden executes through the existing prepared-local source profile.
The published Worker consumer and its corpus gate remain unchanged. The Worker
does not enable live scope execution or advertise that capability.

Current connected qualification
is still running against exact Server, Native, Python, Rust and CLI candidates
listed in the PR body. Live scope preparation/delivery, populated projections,
mixed groups, descendant cleanup and scoped SIGKILL/replacement remain separate
gates. No release occurs, and shared #136 remains open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Remote callback-stop receipt recovery

PHP source bdfc5871629de5fe1bda5edd6272347240731679 adds bounded recovery for a transient receipt discovery/write failure after the supervisor has stopped and joined the callback. Four injected cases fail before the change. The corrected remote class passes 34 tests / 491 assertions, including original deadline expiry, invalid deadlines, persistent failure and protocol refusal. Retries preserve the original attempt, owner and cancellation identity. A lost accepted reply must return the first receipt. Discovery and reporting share five seconds and cannot extend the original cleanup deadline or resume the callback.

The full PHP 8.4 suite passes 1689 tests / 8292 assertions, with 35 opt-in connected skips. Inventory verification retains every prior 1679 case once, adds eight receipt tests and two connected faults, and verifies all 108 installed package references against the unchanged lock. Static analysis and the dependency boundary pass. The published corpus consumer remains unchanged.

The original connected run fails one remote WAIT_CANCELLATION_COMPLETED scenario. Its other 31 scenarios pass, including the mixed-language root cascade. Six local remote scenarios pass, so the original failure remains unexplained. Diagnostic qualification retains stalled state and stop receipt diagnostics. The independently reproduced receipt weakness is not yet proof of that original failure's cause.

Current ordinary CI and current connected qualification are pending. The latter adds a failed first stop-receipt write and a lost accepted reply against the exact Server/Native candidates. It retains the same Python/Rust/CLI tuple.

Protocol 1.20 remains unfrozen and unpublished. Scope execution stays disabled in the Worker. The draft PR and #136 remain open for the remaining scope, published-artifact and fair competition acceptance gates.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Bounded receipt recovery qualified at the current source

PHP bdfc5871629de5fe1bda5edd6272347240731679 passes ordinary CI and connected qualification.

  • Local PHP 8.4 and hosted PHP 8.3 both pass 1689 tests / 8292 assertions, with zero errors or failures and 35 opt-in skips. Case identities match and all original locked references are unchanged.
  • Connected qualification passes 34 scenarios / 1880 assertions, with zero errors, failures or skips. Memo restart passes 1 / 67. All 32 preceding connected scenarios remain present once.
  • Both new receipt faults pass against Native/Server. A failed first write preserves the original attempt, owner, request and deadline. A retry after a lost accepted reply returns the original history receipt with duplicate=true. The retained requests and replies have been checked.
  • The PHP parent, Python child, Rust remote activity and PHP local activity source cascade passes 237 assertions in 22.15 seconds, including cleanup replacement before the original 30-second deadline.

Connected artifact 11321860246 and ordinary artifact 11320059507 are retained until January 3, 2027. The original intermittent WAIT_CANCELLATION_COMPLETED stall remains unexplained. This repair independently addresses the reproducible transient receipt weakness, and the diagnostic-only run also passes.

Next: qualify live scope preparation/delivery and the original scoped receipt through callback supervision and replacement replay before enabling the Worker scope profile. Complete selective projections, descendant propagation and operator inspection across SDKs. Protocol 1.20 remains unfinished, unfrozen and unpublished. Published-artifact acceptance and the competitive comparison remain required. Shared #136 stays open.

@rmcdaniel

rmcdaniel commented Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

Scope preparation/delivery transport admitted behind the source profile

PHP 1851c498e67853d2923227f80dba741576452420 adds Server's existing scope preparation and delivery controls to the internal transport. Original owner, claim epoch, scope request and authored call range are retained. Both controls require an explicit shared bounded budget. Supplied deadlines, borrowed preparation IDs, changed ranges and malformed identities are refused before I/O. An uncertain reply remains an error without retry or a delivery claim. A raw transport response is not callback authority.

Focused checks pass 32 tests / 80 assertions. Full PHP 8.4 checks pass 1711 / 8351, zero errors/failures and 35 opt-in skips. All 1689 previous case identities remain once, 22 guard/fault cases are added, 108 locked dependency references match, and the published corpus consumer is unchanged. Static analysis and the public boundary pass. Current ordinary CI passes the PHP 8.1–8.4/framework matrix, regression corpus, packaging and docs. Hosted PHP 8.3 matches local counts and every case identity. Artifact 11321532854 is retained until January 3, 2027. The connected job was skipped in ordinary CI.

The first full local run failed four orphan-process checks because its container lacked init. All four reported processes were stopped zombies under PID1. The corrected runner uses Docker init at unchanged source/dependencies/limits and passes the complete suite. The first run remains retained as failed evidence.

Next is canonical preparation/delivery receipt validation, including the original frozen projection and authority ceiling, then live scoped callback/replacement qualification. Worker scope execution remains disabled, protocol 1.20 unfinished/unfrozen/unpublished and shared #136 open. The earlier intermittent remote Wait stall remains unexplained. The prior connected 34 / 1880 evidence qualifies receipt-repair source bdfc587. The new transport's live scope path remains unqualified.

@rmcdaniel

rmcdaniel commented Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

Canonical scope boundary receipt proof

PHP c99564d043395c28231e98bd7378fc0b68f79d13 now verifies scope preparation and delivery against complete original-claim history. The accepted request, tree, lineage, authored range, v5 preparation and captured deadline must agree. Delivery also retains the previously verified preparation, preventing a substituted identity or deadline even when a new receipt and history agree with each other.

The caller's original monotonic budget covers mutation, one transient lost-reply reconciliation and all history pages. Replacement claims preserve the original preparation and cancellation context. Missing operation addresses, pending stop, incomplete history, changed authority and repeated cursors are refused. These facts grant no callback authority, and Worker scope execution stays disabled.

Final PHP 8.4 checks pass 1788 / 8485, zero errors/failures and 35 opt-in skips. Affected receipt/opening/replay/cleanup checks pass 240 / 636 without skips. All 1711 prior case identities remain once, plus 77 guard/fault cases. All 108 original locked references, the published corpus consumer and Native-generated scope fixture are unchanged. Static analysis and the dependency boundary pass. Ordinary CI passes the PHP 8.1–8.4/framework matrix, regression corpus, packaging and docs. Hosted PHP 8.3 matches every local case identity and the counts. Artifact 11321864340 is retained until January 3, 2027. Ordinary CI skipped the connected job.

Dependency verification caught an omitted ignored lockfile in the first local archive. Those fresh-resolution runs are diagnostic evidence. The final qualification restores the original lock and repeats both negative controls and the complete checks. A missing nullable operation address also has a failing control before its explicit presence guard and a passing regression afterwards.

This qualifies the internal receipt profile for empty member lists. Live scope Server/callback/replacement qualification, populated projections and portable scoped execution remain next. The prior connected root cascade is bound to receipt-repair source bdfc587. Protocol 1.20 remains unfrozen/unpublished and #136 stays open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Actual Server scope receipt qualification started

PHP 667c11dd4d095d94feed73bd3b9f1cd1416bc973 adds two opt-in connected cases for the unchanged canonical receipt implementation. They exercise actual Server preparation/delivery and original-claim paging, both normally and with accepted replies lost. Duplicate Native admission must retain the original request and deadline.

Server has no public per-scope request endpoint yet. Admission uses an explicit Native fixture in the isolated testing app, limited to its synthetic workflow IDs. The fixture runs as UID/GID 1000 with bounded process time and a read-only source mount. It creates no published customer API.

Ordinary CI passes at this head. Local PHP 8.4 and hosted PHP 8.3 match at 1790 / 8485, zero errors/failures, 37 opt-in skips and identical case inventories. All 1788 previous cases and 108 original installed references are preserved. The Client, receipt implementation and published corpus consumer are unchanged. Artifact 11323056465 is retained until January 3, 2027.

Connected qualification is running with Server 92e9f12866be51e86d35b043597af3e705ff8122, Native 6fcba5c64e9c5f673969b04906a35717e4b83421, Python 3c77a01059964cd093f4d7214f1917741818c97a, Rust 08824f2950460ec590e2f16272adc6ddf9a890ed and CLI 933c554f8bffa2bf7d188ced712b69b20844ac2d. Its scope results are pending. Worker scope execution remains disabled, live callbacks/cleanup/replacement remain unqualified, and #136 stays open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Actual Server scope receipt qualification passed

PHP 667c11dd4d095d94feed73bd3b9f1cd1416bc973 passes connected source qualification: 36 scenarios / 2002 assertions, zero errors/failures/skips, plus memo restart 1 / 67. All 34 preceding connected scenarios remain present once. The mixed PHP parent / Python child / Rust remote activity / PHP local activity root cascade passes 237 assertions in 23.05 seconds.

Both new scope receipt cases pass real Server preparation, delivery and complete original-claim history paging. Ordinary and lost accepted replies preserve one Native request, preparation and delivery event, original context and +30-second deadline. Both read 14 single-event pages. Reconciliation keeps the original mutation body, owner and claim epoch. No claim is released and no extra task is created.

Exact source tuple: Server 92e9f12866be51e86d35b043597af3e705ff8122, Native 6fcba5c64e9c5f673969b04906a35717e4b83421, Python 3c77a01059964cd093f4d7214f1917741818c97a, Rust 08824f2950460ec590e2f16272adc6ddf9a890ed, CLI 933c554f8bffa2bf7d188ced712b69b20844ac2d.

Artifact 11323212087 contains source provenance, XML, full histories, mutation receipts and cascade inspection evidence, retained until January 3, 2027. The hosted stack/image teardown succeeded. Ordinary CI and the unchanged locked local source qualification remain passed at 1790 / 8485 with 37 opt-in skips.

Scope request admission still uses a restricted Native fixture in the isolated testing app. There is no public per-scope request API yet. These cases qualify empty-projection receipts, not Worker scope execution, scoped callbacks or cleanup/replacement. The Worker scope execution capability remains disabled. Next: populated selective projections, groups and descendants, then live scoped cleanup/replacement across the SDKs and operator inspection. Specification freeze and exact published acceptance still follow. This draft and shared #136 stay open.

This branch has not been deployed

No deployments
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.

2 participants