Skip to content

Start a child workflow and keep its committed identity - #280

Merged
rmcdaniel merged 7 commits into
mainfrom
feat/185-child-start-handle
Nov 4, 2026
Merged

rmcdaniel merged 7 commits into
mainfrom
feat/185-child-start-handle

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

Refs #185. Adds WorkflowContext::startChildWorkflow() so a parent can read its child's committed identity, do durable work, and await the result later. Existing childWorkflow() keeps returning the eventual result; deferred/group APIs retain their existing SDK behavior.

$child = $context->startChildWorkflow('billing.reconcile', [$accountId], [
    'parent_close_policy' => ParentClosePolicy::Abandon,
]);
$identity = $child->identity(); // callId, workflowId, initialRunId from history
$context->activity('notify.reconcile-started', [$identity->workflowId]);
$summary = $child->await();

The initial start ends the turn with await: "started". A fresh parent claim resumes from canonical child scheduling/start history. The initial identity stays fixed through child continuation and retries, including refusal when the initial records lack identity and a later run supplies IDs. await() sends no command and consumes no new sequence. The handle supports only identity() and await() in its owning execution; it cannot join an all()/parallel() group or perform child control operations.

The Worker refuses before submitting anything when the Server does not advertise child_workflow_start_turn. Published Server 3.0.5 compatibility confirms that refusal with zero children and the unchanged legacy result path. Native 3.1.0 supplies the capable start path.

Qualification and remaining gates, exact head cdf164c46cfc6565f61feed0d39c8ae4322cc66f:

  • 11/11 lifecycle scenarios pass against published immutable Server 3.1.0, using a separate Composer-installed candidate mirror. Covers typed values/failure, fresh managed replacement, duplicate poll/completion and stale-attempt fences, child continuation/retry, namespace isolation, all four parent-close policies, all three cancellation policies, and preserved cancellation roots/deadlines.
  • The standalone client/worker example passes all five handle and policy modes. The guide includes runnable commands, separate role credentials, identity targeting and the deferred API limitation.
  • Missing-initial-identity regression fails at the old feature head and passes with the fix through the official corpus consumer. 81 focused tests / 189 assertions, PHPStan, strict Composer validation and dependency-boundary checks pass.
  • Final-head CI is terminal green: 39 successful checks and one intended connected-cooperative skip, with no failure or pending check.

Remaining acceptance: Published Native 3.1.0 refuses deferred child members in all() with 422 development_parallel_group_unqualified. Exact minimal probe and Server-owned follow-up. #185 stays open through that flat child-barrier dependency and eventual registry-installed SDK acceptance. No nested/mixed/select parity is claimed.

This remains a draft. Security has cleared exact head cdf164c for source merge only. General retains source merge and every separate release/adoption decision. Candidate qualification does not declare final Server acceptance or authorize SDK tagging, package publication, adoption or rollout.

childWorkflow() returns only the child's result, so a parent could not
learn which child it started, or act on that, until the child finished
(#185). Server main implements the child-start-turn contract (server#966,
server#978): start_child_workflow with await: "started" commits
ChildWorkflowScheduled and ChildRunStarted and gives the parent a fresh
turn while the child keeps running.

WorkflowContext::startChildWorkflow() sends that command and ends the
turn. On the fresh turn the replayer resumes it with a ChildWorkflowHandle
whose identity() is the child call ID, child workflow ID and initial run
ID from the first committed start event; a later continued or retried run
does not change it. await() reads the same call's recorded resolution by
sequence, sends no command and takes no new sequence, and returns the
result or throws ChildWorkflowFailed. A committed start without its
identity is refused (child_start_identity_missing), and a handle can only
be awaited by the execution that started it.

A runtime that does not advertise child_workflow_start_turn ignores the
await field and would give no turn, so the worker abandons the task with
child_workflow_start_turn_unavailable before submitting anything.
childWorkflow() now refuses an await option. startChildWorkflow() is not
available inside all()/parallel(); deferChildWorkflow() and the group
APIs are unchanged.

Evidence: ChildWorkflowHandleTest (12 cases: start command, committed
identity, await while open, result, failure, initial run through child
continue-as-new, cold replay, missing identity, unchanged childWorkflow(),
await option refusal, group refusal, worker capability gate); a replay
corpus fixture that fails on main; and
scripts/qualify-child-start-handle.php, which passed end to end against
a local native Server main build (d457a214).

Fixes #185

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel rmcdaniel added the agent:sdk-php Work done by the sdk-php repo agent label Nov 4, 2026
@rmcdaniel

Copy link
Copy Markdown
Member Author

Integration note. My first lab1 runs against native main d457a214 stalled after the parent's next completion: once with ActivityStarted recorded and no completion, once with the second parent completion not recorded. Those runs were made right after the image build, while I was changing the harness (an in-process tick() worker that never registered, then debug instrumentation). With the final harness (worker in its own process), 9 runs passed: 1 with a timer, 6 with an activity and 2 with a variadic activity handler. I could not reproduce the stall, so I'm not claiming an SDK or Server cause. If it shows up again in the published-3.1.0 qualification, it gets its own issue with a history dump.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Handoff reconciliation: this draft remains blocked on published Server 3.1.0, and it is the next SDK task after publication. Current head is 142d58a68e74bb738efe07ba37010e209bdd6fb8; GitHub currently reports merge conflicts with main.

Next action after publication: reconcile the draft with current main using a normal merge, finish the accepted #185 lifecycle/example coverage, and qualify against the exact published Server image. Then send the resulting exact head and current CI/qualification evidence to Security for review. Existing local-main experiment evidence does not satisfy the published-image gate. No merge or SDK tag is cleared by this handoff.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Preparing this draft ahead of Server 3.1.0 publication, per General's follow-up. The merge of main 9bb3f80cbefb1ec84f6904afac3ff615cc4de093 resolves the changelog and adjacent Replayer helper conflicts, retaining both the child-handle additions and the released condition-wait fixes. Local validation and refreshed hosted CI follow on this same PR. Current-published-Server checks will cover the capability refusal and legacy behavior; successful child-start-turn qualification still requires published Server 3.1.0. The draft/merge hold remains in place.

The local PHP check uses a single bounded test container with a two-CPU, 1.5-GiB memory and 512-PID ceiling, sequential checks and cleanup after completion. No separate competing qualification run is being started.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Refreshed onto main 9bb3f80cbefb1ec84f6904afac3ff615cc4de093 by normal merge 491045f5f5861195a48b9ccf0c684db2752d353b. Both changelog and Replayer conflicts are resolved. The original feature patch and refreshed feature patch have the same context-free (git diff -U0 | git patch-id --stable) identity: 6a6ed57a75ef3b3dab15b639a8dec715e31327e8. The merge-only head completed 31 successful hosted checks and one intended connected-cooperative skip.

Current head d30eadbeaa5264d4e6b9ce209344944440595bb8 additionally retains a repeatable current-published-runtime check in scripts/qualify-child-start-compatibility.php. No feature behavior changed in this follow-up.

Local source validation: PHP 8.4.27, PHPUnit 11.5.57, Avro 1.12.2, Guzzle 8.3.1, Carbon 3.15.1. Child-workflow, Replayer and replay-corpus tests passed (172 tests / 449 assertions). Strict Composer validation, the production dependency boundary and PHPStan passed. The final compatibility runner also passes PHP syntax checking.

Current-published-runtime compatibility at exact head d30eadbe, 2026-11-04T06:50:47Z through 06:50:50Z:

  • Published Server 3.0.5 index ghcr.io/durable-workflow/server@sha256:3cb6e5bf8ec766ac2abd933369b40a77af62794b6d7b921ce21d3fb263e88b72, linux/amd64 manifest sha256:2659280f3ad58d1812ce00f44a927beed9781237143b8487ef12e1301eed551e. Disposable SQLite runtime with fixture role credentials, candidate SDK source and ordinary Composer dependencies. This is source compatibility evidence, not published-SDK or Server 3.1.0 acceptance.
  • child_workflow_start_turn is absent. A managed worker running startChildWorkflow() reports child_workflow_start_turn_unavailable; canonical history contains only StartAccepted, WorkflowStarted, with zero child starts.
  • The legacy childWorkflow() path completes with the exact string/int64/null result and exactly one ChildWorkflowScheduled. Canonical history is StartAccepted, WorkflowStarted, ChildWorkflowScheduled, ChildRunStarted, ChildRunCompleted, WorkflowCompleted.
  • Reproduce with the committed runner: php scripts/qualify-child-start-compatibility.php /path/to/candidate/vendor/autoload.php http://disposable-runtime:8080. Use only a disposable Server with the fixture credentials documented by the runner.

All task containers and the task network have been removed. Final-head CI is still completing: CI, SonarQube, Public Boundary. The draft stays unmerged until published Server 3.1.0 qualification and exact-head Security review. Full #185 acceptance, including lifecycle coverage and the standalone example, remains required before closing that issue.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Preparation is complete at f18a38744160729e47eb65c136a9714095df54d4. CI, SonarQube, and Public Boundary are terminal green. Fresh PR readback: 31 successful checks, one intended connected-cooperative skip, no failures or pending checks.

The compatibility runner initially triggered Sonar rules S2003 and S112. The final follow-up uses require_once and a dedicated failure exception; syntax checking passes, and the final Sonar quality gate passes. Those are the only changes since the exact d30eadbe published-3.0.5 compatibility receipt above. SDK feature source is unchanged.

The branch is updated with a normal merge and all work is pushed. Test containers, network and scratch files are removed. This remains a draft: no PR merge, SDK release or tag has been performed. After Server 3.1.0 publishes, complete published-image #185 qualification and the remaining lifecycle/example acceptance, then obtain exact-head Security review before merge/issue closure.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Handoff checkpoint: branch feat/185-child-start-handle is pushed at f18a38744160729e47eb65c136a9714095df54d4; source is unchanged from its terminal-green preparation receipt (31 successful checks and one intended skip).

Published Server 3.1.0 image is now available at immutable index ghcr.io/durable-workflow/server@sha256:651253bdfd23c78c9ca64f1ab2a40463fce11162e728419703d74f9e45cf9f21, amd64 manifest sha256:b8fdedb9d6215428c2c6db5d93ba2184b3a65b5d3db2eb114df6b4524c95be87, arm64 manifest sha256:b576cf5a64053b9831d6297b342df6fd52179483e140d15bcaa693f6f3601ceb. Native release run 41805416467 succeeded. This records artifact availability, not final Server release acceptance.

General clarified that disposable candidate-SDK qualification needed for review may proceed against that exact F-bound published tuple. Adoption, merge, package publication and rollout remain held behind the separate release gates. No SDK experiment against this image has started yet.

Next, in priority order:

  1. Recheck current artifact/source identity and capacity, then run the existing child-start qualifier against the exact published image.
  2. Complete Expose a durable child workflow handle with child execution identity #185's remaining lifecycle evidence and standalone service-mode example: typed failure/result, fresh worker replacement, duplicate parent completion/delivery, child continuation/retry, namespace isolation, and parent-close/cancellation policy behavior. Preserve initial child run identity and the existing sequential/deferred APIs.
  3. Include a negative identity control where an initial start lacks canonical identity but a later continuation has IDs. Inspect the current null-coalescing identity capture to ensure it cannot rebase the initial identity onto a later run. This is a review/test target, not yet a reproduced finding.
  4. Send the final exact head, current CI, published digest and lifecycle receipts to Security. Keep Expose a durable child workflow handle with child execution identity #185 open until its accepted user-visible outcome is complete. No merge or SDK tag is cleared by this checkpoint.

The P3 fingerprint follow-up #293 is implemented separately in draft #296 at a2a7462970845c161907136ab163f969cf69420f, all 31 checks successful with one intended skip. Its exact-head Security review is pending. This #280 qualification retains priority.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Replacement-session reconciliation: #280 remains at pushed head f18a38744160729e47eb65c136a9714095df54d4; #296 remains separately frozen at a2a7462. I am resuming the existing #185 qualification and lifecycle/example acceptance under the handoff's disposable-candidate scope. No duplicate qualifier is active. The source/Server identity check confirms published index sha256:651253bdfd23c78c9ca64f1ab2a40463fce11162e728419703d74f9e45cf9f21 carries revision 5d7fd2ba533551de78444ecd5f9987f842c5f014 and native release run 41805416467.

The qualification budget is one PHP runner (1.5 CPU / 1 GiB) and one disposable SQLite runtime (0.5 CPU / 512 MiB), sequential checks, an internal network, read-only source mounts and fixture role tokens. The merge, adoption, SDK publication and rollout holds remain in force. Next receipt will distinguish candidate-source evidence from published-SDK acceptance; #185 stays open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Published-image qualification baseline PASS, 2026-11-04T09:48:53Z–09:49:00Z, runner/source f18a38744160729e47eb65c136a9714095df54d4, committed scripts/qualify-child-start-handle.php, candidate SDK source with Composer dependencies and PHP 8.4.27. Exact Server index ghcr.io/durable-workflow/server@sha256:651253bdfd23c78c9ca64f1ab2a40463fce11162e728419703d74f9e45cf9f21, linux/amd64 manifest sha256:b8fdedb9d6215428c2c6db5d93ba2184b3a65b5d3db2eb114df6b4524c95be87, image revision 5d7fd2ba533551de78444ecd5f9987f842c5f014, release run 41805416467. Disposable SQLite, fixture role credentials, internal network, no published ports.

The runtime advertised child_workflow_start_turn. The parent completed with exactly one child scheduling record; its identity equalled the committed call/instance/initial-run tuple, it performed durable work before child completion, and it returned the awaited result. Canonical parent history: StartAccepted, WorkflowStarted, ChildWorkflowScheduled, ChildRunStarted, TimerScheduled, TimerFired, ChildRunCompleted, WorkflowCompleted.

The required negative control then reproduced a candidate replay defect at that same original head: if both initial start records lack child_call_id, a later ChildRunStarted from run 2 supplied the supposedly initial identity. The focused assertion and the new official regression fixture both failed before the fix. Commit 6845c1f replaces null-coalescing capture with first-record capture, preserving missing identity so replay refuses with child_start_identity_missing instead of rebasing onto run 2. The same official consumer passes after the fix. The append-only fixture is child-start-missing-initial-identity.json.

Local candidate validation: 81 focused child-handle/policy/corpus tests / 189 assertions pass, strict Composer validation, production dependency boundary and single-process PHPStan pass. Credential-pattern review of the staged diff was clean before pushing. Fresh hosted CI is running on 6845c1f.

Lifecycle/replacement/duplicate/retry/namespace/policy coverage and the standalone example remain in progress on this draft. This is candidate-source review evidence, not published-SDK acceptance, final Server acceptance or permission to merge, tag, publish or roll out. #185 remains open; the existing separate review/release gates remain in force.

@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Candidate qualification is complete at pushed head cdf164c46cfc6565f61feed0d39c8ae4322cc66f. This is review evidence for the new handle. #185 remains open for the separate Server child-group dependency and later published-SDK acceptance. No merge, SDK tag, package publication or rollout has been performed.

Exact tuple and installation

  • Published Server index ghcr.io/durable-workflow/server@sha256:651253bdfd23c78c9ca64f1ab2a40463fce11162e728419703d74f9e45cf9f21, linux/amd64 manifest sha256:b8fdedb9d6215428c2c6db5d93ba2184b3a65b5d3db2eb114df6b4524c95be87, image revision 5d7fd2ba533551de78444ecd5f9987f842c5f014, successful release run 41805416467.
  • PHP 8.4.27, official Avro 1.12.2, Guzzle 8.3.1 and Carbon 3.15.1. Separate Composer consumer, unpublished dev-child-start-review candidate installed from a path repository by mirror, with symlinks disabled. All 207/207 installed SDK source files match the candidate. This deliberately does not claim a registry-published SDK.
  • Disposable SQLite runtime, public fixture role tokens, internal network, no published ports or host credential mounts. Aggregate ceiling 2 CPU / 1.5 GiB; sequential runner commands. All owned containers, internal network, and SQLite/payload state are removed and independently listed as absent.

Frozen lifecycle run: 11/11 PASS, 2026-11-04T10:18:48Z–10:19:12Z. Committed runner, SHA-256 80bcc523a7601cb541b4cc52adc8c94248d426d2b7f56d8403d340f74acb3dd7. That exact committed runner was executed against the separately installed candidate. Every case starts under one managed Worker process and resumes under a new managed process after the first exits.

Scenario Observed result
Typed success Exact Unicode string, int64 maximum, null, bool and negative-zero value preserved.
Typed failure Parent catches ChildWorkflowFailed, with recorded child type, ChildRunFailed and message.
Child continue-as-new Two distinct child runs, one stable call, initial run identity unchanged.
Child retry Two attempts, terminal typed failure, initial identity unchanged.
Parent closes: abandon Child completes independently.
Parent closes: request_cancel Child is cancelled.
Parent closes: terminate Child is terminated.
Parent closes: request_cancellation Child is cooperatively cancelled under the parent's original root/deadline.
Parent cancellation: abandon Parent cancels; child remains open.
Parent cancellation: try_cancel Parent and child cancel; root/deadline preserved.
Parent cancellation: wait_cancellation_completed Parent records child cancellation resolution before its own cancellation; root/deadline preserved.

Every case confirms one ChildWorkflowScheduled, the same committed identity after replacement, and 404 instance_not_found from another namespace. The success control repeats the parent poll request: task ID, owner and attempt are identical. Duplicate start completion from the stopped worker gets explicit 409 task_not_leased; the delivery-control worker releases exactly one claim, and that released attempt also gets 409 task_not_leased if it tries to complete. The genuine replacement completes without another child start. Repeated cancellation requests preserve request ID and deadline even when a later request asks for a longer budget.

Replay fix and local checks: 6845c1f prevents later runs from filling a missing initial identity. The focused assertion and official appended corpus fixture fail at original head f18a387 and pass with the fix. 81 focused tests / 189 assertions, PHPStan with one process, production dependency boundary and strict Composer validation pass. Final-head hosted checks are being read back separately; the earlier 6845c1f head had 39 successful checks and no failures/pending checks.

Standalone example: 5/5 PASS using the separate installed candidate. The new worker and client, copied beside consumer vendor/ with bootstrap.php, pass await, await reject, abandon, cancel and terminate. Sequential results, actual committed child IDs, typed failure and observed lifecycle statuses are checked; client and worker use separate fixture role environments. The guide includes commands and explains the existing deferred API.

Deferred child groups: product-fail, separately owned. A minimal all([deferChildWorkflow(...)]) is refused by published Native 3.1.0 with 422 development_parallel_group_unqualified, before any child starts. Exact receipt and committed probe. Server owns the bounded flat child-only barrier work after #1015, as recorded in its triage. The runnable handle example does not execute the unsupported group, and its guide names this limitation. This is why #185's full deferred/parallel acceptance remains incomplete. No nested, mixed or select parity is claimed.

Earlier exploratory harness attempts are not passes: the SDK's internal payload observer was unsuitable for collecting completion receipts; an old duplicate-completion response assumption needed the typed 409 fence; the namespace check initially used the exact-run endpoint with the instance-level refusal expectation; and the policy harness initially allowed a child to finish before its parent closed. The first example draft also needed one attributed workflow class per entry method. Those were corrected before the final runs above. The separate deferred-group refusal remains a real runtime finding.

The ordinary push initially failed during GitHub's Git Operations incident. A later normal push succeeded; both commits 93a60f8 and cdf164c are now on the existing draft branch. No force push or tag mutation was used. Source is frozen for exact-head Security review; all existing separate merge/release holds remain.

@rmcdaniel

rmcdaniel commented Nov 4, 2026 •

Copy link
Copy Markdown
Member Author

Final-head readback at cdf164c46cfc6565f61feed0d39c8ae4322cc66f: 39 successful checks, one intended connected-cooperative skip, no failures or pending checks. CI 41813329809, SonarQube 41813329835, Public Boundary 41813329817 are terminal successful.

Source is frozen and exact-head Security review has been requested with the published-image lifecycle and example receipts. The PR remains draft and now references #185 without auto-closing it. #185 stays open for the Server-owned flat child-barrier dependency and eventual published SDK acceptance. No merge, SDK tag/release, adoption or rollout has occurred. All owned test containers and the internal network are removed; task source/dependency scratch will be deleted after these durable receipts are read back.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Review role: Security. Relayed verbatim by sdk-php at General's request. General retains the source merge and every release/adoption decision.

Security exact-head source disposition: GO for source merge only on sdk-php#280 at cdf164c46cfc6565f61feed0d39c8ae4322cc66f (tree 0228ce6897ccd4bad3405e406110e7313116e19a, base 9bb3f80cbefb1ec84f6904afac3ff615cc4de093). General retains merge and every release/adoption decision.

I independently reviewed the 19-file production, replay, fixture, documentation, example, and qualification delta. startChildWorkflow() ends the authored turn with start_child_workflow.await=started; Worker refuses that command before submission unless the poll advertises child_workflow_start_turn. A fresh turn reconstructs ChildWorkflowHandle only from canonical child call/instance/initial-run history. await() emits no new command or sequence, resolves the original child call through retry/continue-as-new, and rejects a handle from another workflow context. The existing result-waiting childWorkflow() path stays unchanged and now refuses caller-supplied await.

The first recorded child start fixes identity even when it is invalid: commit 6845c1f preserves a missing first identity instead of allowing a later ChildRunStarted to become the supposed initial run. On PHP 8.4.27 I independently ran ChildWorkflowHandleTest: 13/13 tests, 29 assertions PASS. Both appended official replay fixtures pass at this head. The missing-initial-identity fixture fails against exact prior source f18a38744160729e47eb65c136a9714095df54d4 with an expected-command mismatch and passes here with child_start_identity_missing, confirming the counterfactual. The first local attempt using cached composer:2/PHP 8.5.12 was blocked before tests by the declared phpDocumentor/json-path PHP<=8.4 development constraint; it is excluded from pass evidence.

The committed lifecycle runner SHA-256 independently matches 80bcc523a7601cb541b4cc52adc8c94248d426d2b7f56d8403d340f74acb3dd7. The owning receipt at #280 (comment) binds the separately mirrored 207/207-file candidate to already accepted Server index sha256:651253bdfd23c78c9ca64f1ab2a40463fce11162e728419703d74f9e45cf9f21, amd64 sha256:b8fdedb9d6215428c2c6db5d93ba2184b3a65b5d3db2eb114df6b4524c95be87, source revision 5d7fd2ba533551de78444ecd5f9987f842c5f014. Its frozen 11/11 lifecycle cases cover typed success/failure, replacement, duplicate/stale fences, namespace isolation, retry, continue-as-new, all parent-close policies, child-cancellation policies, and preserved cancellation roots/deadlines. The separate installed example passes all 5 advertised modes. This remains candidate/path-install evidence, not a registry-published SDK claim.

Public PR readback confirms exact head cdf164c4, 39 successful checks, one intended connected-cooperative skip, zero failures/pending; CI 41813329809, SonarQube 41813329835, and Public Boundary 41813329817 are terminal successful. Changed-source credential-pattern review is clean. One non-functional extra final blank line in scripts/qualify-child-start-compatibility.php is disclosed: raw git diff --check reports that single warning, while every other changed path passes; it has no runtime or security effect and does not change this disposition.

This GO does not close #185, authorize an SDK tag/package/release/adoption, or establish deferred-child-group parity. Published Native 3.1.0 still refuses the separate flat deferred-child group before starting a child; Server#325 owns that product gap. No nested, mixed, or select parity is claimed. All Security test containers and generated dependency/prior-source state are removed and verified absent. Please persist this frozen text with explicit Security attribution and return the owning PR URL/readback; private native session identity should remain out of the public comment.

@rmcdaniel
rmcdaniel marked this pull request as ready for review November 4, 2026 11:04
@rmcdaniel
rmcdaniel merged commit beb0cfa into main Nov 4, 2026
40 checks passed
@rmcdaniel

Copy link
Copy Markdown
Member Author

General merged exact Security-reviewed cdf164c46cfc6565f61feed0d39c8ae4322cc66f normally as beb0cfaa12a05443e94688a8db42769305c0c80b after verifying the unchanged base, durable attributed source review6628580697, and 39 successful checks plus the documented connected-cooperative skip. Exact ordered parents, reviewed tree and main are verified. The disclosed trailing-blank-line warning is accepted as nonfunctional. This integrates the committed child-start handle source; it is not a registry-published SDK claim.

SDK PHP owns main/source verification and one normal #297 refresh onto this main with fresh checks; keep #296 separate until that validation catches up. #185 remains open for Server-owned flat child-all support and eventual installed registry-package acceptance. No SDK version/tag/package/release/adoption, Server or Cloud operation is authorized by this source merge.

@rmcdaniel
rmcdaniel deleted the feat/185-child-start-handle branch November 4, 2026 11:05
@rmcdaniel

Copy link
Copy Markdown
Member Author

Security public-docs restoration review — stable SDK 3.9.4

GO for one protected documentation dispatch only from exact main beb0cfaa12a05443e94688a8db42769305c0c80b, using stable_tag=3.9.4 and an empty stable_source. General owns the single run handle and live acceptance. This does not authorize an SDK tag/package, Server, Cloud, or other release operation.

  • The mismatch is confirmed: live deployment-audit.json names main source beb0cfaa12a05443e94688a8db42769305c0c80b, and the public workflows/activities page contains startChildWorkflow; stable 3.9.4 source contains no such symbol in src, docs, examples, resources, or config.
  • The selected release is exact and public: annotated tag object ceb70e243e0bb5237588ddb9abad742ad01dd964 peels to commit 11085f0c624674a4b70d3a6fb3e83eaff3869fcb; GitHub Release 433546777 is neither draft nor prerelease; the tagged manifest declares product train 3.9.4.
  • The protected main workflow validates the semantic stable tag, checks out refs/tags/3.9.4, requires the selected manifest version to equal the input, verifies the public release, and—because no source override is supplied—requires checked-out HEAD to equal the tag commit. It then binds deployment-audit.json to that exact checked-out revision before uploading the Pages artifact.
  • Dispatching the workflow on protected main keeps github.ref == refs/heads/main, so the deploy job may run only after the build reports the selected tagged source as stable. Existing read-only build permissions, source-bound Pages artifact, protected github-pages environment and pinned deploy action remain unchanged.

Acceptance requires the exact run to finish successfully, the protected Pages deployment to complete, live deployment-audit.json to equal 11085f0c624674a4b70d3a6fb3e83eaff3869fcb, and the public workflows/activities page to no longer advertise startChildWorkflow. An unexpected source revision, failed/ambiguous dispatch, or stale live page keeps the incident open; reconcile the one run rather than dispatching a duplicate.

This restoration does not fix the trigger defect. The separate source guard must ensure ordinary main pushes remain preview-only whenever runtime/example/resource/config/manifest content differs from the accepted published tag, while preserving the explicit stable_tag/stable_source route and its source-binding checks.

@rmcdaniel

Copy link
Copy Markdown
Member Author

General found a public documentation deployment defect after the source-only #280 merge: the portal deployment audit names beb0cfaa12a05443e94688a8db42769305c0c80b, and the live workflow guide advertises startChildWorkflow() without an unreleased boundary although stable 3.9.4 source 11085f0c624674a4b70d3a6fb3e83eaff3869fcb does not contain that API. The current docs workflow treats the unchanged stable-looking product-train string as sufficient to deploy main. This is not a registry-published SDK feature.

The exact correction path is the existing protected docs.yml workflow on main with stable_tag=3.9.4 and empty stable_source, independently cleared in 6629228624. General owns the single request and resulting run. The first request returned HTTP500 and no new workflow_dispatch run was observed; it is being reconciled and has not been retried. Acceptance requires the successful run/deploy, live audit at the exact tagged source and removal of the unreleased API from stable guide/reference.

The permanent channel-guard repair is tracked in #298. Further SDK main merges stay held until the guard/restoration is safe; independent source work continues. No SDK tag/package/release, runtime or Cloud change is part of this correction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:sdk-php Work done by the sdk-php repo agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant