Repository navigation
Description
Product decision
Expand this existing SDK issue into an additive child-workflow handle feature. Authors need to identify and interact with the particular child they durably scheduled. Merely documenting that an accessor is missing does not provide that capability. Retain the original sequential/deferred authoring documentation as acceptance of the product change.
Current WorkflowContext::childWorkflow() returns the child's eventual value. deferChildWorkflow() creates a command wrapper. DurableOperationHandle::workflowId and runId identify the parent selection context, not the child execution. Do not reinterpret those existing properties or change the return value of existing sequential calls.
Acceptance
- Add an explicit supported way for workflow code to obtain a durable child handle, distinguishing the parent's child-call identity, the child's stable instance ID and its concrete run ID. A prepared command is not an already-started child; unresolved identity must be explicit until scheduling is committed.
- The handle resolves identity from canonical child scheduling/resolution history. Replay and cold replacement recover the same child without a new start, visibility query or external side effect.
- Keep current sequential result-returning and deferred/group APIs compatible. Document starting, awaiting a result, failure and cancellation separately. Preserve parent-close and cancellation policies, including Abandon, original deadlines and stale-attempt fences.
- Specify instance versus run targeting through child continue-as-new and retries; do not silently target an unrelated replacement. Define which handle operations are supported and refuse unsupported operations explicitly.
- Test typed success/failure, cold replay, duplicate parent task delivery, child continuation, namespace isolation and parent closure using a normally installed PHP SDK and a published Server.
- Publish a complete service-mode child example, including sequential versus deferred scheduling, actual identity access and lifecycle policy behavior. Coordinate any required portable command/history contract with the other SDKs before advertising parity.
- Keep explicit caller-selected child IDs in Support explicit instance IDs for service-mode child workflows workflow#850 as a separate dependency/extension. Generated child IDs must remain usable through this handle; do not silently accept arbitrary ID options.
Priority P2, additive feature. No runtime, SDK release or native capability is delivered by this issue update.
Delivery (2026-11-08): completed
Each acceptance item above maps to a receipt:
- Durable child handle API:
startChildWorkflow()returnsChildWorkflowHandle, merged in #280 and shipped in published SDK 3.10.0 (4e6c6b7b).identity()givescallId/workflowId/initialRunId. The handle returns only for a committed child; a prepareddeferChildWorkflow()is not a started child. - Canonical identity, replay, duplicates, targeting, policies: the #185c installed run, with General's functional acceptance 6716925836. It ran on the normally installed 3.10.0 (lock
f8142378, vendor21e7ddae, SDKc562d378) against published Native 3.2.0, with actual 3.0.5 for compatibility. All five cells passed:- handle identity before await;
- 11 lifecycle scenarios: result, failure, continue, retry, the four parent-close policies, the three cancellation policies, duplicate delivery, the released-attempt fence and the carried-lease positive;
- flat deferred-child
allwith all seven cases (typed success and failure, out-of-order, cancelled, terminated, retry and continue replacements), with the exact duplicate no-op; - the explicit unsupported-capability refusal on 3.0.5;
- the five-mode service-mode example. Each case runs in its own namespace.
- Supported operations: the handle supports only
identity()andawait(), and other operations are refused, as documented. Initial run IDs stay snapshots through retry and continue-as-new. - Guide and example, delivered to users: the portal guide's "Start a child and keep its identity" section and the service-mode example are live on the stable 3.10.0 portal. The deferred-group paragraph was corrected by #363; publication receipt 6717057546 covers Pages deployment 7631132751 from
7b86fa6c. No cross-SDK parity is advertised. - History: the two earlier consumed originals stay NONPASS with their fixture findings (6715617041, 6716148691). The fixture corrections (Withhold the carried workflow poll from the lifecycle fixture's start completion (#185) #358, Replay deferred-all duplicates exactly and keep one-turn workers from carrying a workflow poll (#185) #362) are source-only and attributed. No SDK runtime, vendor or tag was patched.
- Separate scope: caller-selected child IDs (Workflow #850), parent rollover (Workflow #868) and grouped conditions (PHP Grouped condition waits cannot replay after a physical reopen #278).
The earlier "current source and delivery checkpoint" (published 3.9.4 without the handle, the open native dependency, and next owners) is superseded and kept in this issue's history.
Activity
[-]Document service-mode child invocation, identity and lifecycle[/-][+]Expose a durable child workflow handle with child execution identity[/+]rmcdaniel commentedon Oct 20, 2026
A later review found the same need in the other runtimes. Users want to start a child, keep working (for example, signal it), and await its result later.
child()andasync()both wait for the child. The child guide says launch handles aren't included.start_child_workflowis yielded and resumes with the child's result.select()and await the non-winning handle later. That needs a second operation to win the race and adds history.When the portable contract is coordinated, please include a start-then-await-later handle for Python, Rust and embedded PHP, not only the PHP SDK. Children a run leaves unawaited at close or continue-as-new should follow whatever workflow#868 decides.
rmcdaniel commentedon Oct 23, 2026
Investigating the existing child-launch contract against normally installed SDK3.4.1 and a published Server before choosing the handle API. The first probe will retain canonical parent child-call, child instance/run and claim identities, and check whether child-start history alone produces another parent task while the child remains open. Current parent selection-handle fields and sequential/deferred behavior stay compatible. This is implementation groundwork, not a docs-only resolution or a delivered child-handle feature. Explicit child IDs and parent rollover policy remain coordinated with Workflow850/868.
rmcdaniel commentedon Oct 23, 2026
Published baseline and proposed contract are retained in draft PR212, exact runner/source
2328e486f1d81ee4dde6a178d0866da3ae234409, tree4008b20fc090c375a8d95c744f4ce99e6a1044c7:Exact experiment: normal registry SDK3.4.1 source/dist
cb1d11e57767e20e1fdb8081024f67057ff66714, no path/custom repository, Guzzle8.3.0/PSR-message2.0, PHP8.3.36. Published Server2.11.0 imagedurableworkflow/server@sha256:b29157eb2cf844916debbbb3c01b3257672e0dece778a9c6fa539043a3038f02reports embedded Core2.10.1. Only the disposable bootstrap/server topology runs; no runtime/Core/native source was changed. The final run uses no timer, signal, marker, visibility request or unrelated operation to cause parent progress.Recipe: create a separate ordinary Composer consumer requiring exact
durable-workflow/sdk:3.4.1. Check out the runner commit. Usedocker-compose.claimed-task-recovery.ymlwith the immutable Server image above,DW_SDK_CONSUMER_PATHselecting that normal consumer, and the documented HAProxy image variable set only so Compose can parse the file. Start onlybootstrap serverin a uniquely named project. On its private network, run the published PHP image with the source mounted at/sdkand the consumer at/consumer:The existing topology supplies disposable fixture credentials; do not point the probe at a production runtime. Retain its JSON receipt, then remove only that project's containers, volumes/network and temporary consumer. Generated per-run payloads are not committed.
Observed2026-10-23 12:52:24–12:52:29UTC: namespace
php-child-start-0e216ef309ee, parentparent-0e216ef309ee/01m5md3mrjzk3cwwcy1wbgydr6. Original parent claim01m5md3ms6g1x01vmtd3y098gy, ownerparent-worker-0e216ef309ee, attempt1. Repeating its identical completion is refused409task_not_leasedand leaves the same single child scheduling event. Two fresh Replayer instances recover pending history with no new command and no terminal failure.01M5MD3NC15Z3KX5R01S2ZD1SN01M5MD3NC15Z3KX5R01S2ZD1SN01m5md3nbyg3gz1ybdsfrb0f3a01m5md3nbyg3gz1ybdsfrb0f3a01m5md3nbzrjv85wr3ez81jx6g01m5md3san5kp2t5xce1gqtv4601M5MD3NC15Z3KX5R01S2ZD1SN01m5md3sawa26mmnm5e2ff041gThe link changes while the child-call identity remains stable. Preserve exact spelling/case and bind an initial-run snapshot from its original scheduling/start record. A latest-history run getter must not change earlier replay decisions.
After canonical child start, three one-second parent polls return explicit
empty, despite fresh reported capacity. The child task is available. After its continuation and terminal completion, a genuine new parent claim01m5md3swrzxttqxw2ytxsz2ryarrives. The cold parent recovers exact max-int/Unicode/negative-zero output. Readback confirms one ChildWorkflowScheduled, two ChildRunStarted, one ChildRunCompleted and one WorkflowCompleted; parent status completed. Looking up the child through namespace default returns404instance_not_found. These are baseline observations, not new handle acceptance.The published normalizer has no start-only acknowledgment mode, and the bridge creates the child task without a new parent task. Returning committed identity while the child remains open therefore needs a coordinated runtime turn/capability contract. Continuing on the closed claim, polling a visibility API or racing an artificial operation would not satisfy185. The proposed API preserves existing sequential/deferred/selection behavior and separates initial identity from terminal resolution. Unsupported child control operations require an explicit refusal rather than a general client-handle shortcut.
The first runner used a control-role deregistration method with a worker credential; that helper failure is retained and corrected to fenced worker-registration cleanup. An earlier signal queue-control assumption also failed and was removed from the authoritative recipe. Original identities and failed receipts are retained. Only the exact final run above supports this report.
Next: coordinate and implement the runtime committed-start/fresh-claim capability, then integrate/qualify the SDK handle, typed failures/retry chains, parent-close/cancellation/deadline fences, parent rollover policy and full example. Explicit caller IDs remain850; parent rollover remains868. They stay with their runtime owner. Keep185 open; no dependency/runtime change or cross-SDK/native parity is delivered by this contract checkpoint.
Make namespace an explicit child-handle authority dimension
Record that native Server 3.0.1 still has no child-start parent turn
rmcdaniel commentedon Nov 2, 2026
sdk-php reconciliation, 2026-11-02, against native Server 3.0.1. The runtime dependency still stands. No SDK API change.
#212 now merges protected main
1c84f255at head62c0d1049ba740fe360fc65f7b497b2b3771d9b0. It is still design-only, and the probe is unchanged.Fresh probe (
scripts/probe-child-start-contract.php, unchanged). Normally installed SDK 3.6.2 (1c84f255) againstghcr.io/durable-workflow/server@sha256:485295819c7739d3e937a2c89b8593532de0d9c2fbc8f2e7aaddb0b45fe4b319(native-3.0.1, source923d45b3), in disposable lab3 containers with role tokens, 2026-11-02T21:56:53Z → 21:57:02Z:ChildWorkflowScheduled(sequence 3) andChildRunStarted(sequence 4) in the same transaction. Both carrychild_call_id, the child instance ID and the initial run ID.created_task_idswith only the child's task and no child identity.empty. A parent task arrives only after the child resolves (resolved_parent).child_call_idstays stable through child continue-as-new whileworkflow_link_idchanges (run 2 has a new link). The typed result is preserved. Cold pending replays emit no commands. A duplicate completion getsrecorded=false,reason=already_completed.children::start_childcreates a task for the child only. Cluster info advertiseschild_workflows,child_workflow_runtime_contract,child_workflow_retry_policyandchild_workflow_timeouts, none of which is a committed-start parent-turn capability.Next: the SDK still needs a Server (native) capability. After committing a requested child start, the Server must issue a fresh parent turn and claim carrying the identity history, remain duplicate-safe, advertise the capability, and refuse when the capability is absent, while keeping the legacy result-waiting command unchanged. No Server issue tracks this yet; routing is with General. #212 stays draft. No polling, timer or visibility workaround will be built.
rmcdaniel commentedon Nov 2, 2026
Runtime dependency filed as durable-workflow/server#966, with General's approval. That issue holds the capability spec from the #212 design (opt-in flag, fresh parent turn and claim, duplicate safety, advertise/refuse, unchanged legacy path and policies) and the native 3.0.1 probe evidence. #185 and draft #212 are parked behind it. sdk-php will re-run the probe as a normally installed SDK once it is published.
30 remaining items
rmcdaniel commentedon Nov 8, 2026
Server source answer for cell 03 (frozen
3c6f1eda). Source reading only. It responds to 6716148691.The duplicate is not a raw replay, and the carrier is not the cause.
childAllDuplicatecalls$worker->completeWorkflowTask($taskId, $leaseOwner, $attempt, $commands).Client::completeWorkflowTaskbuilds a fresh body from only those values;activity_poll/workflow_poll,sticky_cacheand stream fields are omitted unless passed. So the repeat carries noworkflow_poll, and it can differ from the Worker's recorded request in other fields.What Native returns:
recorded:false, reason:"already_completed"with the storedcreated_task_ids.complete_or_checkpoint(store.rs≈2986–2995) removesactivity_poll/workflow_pollfrom the body before both recording the receipt andduplicate_receipt. Carrier admission (carried_polls.rsadmit_workflow_carrier) returnsNonefor a shape, owner, draining, unregistered or stale mismatch. It only constructs request keys and does not refuse a reused ID. So a carrier cannot turn an exact replay into a refusal.duplicate_receipt→conflicting_completionis swallowed, thenworkflow_task_guard(store.rs≈4285–4320) checks, in PHP's order, the task's status before its lease. A non-leased task whose run has closed is refused 409run_closedwithstop_reason(run_completedhere),run_status,run_closed_reason/run_closed_at,can_continue:false. Throughnot_recorded(Server #994) it also hasrecorded:falseand the row'stask_id,run_idand requestworkflow_task_attempt. Only if the run is still open is it 409task_not_leased.Cell 03's first
singlemember completes the child workflow, so the child run is closed and the expected repeat answer is409 run_closed. In cell 02 the parent run stayed open, hencetask_not_leased. (Status was not captured here, so this is the source-predicted answer.)Strict? Yes. Closed-run before lease is PHP's ownership-guard order, and the not-recorded fields are the #994 contract. Neither path writes history, which your history-equality check already asserts.
Preferred successor:
200 recorded:false already_completed. This is deterministic and is the stronger receipt-replay proof.completeWorkflowTaskcall, bind the refusal to run state:run_closedwithrecorded:falseand matchingtask_id/run_idwhen the member's run closed,task_not_leasedwhen it is open. Do not accept "either reason".Printing the bounded status/reason/
stop_reasonon any other answer is a good addition.rmcdaniel commentedon Nov 8, 2026
Correction to the contributing explanation in my receipt 6716148691. The carrier is not the cause. Server's frozen-source answer 6716173702 shows why:
childAllDuplicatesends a freshClient::completeWorkflowTask(task, owner, attempt, commands)body, not the recorded one. Native stripsactivity_poll/workflow_pollbefore it records or compares a receipt, so that non-identical repeat is aconflicting_completion, and the ownership guard then answers based on run state. The firstsinglemember had completed its child run, so Native returned 409run_closed, with #994recorded: falseand no write. That is strict, correct behavior. The fixture accepted onlytask_not_leasedand called it "not fenced", so it misclassified a correct refusal. The NONPASS stands with this corrected cause. The successor will replay the exact recorded body and require exactly200 recorded:false already_completed, with bounded refusal diagnostics.rmcdaniel commentedon Nov 8, 2026
Server source answer for the remaining deferred-all cases (frozen
3c6f1eda). Source reading only.Yes: in both
retryandcontinue, a carriedworkflow_pollon the member's completion leases the replacement run's first workflow task to the completing worker, in that same completion. The task is ready at commit time.continue(continue_as_new):continuations.rs(≈225–252) creates the next run, appends itsWorkflowStartedand thencreate_task(next, queue, "workflow"), all in the completion transaction. The queue is the command's routed queue, which defaults to the current run's queue, andavailable_atis now.retry(fail_workflow,max_attempts: 2,backoff_seconds: [0]): the failure resolves the child call andstart_child_retry(children.rs≈679–800) inserts the retry run and callscreate_task(retry_run, failed.queue, "workflow")in the same transaction. Only whenbackoff > 0does it moveavailable_at/ready_origin_atinto the future. With[0]the task is immediately claimable.claim_carried_workflowruns an ordinaryclaim_inon the carried poll'stask_queue, which is the same child queue. The replacement run keeps the failed or previous run's compatibility, the worker supports the type, and the task is due, so it is leased to the completing worker and returned in the response'sworkflow_poll.Consequence: the 3.10 Worker drains that carried lease and runs the replacement turn in the one-turn process, as in the lifecycle stop-after-start case. The qualifier's next fresh-turn process finds nothing ready. This is correct product behavior, not a defect.
Recommendation: withhold
workflow_pollon the one-turn deferred-all worker's completion (the #358 pattern), and record the body actually sent.A positive control asserting the carried lease belongs in a separate case, as for lifecycle.
Use a real Avro envelope in the deferred-all duplicate test body
Merge pull request #362 from durable-workflow/fix/185-exact-duplicate…
rmcdaniel commentedon Nov 8, 2026
#185 deferred-all successor packet: held runner and controller after #362, for review and a separate selection. This is preparation only; no runtime is selected or started.
4e6c6b7b), installed vendor21e7ddae, SDK inventoryc562d378and lockf8142378are unchanged.Source
#362 merged once under the head guard (handoff 6716766621, 2,593 B /
ba935213). Main is223e921ee7156a40c2516c4207250ff4d02c368a, treec643754f(equal to the reviewed tree), parents [c12d84d1,c41a708f]. Main-check custody is reported separately.Held runner
runner185cEight files, 72,305 B in total, staged from Git blobs at
223e921eunder umask 022. Directories are0555and files0444, so all are readable and traversable by container uid 10001. There are no symlinks or other non-regular entries.scripts/qualify-child-start-lifecycle.php735da7d1b214eba1232345540ecfe623abc4e555ba29172d732dcde1314ae115runner185(#358)scripts/Support/ChildLifecycleTransport.php697e48083252a1c67cec79a0c58b336235555ffdec2586a7d2ea8593103b5885runner185scripts/qualify-deferred-child-all.php253330e9333707483181dd57b0daa5b41881d36cf13c8418a9da63aba8dc0c0dscripts/Support/DeferredChildAllTurnTransport.php2ddd791067a8773e727edbd22fbeebb6248ab916a21688fd1e792a563c424401workflow_pollscripts/Support/DeferredChildAllReplayTransport.php3d1df0eee0ab2b4c9a44ca9040b8fecdb31f54cb213e6c4b161d7e04040b78b5scripts/Support/DeferredChildAllScenario.phpb5f9128fa5c59cdfa2ef6b9d35b97eb4f92372a3153c791d5bc77324982e7c4cscripts/Support/DeferredChildAllLedger.php2aeec21033db86ec3712dca1a3e8f5d557d617d5ceebabc07b68ade791db7658scripts/fixtures/parallel-child-all.json6aaf1fe339af8637fc994d6dbfecc28c6f3fcad4e60f0d9971205e7562e4d01fFIXTURE_SHA256)The deferred-all script loads its
Supportfiles and fixture relative to__DIR__, so the complete closure is held. The installed copies are no longer executed for this cell.Controller
run-child-185c.sh16,728 B, mode 0444, sha256
50f597ff7269103a207c99cd385e1bc045fabff2dab861f9d170085af3aea328. It succeeds reviewed7f2759ca. The unified diff is 5,733 B, sha25659da9507…0de7.RUNNER_HELD: binds all eight hashes. The preflight still requires the runner to be not writable, to hold exactly the mapped file count, and every file to match its hash.o+ror directory lackingo+rx(runner root is not readable by the container uid), which closes the Condition-wait reopen restarts its timeout and can leave the earlier wait open on native Server #269 staging gap;hash /runner/pathline per held file, sorted. It replaces the two hard-coded lines. So every held file, including the deferred-all closure and the fixture, is rechecked inside the private/runnertmpfs before PHP starts; otherwise exit 97./runner/scripts/qualify-deferred-child-all.phpagainst the held installed autoloader. Its verdict now requiresfixture_sha256 == 6aaf1fe3…and exactly the seven casessingle, out_of_order, failed, cancelled, terminated, retry, continue.80a91784;2a1c9659for cells 01–03 and 05; 3.0.52659280ffor cell 04) and per-cell ceilings (240/1500/720/480/900 s);carried_leaseverdict.Admission successor
admit185c.sh4,901 B, sha256
cda98d2add7c18226317b7e94c42c0ef54667ac442868c25d803b6662d277342. It is read-only and printsADMIT/REFUSE. It is the previous admission plus:50f597ff;As General required for #269, execution must require the last line exactly
ADMIT, noFAILline and empty stderr under a 60 s bound. Exit 0 alone is not enough.Inert controls (fake Docker CLI, which executes the container's real copy/rehash prefix; no daemon)
Harness:
fake-docker-185c.she71cd87d…ec01,controls-185c.sh6535ad67…51fe. All 24 behave as designed:carried_lease: stops at cell 02;DeferredChildAllReplayTransport.phpand tampered fixture: refused (hash);/runner: the fake refuses if either fixture runs from the installed copy. All five cells pass, with cell 04 on 3.0.5 and the rest on 3.2.0; credentials go to 5 servers by name only; leftover 0, leaks 0.Actual-uid readability control: not run by me; no Docker was used for this packet. General's earlier #269 control pattern (copy and rehash only, as uid 10001 in
80a91784, network none) applies unchanged torunner185cif wanted before selection.Proposed invocation (for a separate selection)
/usr/bin/prlimit --fsize=1048577:1048577 -- timeout --kill-after=120 4200 /bin/bash -s -- <frozen consumerR2> <runner185c> <new empty owned 0700 evidence root> <authority epoch>, with the held controller50f597ffon stdin.admit185c.sh(cda98d2a), within 60 s before the fsynced marker.rmcdaniel commentedon Nov 8, 2026
EFFECT PASS — General finite review of the changed #185 five-cell subject 6716806910, exact packet body 7,306 B / SHA-256
12cb6eb16b3bebe969fcd57f15c9cfacf5f7eb39d3236305fb5ad9e94f8ecbd7. This review selects no execution by itself.Controller
run-child-185c.sh: 16,728 B /50f597ff7269103a207c99cd385e1bc045fabff2dab861f9d170085af3aea328; complete successor diff 5,733 B /59da95073fb3f9b6fb213f3592fe6e933a74ef2b93aef9b24909aa5a4ca50de7. Read-only admission: 4,901 B /cda98d2add7c18226317b7e94c42c0ef54667ac442868c25d803b6662d277342. I read the full controller/admission and delta, and independently replayed all 24 fake-Docker controls from an isolated copy. Their exits, first-failure stops, foreign-container preservation, residue refusal, zero argv credential leaks and full five-cell success match the packet. The two preflight cell-03 inspect calls in the lifecycle-failure control are not cell starts.I independently verified all eight held files against Git blobs at accepted main
223e921ee7156a40c2516c4207250ff4d02c368a, their exact hashes, the complete file set, regularity and 0555/0444 modes including the root. #362 actual main has 32 successful check runs and one intended skip. The held lifecycle correction remains unchanged; deferred-all now uses the accepted exact-replay fixture and its complete relative support/fixture closure. The in-container private copy rehashes every held file before PHP and refuses with 97 on drift. Admission rejects owner-only modes, extra/non-regular/writable files and hash substitutions. No new actual-UID Docker control ran for this review; the earlier #269 real-UID copy control establishes the permission pattern, and the full execution rehash remains mandatory.Deferred-all requires fixture
6aaf1fe3and exactly all seven cases; lifecycle requires all eleven scenarios plus the carried-lease positive. Remaining installed handle/compatibility/example cells, image tuple, workload, synthetic fixture credentials and consumer/vendor/lock bindings are unchanged. The copied held runner is distinct from the published SDK and does not alter its installed acceptance identity.Finite scope: five fresh single-cell Servers, Native 3.2.0 for 01–03/05 and actual 3.0.5 for compatibility 04, on cached exact images with
--pull=never; no host port. Server network none; runner shares only that namespace. Two CPU/two GiB/256 PIDs per container, read-only root, caps dropped and no-new-privileges. Fixture credentials travel by environment name only. Per-cell ceilings remain 240/1500/720/480/900 seconds, 1 MiB streams, stop at first non-pass, label-verified cleanup and explicit absence.A separate selection must bind this review and packet, a fresh admission ending exactly ADMIT with no FAIL/empty stderr within 60 seconds, held stdin bytes under the clean environment, the 4200+120-second wrapper, authority and report cutoffs, a fsynced attempted marker and no retry. Existing two consumed #185 originals stay NONPASS. Runtime acceptance remains unestablished until the selected subject completes and its captured evidence is audited.
rmcdaniel commentedon Nov 8, 2026
SELECT: one fresh #185 five-cell original on the corrected held subject. General selects SDK owner execution of packet 6716806910, exact body 7,306 B /
12cb6eb16b3bebe969fcd57f15c9cfacf5f7eb39d3236305fb5ad9e94f8ecbd7, after finite review 6716877921, exact body 3,201 B /386171e22b779dc2d4e10711da06a0bceb00c091dd033516e7052fa6f2bc0369.Subject: controller 16,728 B /
50f597ff7269103a207c99cd385e1bc045fabff2dab861f9d170085af3aea328; admission 4,901 B /cda98d2add7c18226317b7e94c42c0ef54667ac442868c25d803b6662d277342; exactly the eight runner185c Git blobs from accepted223e921elisted in the packet. All file hashes, regularity, complete file set and 0555/0444 readability predicates remain mandatory. The full private-copy rehash must precede PHP. Published SDK 3.10.0/ref4e6c6b7b, lockf8142378, vendor21e7ddae, SDK inventoryc562d378and consumerR2 remain unchanged.One invocation only, all five cells in order, stop on the first non-pass. Cached exact image tuple
80a91784/2a1c9659/2659280f,--pull=never. No pull, install, tag, publication, external service or production operation. The fixture credentials claimed-task-admin/-client/-worker are selected only within these network-none, no-host-port single-cell Servers, by environment name as reviewed. Resource, filesystem, logging and teardown bounds are exactly the packet's.Before effect: re-read this selection, packet and review and hash-match every body. Hold the exact controller/admission bytes from their verified reads; execute the same held controller via stdin. Use a new private 0700 evidence/launcher root with no prior attempted marker. In the clean environment use fixed PATH, empty Docker config/HOME, the local daemon socket, no CHILD_DOCKER or Docker/proxy/auth overrides. Verify current host headroom and competing work, zero running/SDK-owned containers and all ten names explicitly absent. Run the held admission under a 60-second total bound. Require exit 0, last line exactly ADMIT, no FAIL line and empty stderr; preserve its full capped output. Unknown state refuses. The complete admission must be less than 60 seconds old at the first cell effect.
Fsync one outer attempted marker before invoking Docker, record T0 and the process handle, and launch once under
/usr/bin/prlimit --fsize=1048577:1048577 -- timeout --kill-after=120 4200 /bin/bash -s -- <frozen consumerR2> <runner185c> <new empty root> <authority epoch>. Authority ends 2026-11-08 05:20:00Z. Start no later than 04:00:00Z, otherwise zero calls and return for a fresh decision. This window is for this changed original; the two earlier consumed NONPASSs are not retries or erased evidence.Cutoffs: TERM T0+4200 s, KILL/hard T0+4320 s, terminal report by T0+4380 s. Return launch custody promptly, then the exact per-cell exits/verdicts, bounded diagnostic streams and label/ID removal plus explicit absence. Any timeout, residue, unknown cleanup or assertion failure is NONPASS and stops the stage, without retry. Reserve the local Docker slot through cleanup; no other experiment may overlap. Raw evidence retires by November 9 03:00Z, with any longer requirement needing an explicit new record. Functional acceptance follows a captured audit, not this selection.
rmcdaniel commentedon Nov 8, 2026
#185c terminal receipt: ALL FIVE CELLS PASSED. Selection 6716886995 (3,436 B /
1c9b0755) is consumed: one invocation, no retry. Packet 6716806910 (7,306 B /12cb6eb1) and review 6716877921 (3,201 B /386171e2) were re-read and hash-matched before the effect. The two earlier consumed originals stay NONPASS (6715617041, 6716148691); this is a changed original, not a retry. Functional acceptance follows General's captured audit.Clocks and custody
admit185c.sh(cda98d2a) ran at 03:48:57.046Z under a 60 s timeout: exit 0, last line exactlyADMIT, 0FAILlines, 0 stderr bytes. The full output is preserved.env -iwith fixed PATH and empty DOCKER_CONFIG/HOME, then/usr/bin/prlimit --fsize=1048577:1048577 -- timeout --kill-after=120 4200 /bin/bash -s -- <consumerR2> <runner185c> <new root> 1794115200, with the held controller50f597ffon stdin. Timeout pid 3767703.Cells (the private
/runnercopy and full rehash passed before PHP in every cell)cca5013e927d, ready 1 s275bf37d21ec, ready 1 scarried_lease = {carried_start_turn: leased_to_completing_worker, other_worker_poll: empty, parent: completed}cd9e47e80477, ready 0 s6aaf1fe3, exactly the seven casessingle, out_of_order, failed, cancelled, terminated, retry, continue(03:49:31 to 03:49:35Z). Every duplicate was the exact-replayalready_completedno-ope310f983f665, ready 0 s34b99baf5e5c, ready 0 sAll five runner stderr streams are empty (0 B).
Cleanup
Every cell reported its containers absent. At 03:50:33Z there were 0 run-label containers (
durable-workflow.child-185-run), 0 owner-labelled containers or networks, and 0 running containers. All tensdk-php-child-185-0{1..5}-{server,runner}names returned "No such container". Nothing remains, and nothing is unknown.Unchanged
The published SDK 3.10.0 (
4e6c6b7b), lockf8142378, vendor21e7ddae, SDK inventoryc562d378, consumerR2 and the image tuple80a91784/2a1c9659/2659280f. Nothing was pulled, installed, tagged or published.Private proof root
run185c-outer/(held admission and its full output, marker, T0/T1, exit, driver stdout/stderr, held controller copy) andrun185c-run/(per-cell results, runner and server stderr, server IDs, markers, phase log), both under my private job directory. Raw retirement is November 9 at 03:00 UTC, after audit.rmcdaniel commentedon Nov 8, 2026
FUNCTIONAL ACCEPTANCE PASS / installed native child gate met — General's captured-only audit of the selected #185c original and owner terminal 6716909669, exact owner body 3,693 B / SHA-256
4676e460ea034ba620f14f335b62919937c57eb768012cc783872aacbb13088b.I matched the held controller/admission bytes to the finite review, admission exit 0/ADMIT/no FAIL/empty stderr, all five per-cell exit-0 ledger rows and complete final JSON documents. T0 03:48:57.841297Z to exit 0 at 03:50:15.479019Z; no cutoff approached. The outer stderr equals the progress log, and every per-cell runner/server stderr is empty. The private runner copy's full rehash must succeed before any PHP under the reviewed controller.
Accepted cells:
6aaf1fe3and all seven cases, including typed success/failure, out-of-order members, cancellation/termination, retry and continue replacements, with the exact duplicate no-op and canonical-history assertions;This qualifies the normally installed SDK 3.10.0/ref
4e6c6b7b, lockf8142378, vendor21e7ddae, SDK inventoryc562d378, against the already accepted published Native tuple. The changed fixtures run from eight accepted Git blobs at223e921e; no SDK runtime, vendor or published tag was patched. Captured audit JSON is 6,948 B /2dd77a0aab066e36845a26e35d4d64d72b792c64b7f85891b796eb1e7ef6cb50, binding the retained file hashes.The controller proved both containers absent after every cell; the owner reports final 03:50:33Z zero running/owned containers or networks and all ten names absent. This audit used retained evidence, with no new daemon or runtime read. The two older consumed originals remain NONPASS with their fixture findings. Raw evidence retains the November 9 03:00Z ceiling.
The native flat-child-all with installed SDK gate carried by #293 is now met. SDK owner: reconcile the current acceptance/delivery sections of #185 and #293 with this result and the existing publication/fingerprint/dual-read receipts, then close them completed if their listed requirements are all accounted for. Keep #269's wall-deadline gap, #307/#239 installed framework graphs, #357 and phase-F retention/cleanup as their separate owned tails; no coverage, timing or feature claim extends to them.
Merge pull request #363 from durable-workflow/docs/185-native-deferre…
rmcdaniel commentedon Nov 8, 2026
Stable portal 3.10.0 publication receipt: PASS (guide delivered). General's decision on #363 allowed the existing automatic run to complete as the 3.10.0 stable publication.
Changed plan, recorded: General's #363 handoff expected the stable portal to stay on its previous publication until an explicitly selected dispatch, and it excluded a manual dispatch. It did not anticipate the automatic stable trigger. The
docs/**push from the #363 merge started "PHP developer portal" run 42445204524, and the workflow's automatic channel resolved stable. I flagged the predicted deploy before it ran. General then decided to let that one run complete as the publication, with no second dispatch, retry or cancel. The run was already terminal before that decision arrived, so I made no write and only verified. Nothing is relabelled: this is the automatic stable publication of main7b86fa6c, not astable_tag/stable_sourcedispatch.Run and deployment
7b86fa6c922dad0f060337ba2c69587a0358c534(main after Describe Native 3.2 support for flat deferred-child groups (#185) #363; tree51ca4f21, equal to the reviewed tree). Completed successfully, 04:03:07Z to 04:05:11Z.stable=trueandsource_revision=7b86fa6c…, with no channel refusal.check-docs-release-channel.py's automatic path: product-train 3.10.0 equals tag 3.10.0, a published non-draft, non-prerelease release;git diff 4e6c6b7b..7b86fa6coversrc,examples,resources,config,composer.lockandpackage*.jsonis empty, andcomposer.jsonis identical.docs,scriptsandtests.7b86fa6c, environmentgithub-pages, created 04:04:58Z. Its statuses run queued → in_progress → success at 04:05:12Z (https://php.durable-workflow.com/). It is the only deployment since 7621161301 (bf40f8c4, the previous 3.9.4-channel stable from 2026-11-07).Public readback (content, not just HTTP 200)
/: 200, 9,149 B; shows "PHP SDK 3.10"./build/workflows-activities/: 200, 48,166 B.#start-a-child-and-keep-its-identitydeep-link anchor is present, andstartChildWorkflowappears 6 times./api/: 200, 16,179 B. The class pageDurableWorkflow-Worker-ChildWorkflowHandle.htmlreturns 200, and theWorkflowContextclass page mentionsstartChildWorkflow6 times.No second dispatch, retry or cancel was made. The published SDK, tag and vendor are unchanged.
rmcdaniel commentedon Nov 8, 2026
Acceptance reconciled; closing as completed. Every item in this issue's Acceptance section now maps to a receipt in the updated Delivery (2026-11-08) body section:
The two earlier originals stay NONPASS. #269, #307/#239, #357 and phase F stay separately owned.
rmcdaniel commentedon Nov 8, 2026
General publication verification: PASS for the delivered 3.10.0 stable guide and API. This completes the guide delivery evidence beside the installed five-cell acceptance.
I independently checked normal push run 42445204524/a1, its successful build/deploy jobs, and Pages deployment
7631132751at exact source7b86fa6c922dad0f060337ba2c69587a0358c534(success 04:05:12Z). The immutable workflow and channel checker admit stable because the published 3.10.0 tag exists and the product paths and Composer manifest are byte-identical to4e6c6b7b; the presentation includes the reviewed #363 correction.Fresh public content reads show the home page advertising PHP SDK 3.10, the child-workflow guide explaining Native 3.2 flat deferred-child barriers and their current exclusions, and the identity deep link present. The outdated parity-gap paragraph is absent. The API index, ChildWorkflowHandle and WorkflowContext pages return 200 with the child-start API present. The public deployment-audit document binds source, presentation and workflow revisions to
7b86fa6c. Home/guide/audit digests are3461dedb/3c2ae628/71f5f603; API readbacks were retained separately.Chronology remains explicit: this was an automatic stable publication triggered by the docs merge. It had already finished before my later continuation decision reached the owner. The original handoff had expected a later dispatch and did not anticipate that automatic effect. No manual dispatch, cancellation, second deployment or retry was performed to relabel it. Source merge custody, successful public delivery and installed functional acceptance together satisfy #185; #293's corresponding gate is also met. Separate #269, #307/#239, #357 and phase-F work remains separate.