Skip to content

Commit 3d9f3fc

Browse files
[cross-repo from server#350] Conformance blocker: complete child-workflows parity coverage (#666)
1 parent dd989e6 commit 3d9f3fc

3 files changed

Lines changed: 153 additions & 15 deletions

File tree

docs/architecture/platform-conformance-suite.md

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ The machine-readable mirror of the public authority is
1818
`Workflow\V2\Support\PlatformConformanceSuite`, exported by the
1919
standalone `workflow-server` from `GET /api/cluster/info` under
2020
`platform_conformance_suite`. Schema:
21-
`durable-workflow.v2.platform-conformance.suite`, version `6`.
21+
`durable-workflow.v2.platform-conformance.suite`, version `7`.
2222

2323
## Why one suite
2424

@@ -50,10 +50,10 @@ target (the standalone `server` claims `standalone_server` *and*
5050

5151
| Target | Required surface families | Required fixture categories |
5252
| --- | --- | --- |
53-
| `standalone_server` | `server_api`, `worker_protocol`, `cluster_info_manifests` | `control_plane_request_response`, `signal_query_runtime_contract`, `namespace_runtime_contract`, `worker_task_lifecycle`, `failure_repair_actionability` |
54-
| `official_sdk` | `official_sdks` (own row), `worker_protocol`, `history_event_wire_formats` | `control_plane_request_response`, `signal_query_runtime_contract`, `namespace_runtime_contract`, `worker_task_lifecycle`, `history_replay_bundles` |
55-
| `worker_protocol_implementation` | `worker_protocol`, `history_event_wire_formats` | `worker_task_lifecycle`, `signal_query_runtime_contract`, `namespace_runtime_contract`, `history_replay_bundles` |
56-
| `cli_json_client` | `cli_json` | `control_plane_request_response` (request side), `signal_query_runtime_contract`, `namespace_runtime_contract`, `cli_json_envelopes` |
53+
| `standalone_server` | `server_api`, `worker_protocol`, `cluster_info_manifests` | `control_plane_request_response`, `signal_query_runtime_contract`, `namespace_runtime_contract`, `child_workflow_runtime_contract`, `worker_task_lifecycle`, `failure_repair_actionability` |
54+
| `official_sdk` | `official_sdks` (own row), `worker_protocol`, `history_event_wire_formats` | `control_plane_request_response`, `signal_query_runtime_contract`, `namespace_runtime_contract`, `child_workflow_runtime_contract`, `worker_task_lifecycle`, `history_replay_bundles` |
55+
| `worker_protocol_implementation` | `worker_protocol`, `history_event_wire_formats` | `worker_task_lifecycle`, `signal_query_runtime_contract`, `namespace_runtime_contract`, `child_workflow_runtime_contract`, `history_replay_bundles` |
56+
| `cli_json_client` | `cli_json` | `control_plane_request_response` (request side), `signal_query_runtime_contract`, `namespace_runtime_contract`, `child_workflow_runtime_contract`, `cli_json_envelopes` |
5757
| `waterline_contract_surface` | `waterline_api` | `signal_query_runtime_contract`, `namespace_runtime_contract`, `waterline_observer_envelopes` |
5858
| `repair_actionability_surface` | `worker_protocol` (failure subset), `server_api` (repair routes) | `failure_repair_actionability` |
5959
| `mcp_discovery_surface` | `mcp_discovery_results` | `mcp_discovery_envelopes` |
@@ -76,6 +76,7 @@ them from the declared locations.
7676
| `signal_query_runtime_contract` | `durable-workflow.github.io` | `static/platform-conformance/signal-query-runtime-scenarios.json` | Live published-artifact scenarios for signal delivery and query consistency across PHP and Python workers, CLI and SDK clients, replay timing, terminal runs, malformed payloads, and operator visibility. |
7777
| `history_replay_bundles` | `durable-workflow.github.io`, `workflow`, `sdk-python` | `static/platform-conformance/replay-runtime-scenarios.json`, `tests/Fixtures/V2/GoldenHistory/`, `tests/fixtures/golden_history/` | Deterministic replay coverage for frozen history bundles, worker restart replay, adversarial refusal, and in-flight signal timing across the official PHP and Python runtimes. |
7878
| `namespace_runtime_contract` | `durable-workflow.github.io` | `static/platform-conformance/namespace-runtime-scenarios.json` | Live published-artifact scenarios for Temporal-parity namespace isolation, lifecycle cleanup, CLI and SDK namespace selection, PHP worker routing, Waterline visibility, Nexus opt-in crossing, and search-attribute value query isolation. |
79+
| `child_workflow_runtime_contract` | `durable-workflow.github.io` | `static/platform-conformance/child-workflow-runtime-scenarios.json` | Live published-artifact scenarios for child workflow orchestration across PHP and Python workers, cross-language parent/child execution, failure and cancellation propagation, replay after worker restart, concurrent fan-out, and namespace behavior. |
7980
| `failure_repair_actionability` | `server`, `workflow` | `docs/contracts/external-task-result.md`, `docs/contracts/replay-verification.md`, fixture pointers therein | Failure objects and repair / actionability shapes for stuck tasks, deterministic failure, and replay-mismatch surfaces. |
8081
| `cli_json_envelopes` | `cli` | `tests/fixtures/control-plane/`, `schemas/` | The `--output=json` and `--output=jsonl` envelopes that automation depends on. Diagnostic-only fields are listed and excluded from the contract diff. |
8182
| `waterline_observer_envelopes` | `waterline` | (TBD: `tests/fixtures/observer/`) | The `/waterline/api/v2/*` shapes and operator dashboard JSON envelopes. Status: provisional — fixtures land alongside the next Waterline contract slice. |
@@ -186,6 +187,35 @@ and include:
186187
- result-record evidence with artifact versions, timestamps, outcomes,
187188
and routed product findings.
188189

190+
### Child workflow runtime contract
191+
192+
The `child_workflow_runtime_contract` category is stable and
193+
load-bearing. It must run against published install channels only, pin
194+
the resolved artifact versions in the result, and name every required
195+
child workflow scenario as `pass`, `fail`, `unsupported`, `not_covered`,
196+
or `runner_blocked` with a linked finding. A single parent/child smoke is
197+
nonconforming until the run covers PHP and Python worker participation,
198+
same-language and cross-language parent/child execution, typed child
199+
failure propagation, parent-to-child cancellation, direct child
200+
cancellation observed by the parent, replay across parent-worker
201+
restart, concurrent fan-out, and namespace behavior.
202+
203+
Required scenarios are published in the public child workflow scenario
204+
manifest at `static/platform-conformance/child-workflow-runtime-scenarios.json`
205+
and include:
206+
207+
- published-artifact install-only evidence for server, CLI, PHP runtime,
208+
and Python SDK;
209+
- Python parent to Python child and PHP parent to PHP child baselines;
210+
- PHP parent to Python child and Python parent to PHP child
211+
cross-language execution;
212+
- typed child failure round-trip across the parent/child runtime matrix;
213+
- parent cancellation propagation to a running child and direct child
214+
cancellation observed by the parent as a typed cancellation;
215+
- replay across parent-worker restart while awaiting a child;
216+
- concurrent child fan-out with aggregate result and timestamp evidence;
217+
- namespace behavior for parent/child lineage.
218+
189219
## Pass / fail rules
190220

191221
The harness runs each fixture against the implementation under test and
@@ -213,14 +243,15 @@ emits a structured result. The rules below are normative.
213243
release does not conform.
214244

215245
5. **Stable runtime scenario coverage.** A stable runtime category such
216-
as `signal_query_runtime_contract`, `history_replay_bundles`, or
217-
`namespace_runtime_contract` must
218-
report every scenario it declares with one of the statuses published
219-
by its runtime scenario manifest: `pass`, `fail`, `unsupported`,
220-
`not_covered`, or `runner_blocked`. Full conformance requires every
221-
required scenario to pass. A smoke-only subset, omitted scenario,
222-
unsupported public surface, uncovered cell, or runner-blocked cell is
223-
nonconforming and must link the owning finding.
246+
as `signal_query_runtime_contract`, `history_replay_bundles`,
247+
`namespace_runtime_contract`, or
248+
`child_workflow_runtime_contract` must report every scenario it
249+
declares with one of the statuses published by its runtime scenario
250+
manifest: `pass`, `fail`, `unsupported`, `not_covered`, or
251+
`runner_blocked`. Full conformance requires every required scenario
252+
to pass. A smoke-only subset, omitted scenario, unsupported public
253+
surface, uncovered cell, or runner-blocked cell is nonconforming and
254+
must link the owning finding.
224255

225256
6. **Provisional categories warn but do not fail.** A failed fixture in
226257
a provisional category emits a warning in the harness output. A
@@ -351,6 +382,14 @@ docs. It indexes them under one normative declaration so a single
351382
`sdk-python/tests/fixtures/golden_history/` are the replay scenario and
352383
bundle authorities. The suite cites them as the
353384
`history_replay_bundles` source-of-truth.
385+
- `durable-workflow.github.io/static/platform-conformance/namespace-runtime-scenarios.json`
386+
is the stable public source of truth for the
387+
`namespace_runtime_contract` category and the scenario matrix consumed
388+
by published artifact harnesses.
389+
- `durable-workflow.github.io/static/platform-conformance/child-workflow-runtime-scenarios.json`
390+
is the stable public source of truth for the
391+
`child_workflow_runtime_contract` category and the scenario matrix
392+
consumed by published artifact harnesses.
354393

355394
## Changing this document
356395

src/V2/Support/PlatformConformanceSuite.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ final class PlatformConformanceSuite
2929
{
3030
public const SCHEMA = 'durable-workflow.v2.platform-conformance.suite';
3131

32-
public const VERSION = 6;
32+
public const VERSION = 7;
3333

3434
public const RESULT_SCHEMA = 'durable-workflow.v2.platform-conformance.result';
3535

@@ -113,6 +113,7 @@ private static function targets(): array
113113
'control_plane_request_response',
114114
'signal_query_runtime_contract',
115115
'namespace_runtime_contract',
116+
'child_workflow_runtime_contract',
116117
'worker_task_lifecycle',
117118
'failure_repair_actionability',
118119
],
@@ -128,6 +129,7 @@ private static function targets(): array
128129
'control_plane_request_response',
129130
'signal_query_runtime_contract',
130131
'namespace_runtime_contract',
132+
'child_workflow_runtime_contract',
131133
'worker_task_lifecycle',
132134
'history_replay_bundles',
133135
],
@@ -142,6 +144,7 @@ private static function targets(): array
142144
'worker_task_lifecycle',
143145
'signal_query_runtime_contract',
144146
'namespace_runtime_contract',
147+
'child_workflow_runtime_contract',
145148
'history_replay_bundles',
146149
],
147150
],
@@ -154,6 +157,7 @@ private static function targets(): array
154157
'control_plane_request_response',
155158
'signal_query_runtime_contract',
156159
'namespace_runtime_contract',
160+
'child_workflow_runtime_contract',
157161
'cli_json_envelopes',
158162
],
159163
],
@@ -292,6 +296,18 @@ private static function fixtureCatalog(): array
292296
'authority_doc' => self::AUTHORITY_URL,
293297
'required_scenarios' => self::namespaceRequiredScenarios(),
294298
],
299+
'child_workflow_runtime_contract' => [
300+
'status' => self::CATEGORY_STATUS_STABLE,
301+
'description' => 'Live published-artifact scenarios for child workflow orchestration across PHP and Python workers, cross-language parent/child execution, failure and cancellation propagation, replay after worker restart, concurrent fan-out, and namespace behavior.',
302+
'sources' => [
303+
[
304+
'repository' => 'durable-workflow.github.io',
305+
'path' => 'static/platform-conformance/child-workflow-runtime-scenarios.json',
306+
],
307+
],
308+
'authority_doc' => self::AUTHORITY_URL,
309+
'required_scenarios' => self::childWorkflowRequiredScenarios(),
310+
],
295311
'failure_repair_actionability' => [
296312
'status' => self::CATEGORY_STATUS_STABLE,
297313
'description' => 'Failure objects and repair / actionability shapes for stuck tasks, deterministic failure, and replay-mismatch surfaces.',
@@ -381,6 +397,7 @@ private static function passFailRules(): array
381397
'signal_query_runtime_contract',
382398
'history_replay_bundles',
383399
'namespace_runtime_contract',
400+
'child_workflow_runtime_contract',
384401
],
385402
],
386403
'provisional_categories_warn_only' => [
@@ -453,6 +470,26 @@ private static function namespaceRequiredScenarios(): array
453470
];
454471
}
455472

473+
/**
474+
* @return list<string>
475+
*/
476+
private static function childWorkflowRequiredScenarios(): array
477+
{
478+
return [
479+
'published_artifact_install_only',
480+
'python_parent_python_child_baseline',
481+
'php_parent_php_child_baseline',
482+
'php_parent_python_child_cross_language',
483+
'python_parent_php_child_cross_language',
484+
'child_failure_round_trip_matrix',
485+
'parent_cancellation_propagates_to_child',
486+
'direct_child_cancellation_observed_by_parent',
487+
'worker_restart_replay_preserves_child_outcome',
488+
'concurrent_child_fan_out',
489+
'child_workflow_namespace_contract',
490+
];
491+
}
492+
456493
/**
457494
* @return array<string, mixed>
458495
*/

tests/Unit/V2/PlatformConformanceSuiteTest.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function testManifestAdvertisesAuthorityIdentity(): void
2828
$manifest = PlatformConformanceSuite::manifest();
2929

3030
$this->assertSame('durable-workflow.v2.platform-conformance.suite', $manifest['schema']);
31-
$this->assertSame(6, $manifest['version']);
31+
$this->assertSame(7, $manifest['version']);
3232
$this->assertSame('docs/platform-conformance.md', $manifest['authority_doc']);
3333
$this->assertSame(
3434
'https://durable-workflow.github.io/docs/2.0/platform-conformance',
@@ -110,6 +110,7 @@ public function testFixtureCatalogCoversIssueDeliverables(): void
110110
'signal_query_runtime_contract',
111111
'history_replay_bundles',
112112
'namespace_runtime_contract',
113+
'child_workflow_runtime_contract',
113114
'failure_repair_actionability',
114115
'cli_json_envelopes',
115116
'waterline_observer_envelopes',
@@ -382,6 +383,62 @@ public function testNamespaceRuntimeContractNamesFullTemporalParitySurface(): vo
382383
);
383384
}
384385

386+
public function testChildWorkflowRuntimeContractIsRequiredForParentChildSurfaces(): void
387+
{
388+
$manifest = PlatformConformanceSuite::manifest();
389+
$category = $manifest['fixture_catalog']['child_workflow_runtime_contract'];
390+
391+
$this->assertSame(
392+
PlatformConformanceSuite::CATEGORY_STATUS_STABLE,
393+
$category['status'],
394+
'child workflow parity must be load-bearing, not a single-runtime smoke.',
395+
);
396+
397+
foreach ([
398+
'standalone_server',
399+
'official_sdk',
400+
'worker_protocol_implementation',
401+
'cli_json_client',
402+
] as $target) {
403+
$this->assertContains(
404+
'child_workflow_runtime_contract',
405+
$manifest['targets'][$target]['required_fixture_categories'],
406+
"$target must be graded against the live child workflow contract",
407+
);
408+
}
409+
410+
$this->assertSame(
411+
[
412+
[
413+
'repository' => 'durable-workflow.github.io',
414+
'path' => 'static/platform-conformance/child-workflow-runtime-scenarios.json',
415+
],
416+
],
417+
$category['sources'],
418+
'the public child workflow scenario manifest must be the consumable source for full child workflow coverage',
419+
);
420+
421+
foreach ([
422+
'published_artifact_install_only',
423+
'python_parent_python_child_baseline',
424+
'php_parent_php_child_baseline',
425+
'php_parent_python_child_cross_language',
426+
'python_parent_php_child_cross_language',
427+
'child_failure_round_trip_matrix',
428+
'parent_cancellation_propagates_to_child',
429+
'direct_child_cancellation_observed_by_parent',
430+
'worker_restart_replay_preserves_child_outcome',
431+
'concurrent_child_fan_out',
432+
'child_workflow_namespace_contract',
433+
] as $scenario) {
434+
$this->assertContains(
435+
$scenario,
436+
$category['required_scenarios'],
437+
"child workflow conformance must name scenario $scenario",
438+
);
439+
}
440+
}
441+
385442
public function testPassFailRulesNameTheCoreContract(): void
386443
{
387444
$manifest = PlatformConformanceSuite::manifest();
@@ -415,6 +472,11 @@ public function testPassFailRulesNameTheCoreContract(): void
415472
$rules['stable_runtime_scenario_coverage']['applies_to_categories'],
416473
'smoke-only namespace coverage must not satisfy the stable runtime category',
417474
);
475+
$this->assertContains(
476+
'child_workflow_runtime_contract',
477+
$rules['stable_runtime_scenario_coverage']['applies_to_categories'],
478+
'smoke-only child workflow coverage must not satisfy the stable runtime category',
479+
);
418480
foreach (['pass', 'fail', 'unsupported', 'not_covered', 'runner_blocked'] as $status) {
419481
$this->assertStringContainsString(
420482
$status,

0 commit comments

Comments
 (0)