diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f31e02..bc66e37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -541,7 +541,7 @@ jobs: source_mode: embedded_v2 php: '8.3' laravel: '^12.0' - workflow: '^2.0@RC' + workflow: '^2.0' steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6427f84..9b200e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ project follows [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [2.0.0] - 2026-09-01 + ### Added - Service workers can execute deterministically recorded local activities, @@ -89,16 +91,15 @@ project follows [Semantic Versioning](https://semver.org/). straight-line execution. Durable operations now return recorded values or throw recorded failures directly at their call sites, while replay retains strict command-order, history-shape, and command-detail validation. -- Advanced the PHP SDK source to `2.0.0-rc.54` and qualified it with Server - `2.0.0-rc.68`. Worker registration now explicitly advertises memo upserts and +- Published the stable PHP SDK `2.0.0`, qualified with Server `2.0.0`. + Worker registration explicitly advertises memo upserts and typed search attributes, with worker protocol `1.19` and explicit floors for portable worker affinity (`1.18`) and durable selection (`1.19`). During the worker-session lifecycle, session identifiers are canonicalized once so create, routing, renewal, explicit close, and shutdown target one identity. - During the active 2.0 prerelease train, package - metadata names the exact SDK artifact and exact qualified Server artifact - rather than a compatibility range; other prereleases remain historical and - receive no compatibility shim. Avro is the sole public + Package metadata names the exact SDK artifact and exact qualified Server + artifact. The prereleases remain historical and receive no compatibility + shim. Avro is the sole public Durable Workflow 2.0 payload codec, and non-Avro configuration fails before transmission. Managed registration emits an explicit empty validator list, while low-level registration documents and preserves that declaration and diff --git a/README.md b/README.md index a58bf8e..68a98fe 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,12 @@ Create an empty Composer project and install the current published package: mkdir durable-php-quickstart cd durable-php-quickstart composer init --name=acme/durable-php-quickstart --no-interaction -composer require 'durable-workflow/sdk:^2.0@RC' +composer require 'durable-workflow/sdk:^2.0' ``` -The current 2.0 RC declares its verified Server baseline in package metadata; -using another Server prerelease requires separate conformance evidence. Earlier -2.0 prereleases and pre-1.0 SDK releases remain historical rather than -alternate supported baselines. +The stable 2.0 package declares its verified Server baseline in package +metadata. Earlier 2.0 prereleases and pre-1.0 SDK releases remain historical +rather than alternate supported baselines. To install directly from the source repository before a tagged release: @@ -262,11 +261,10 @@ Release metadata and the machine-readable package and Server compatibility line, runtime URL forms, role-specific environment variables, package-resolvable executable sources, expected result, and public published-artifact qualification identity that keep this path in sync. -During the active 2.0 prerelease train, `product-train` names the exact SDK -artifact being qualified and `supported-server-versions` names the exact -standalone Server artifact qualified with it. The latter is not a version range -or a promise of compatibility with other prereleases; earlier prereleases remain -historical and do not activate compatibility shims. +For the stable 2.0 release, `product-train` names the exact SDK artifact and +`supported-server-versions` names the exact standalone Server artifact +qualified with it. Earlier prereleases remain historical and do not activate +compatibility shims. `npm run qualify:quickstart-contract-deployment` verifies that deployed contract and its public workflow evidence. Local runs may omit `GITHUB_TOKEN` and use @@ -501,7 +499,7 @@ Publish the environment-backed configuration, add attributed handler services, and start the supervised Artisan command: ```bash -composer require 'durable-workflow/sdk:^2.0@RC' +composer require 'durable-workflow/sdk:^2.0' php artisan vendor:publish --tag=durable-workflow-config php artisan config:cache php artisan durable-workflow:worker diff --git a/composer.json b/composer.json index 56df641..4140dca 100644 --- a/composer.json +++ b/composer.json @@ -74,8 +74,8 @@ } }, "durable-workflow": { - "product-train": "2.0.0-rc.54", - "supported-server-versions": "2.0.0-rc.68", + "product-train": "2.0.0", + "supported-server-versions": "2.0.0", "worker-protocol-version": "1.19", "control-plane-version": "2", "durable-selection": true, diff --git a/docs/laravel-adoption-contract.json b/docs/laravel-adoption-contract.json index 73c4e19..1cba45e 100644 --- a/docs/laravel-adoption-contract.json +++ b/docs/laravel-adoption-contract.json @@ -38,12 +38,12 @@ }, "embedded_v2": { "package": "durable-workflow/workflow", - "channel": "^2.0@RC", + "channel": "^2.0", "state_owner": "laravel_application" }, "service_v2": { "package": "durable-workflow/sdk", - "channel": "^2.0@RC", + "channel": "^2.0", "state_owner": "server_or_cloud" } }, diff --git a/docs/laravel-adoption-contract.schema.json b/docs/laravel-adoption-contract.schema.json index 96628b0..fc1e998 100644 --- a/docs/laravel-adoption-contract.schema.json +++ b/docs/laravel-adoption-contract.schema.json @@ -271,7 +271,7 @@ "required": ["package", "channel", "state_owner"], "properties": { "package": {"enum": ["durable-workflow/workflow", "durable-workflow/sdk"]}, - "channel": {"enum": ["^1.0", "^2.0@RC"]}, + "channel": {"enum": ["^1.0", "^2.0"]}, "state_owner": {"enum": ["laravel_application", "server_or_cloud"]} } } diff --git a/docs/portal/frameworks/laravel.md b/docs/portal/frameworks/laravel.md index 3b06211..c6571fb 100644 --- a/docs/portal/frameworks/laravel.md +++ b/docs/portal/frameworks/laravel.md @@ -81,7 +81,7 @@ This path keeps state ownership in Laravel. 1. Install the v2 channel in the existing application and run its migrations: ```bash - composer require 'durable-workflow/workflow:^2.0@RC' --with-all-dependencies + composer require 'durable-workflow/workflow:^2.0' --with-all-dependencies php artisan migrate --force ``` @@ -117,7 +117,7 @@ The SDK can be installed while v1 remains present and draining. 1. Install the service-mode channel and publish configuration: ```bash - composer require 'durable-workflow/sdk:^2.0@RC' + composer require 'durable-workflow/sdk:^2.0' php artisan vendor:publish --tag=durable-workflow-config ``` diff --git a/docs/portal/getting-started/client-setup.md b/docs/portal/getting-started/client-setup.md index e4f7ded..c2a3e4e 100644 --- a/docs/portal/getting-started/client-setup.md +++ b/docs/portal/getting-started/client-setup.md @@ -12,7 +12,7 @@ next: --- ## Install and construct -Install from the supported 2.0 prerelease channel shown throughout this portal: +Install from the supported stable 2.0 line shown throughout this portal: ```bash {{ release.composerCommand }} diff --git a/docs/portal/operate/deployment.md b/docs/portal/operate/deployment.md index 688f8ba..c56da46 100644 --- a/docs/portal/operate/deployment.md +++ b/docs/portal/operate/deployment.md @@ -44,7 +44,7 @@ A minimal production image should: 4. expose no HTTP port unless the container also owns a separate health process; 5. identify the application release through `buildId`. -Do not use the SDK repository's `main` branch as a production Composer source. Install `{{ release.composerPackage }}` to follow the supported 2.0 prerelease channel, then commit the resolved version in `composer.lock`. +Do not use the SDK repository's `main` branch as a production Composer source. Install `{{ release.composerPackage }}` to follow the supported stable 2.0 line, then commit the resolved version in `composer.lock`. ## Roll out compatible workers diff --git a/docs/quickstart-contract.json b/docs/quickstart-contract.json index ada8151..76aaeec 100644 --- a/docs/quickstart-contract.json +++ b/docs/quickstart-contract.json @@ -3,9 +3,9 @@ "schema_version": 2, "package": { "name": "durable-workflow/sdk", - "published_version": "2.0.0-rc.54", - "composer_requirement": "2.0.0-rc.54@RC", - "onboarding_requirement": "^2.0@RC" + "published_version": "2.0.0", + "composer_requirement": "2.0.0", + "onboarding_requirement": "^2.0" }, "runtime_targets": { "server": { @@ -13,7 +13,7 @@ "namespace_input": "default", "input_kind": "server_origin", "example_request": "http://localhost:8080/api/workflows", - "image": "durableworkflow/server:2.0.0-rc.68" + "image": "durableworkflow/server:2.0.0" }, "cloud": { "client_input": "https://cloud.example/api/runtime/v1/namespaces/", diff --git a/docs/quickstart-contract.schema.v2.json b/docs/quickstart-contract.schema.v2.json index dde6b7d..953ed89 100644 --- a/docs/quickstart-contract.schema.v2.json +++ b/docs/quickstart-contract.schema.v2.json @@ -88,7 +88,7 @@ "composer_requirement": {"type": "string", "minLength": 1}, "onboarding_requirement": { "type": "string", - "pattern": "^\\^[0-9]+\\.[0-9]+@RC$" + "pattern": "^\\^[0-9]+\\.[0-9]+(?:@RC)?$" } }, "additionalProperties": false diff --git a/scripts/check-docs-examples.mjs b/scripts/check-docs-examples.mjs index c8dd538..3d6538b 100644 --- a/scripts/check-docs-examples.mjs +++ b/scripts/check-docs-examples.mjs @@ -123,12 +123,12 @@ assert( 'quickstart published version must match the Composer product train', ); assert( - quickstart.package?.composer_requirement === `${quickstart.package.published_version}@RC`, - 'quickstart Composer requirement must select the published release candidate', + quickstart.package?.composer_requirement === quickstart.package.published_version, + 'quickstart Composer requirement must select the published stable release', ); assert( - /^\^\d+\.\d+@RC$/.test(quickstart.package?.onboarding_requirement || ''), - 'quickstart onboarding requirement must select a prerelease release channel', + /^\^\d+\.\d+$/.test(quickstart.package?.onboarding_requirement || ''), + 'quickstart onboarding requirement must select a stable release series', ); assert( quickstart.workflow_authoring?.execution_model === 'fiber' diff --git a/scripts/check-docs-portal.mjs b/scripts/check-docs-portal.mjs index b0c83c4..d860c7c 100644 --- a/scripts/check-docs-portal.mjs +++ b/scripts/check-docs-portal.mjs @@ -22,6 +22,8 @@ const release = composer.extra['durable-workflow']; const sdkVersion = release['product-train']; const composerCommand = `composer require ${quickstartContract.package.name}:${quickstartContract.package.onboarding_requirement}`; const sdkSeries = sdkVersion.split('.').slice(0, 2).join('.'); +const sdkChannel = sdkVersion.includes('-') ? `${sdkSeries} prerelease` : sdkSeries; +const onboardingRequirement = sdkVersion.includes('-') ? `^${sdkSeries}@RC` : `^${sdkSeries}`; function filesUnder(directory, extension) { return fs.readdirSync(directory, {withFileTypes: true}).flatMap((entry) => { @@ -178,7 +180,7 @@ assert.equal( release.frameworks.laravel, 'Laravel adoption and Composer framework compatibility must stay aligned.', ); -assert(home.includes(`PHP SDK ${sdkSeries} prerelease`), 'Home release badge must identify the SDK prerelease channel.'); +assert(home.includes(`PHP SDK ${sdkChannel}`), 'Home release badge must identify the SDK release channel.'); assert( home.includes(''), 'Home must keep a primary entry to the Laravel ownership guide.', @@ -187,7 +189,7 @@ assert.equal(quickstartContract.schema_version, 2, 'Quickstart contract must use assert.equal(quickstartContract.$schema, quickstartSchema.$id, 'Quickstart contract must identify its deployed schema.'); assert.equal(quickstartContract.package.name, composer.name, 'Quickstart package must match Composer metadata.'); assert.equal(quickstartContract.package.published_version, sdkVersion, 'Quickstart version must match Composer metadata.'); -assert.equal(quickstartContract.package.onboarding_requirement, `^${sdkSeries}@RC`); +assert.equal(quickstartContract.package.onboarding_requirement, onboardingRequirement); assert.equal( quickstartContract.integrations.embedded_laravel, 'https://php.durable-workflow.com/frameworks/laravel/', diff --git a/scripts/ci/test-ci-qualification.py b/scripts/ci/test-ci-qualification.py index 9604b67..01c1e4b 100644 --- a/scripts/ci/test-ci-qualification.py +++ b/scripts/ci/test-ci-qualification.py @@ -293,7 +293,7 @@ def test_established_laravel_transitions_are_executable(self) -> None: self.assertIn("source_mode: embedded_v1", transition_job) self.assertIn("workflow: '^1.0'", transition_job) self.assertIn("source_mode: embedded_v2", transition_job) - self.assertIn("workflow: '^2.0@RC'", transition_job) + self.assertIn("workflow: '^2.0'", transition_job) self.assertEqual( 1, transition_job.count( diff --git a/scripts/qualify-quickstart-contract-deployment.mjs b/scripts/qualify-quickstart-contract-deployment.mjs index 72c0b00..ff71069 100644 --- a/scripts/qualify-quickstart-contract-deployment.mjs +++ b/scripts/qualify-quickstart-contract-deployment.mjs @@ -220,7 +220,7 @@ function validateContract(contract, schema) { 'deployed contract must select an exact published package version', ); assert( - contract.package?.composer_requirement === `${contract.package.published_version}@RC`, + contract.package?.composer_requirement === contract.package.published_version, 'deployed contract must derive its Composer requirement from the published version', ); assert(!('published_smoke' in contract), 'deployed contract exposes a repository-local smoke path'); diff --git a/tests/DependencyBoundaryTest.php b/tests/DependencyBoundaryTest.php index 1ddd757..30889c0 100644 --- a/tests/DependencyBoundaryTest.php +++ b/tests/DependencyBoundaryTest.php @@ -39,20 +39,21 @@ public function testProductionManifestHasNoEmbeddedFrameworkDependencies(): void } } - public function testPrereleaseMetadataDeclaresExactQualifiedArtifacts(): void + public function testStableMetadataDeclaresExactQualifiedArtifacts(): void { $metadata = $this->manifest()['extra']['durable-workflow']; $quickstart = $this->quickstartContract(); - self::assertMatchesRegularExpression('/^2\.0\.0-rc\.(?:0|[1-9][0-9]*)$/D', $metadata['product-train']); - self::assertSame('2.0.0-rc.68', $metadata['supported-server-versions']); + self::assertSame('2.0.0', $metadata['product-train']); + self::assertSame('2.0.0', $metadata['supported-server-versions']); self::assertSame('1.19', $metadata['worker-protocol-version']); self::assertTrue($metadata['durable-selection']); self::assertSame('1.19', $metadata['durable-selection-minimum-worker-protocol-version']); self::assertTrue($metadata['message-streams']); self::assertSame('1.15', $metadata['message-streams-minimum-worker-protocol-version']); self::assertSame($metadata['product-train'], $quickstart['package']['published_version']); - self::assertSame($metadata['product-train'].'@RC', $quickstart['package']['composer_requirement']); + self::assertSame($metadata['product-train'], $quickstart['package']['composer_requirement']); + self::assertSame('^2.0', $quickstart['package']['onboarding_requirement']); self::assertSame( 'durableworkflow/server:'.$metadata['supported-server-versions'], $quickstart['runtime_targets']['server']['image'],