Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.0.1] - 2026-09-02

### Fixed
- Cold replay now treats an intervening durable step as the authoritative
boundary for later signals and updates, even when they retain an older
condition sequence. Post-condition activities can therefore complete before
those receivers advance the workflow without rebinding them to a closed wait.

## [2.0.0] - 2026-09-01

The Python SDK `2.0.0` promotes the qualified release candidate to the stable
2.0 line. This Avro-only SDK release is supported with Server `2.0.0`; JSON
remains HTTP transport and is not a workflow payload codec.
Expand Down Expand Up @@ -91,10 +101,6 @@ Earlier SDK versions remain historical releases and are not alternate supported
`NamespaceDescription.deleted`.

### Fixed
- Cold replay now treats an intervening durable step as the authoritative
boundary for later signals and updates, even when they retain an older
condition sequence. Post-condition activities can therefore complete before
those receivers advance the workflow without rebinding them to a closed wait.
- The README and PyPI project description now require Server worker protocol
`>=1.19,<2.0`. Package and API-reference release validation derive that
interval from the shipped SDK protocol and reject semantic guidance drift.
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "durable-workflow"
version = "2.0.0"
version = "2.0.1"
description = "Python client and worker SDK for Durable Workflow Cloud and self-hosted Server"
readme = "README.md"
requires-python = ">=3.10"
Expand Down Expand Up @@ -71,8 +71,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"

[tool.durable-workflow]
product-train = "2.0.0"
registry-version = "2.0.0"
product-train = "2.0.1"
registry-version = "2.0.1"
supported-server-versions = "2.0.0"
worker-protocol-version = "1.19"
control-plane-version = "2"
Expand Down
2 changes: 1 addition & 1 deletion scripts/check_pypi_project_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def release_channel(source: SourceMetadata) -> str:

if re.fullmatch(r"2\.0\.0rc[1-9][0-9]*", source.registry_version):
return "prerelease"
if source.registry_version == "2.0.0":
if re.fullmatch(r"2\.0\.(?:0|[1-9][0-9]*)", source.registry_version):
return "stable"
raise ProjectSurfaceError(f"unsupported PyPI release identity: {source.registry_version}")

Expand Down
10 changes: 8 additions & 2 deletions scripts/check_release_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,19 @@ def load_source_metadata(source_ref: str) -> SourceMetadata:
server_version = tool.get("supported-server-versions")
worker_protocol_version = tool.get("worker-protocol-version")
classifiers = project.get("classifiers")
if not isinstance(version, str) or not re.fullmatch(r"2\.0\.0(?:-rc\.[1-9][0-9]*)?", version):
if not isinstance(version, str) or not re.fullmatch(
r"2\.0\.(?:0-rc\.[1-9][0-9]*|0|[1-9][0-9]*)",
version,
):
raise ReleaseMetadataError("source version is not an exact supported Durable Workflow 2.0 release")
if product_train != version:
raise ReleaseMetadataError("product-train does not match project.version")
if registry_version != version.replace("-rc.", "rc"):
raise ReleaseMetadataError("registry-version is not the PEP 440 form of project.version")
if not isinstance(server_version, str) or not re.fullmatch(r"2\.0\.0(?:-rc\.[1-9][0-9]*)?", server_version):
if not isinstance(server_version, str) or not re.fullmatch(
r"2\.0\.(?:0-rc\.[1-9][0-9]*|0|[1-9][0-9]*)",
server_version,
):
raise ReleaseMetadataError("supported-server-versions must identify one exact qualified Server release")
if not isinstance(worker_protocol_version, str):
raise ReleaseMetadataError("worker-protocol-version must be a string")
Expand Down
6 changes: 3 additions & 3 deletions tests/test_pypi_project_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@


def source_metadata(*, stable: bool = False) -> SourceMetadata:
version = "2.0.0" if stable else "2.0.0-rc.44"
registry_version = "2.0.0" if stable else "2.0.0rc44"
version = "2.0.1" if stable else "2.0.0-rc.44"
registry_version = "2.0.1" if stable else "2.0.0rc44"
return SourceMetadata(
commit="a" * 40,
name="durable-workflow",
Expand Down Expand Up @@ -305,7 +305,7 @@ def test_stable_project_root_mismatch_remains_release_blocking(
"scripts.check_pypi_project_surface._request_json",
lambda url: exact_version_payload(source) if url.endswith(f"/{source.registry_version}/json") else root,
)
monkeypatch.setattr("scripts.check_pypi_project_surface._pip_report", lambda *_args, **_kwargs: pip_report("2.0.0"))
monkeypatch.setattr("scripts.check_pypi_project_surface._pip_report", lambda *_args, **_kwargs: pip_report("2.0.1"))

with pytest.raises(ProjectSurfaceError, match="stable project-root metadata did not converge.*field version"):
main(["--source-ref", "release-source"])
Expand Down
20 changes: 10 additions & 10 deletions tests/test_release_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def source_metadata() -> SourceMetadata:
return SourceMetadata(
commit="a" * 40,
name="durable-workflow",
version="2.0.0",
registry_version="2.0.0",
version="2.0.1",
registry_version="2.0.1",
server_version="2.0.0",
worker_protocol_version="1.19",
summary="Python SDK for the Durable Workflow 2.0 platform",
Expand All @@ -41,9 +41,9 @@ def test_worker_release_identity_matches_supported_server_and_protocol() -> None
project = manifest["project"]
release = manifest["tool"]["durable-workflow"]

assert project["version"] == "2.0.0"
assert project["version"] == "2.0.1"
assert release["product-train"] == project["version"]
assert release["registry-version"] == "2.0.0"
assert release["registry-version"] == "2.0.1"
assert release["supported-server-versions"] == "2.0.0"
assert release["worker-protocol-version"] == PROTOCOL_VERSION == "1.19"
assert release["durable-selection"] is True
Expand Down Expand Up @@ -93,14 +93,14 @@ def test_release_metadata_loader_accepts_the_authorized_stable_identity(
pyproject = b"""
[project]
name = "durable-workflow"
version = "2.0.0"
version = "2.0.1"
description = "Python SDK for Durable Workflow 2.0"
readme = "README.md"
classifiers = ["Programming Language :: Python :: 3"]

[tool.durable-workflow]
product-train = "2.0.0"
registry-version = "2.0.0"
product-train = "2.0.1"
registry-version = "2.0.1"
supported-server-versions = "2.0.0"
worker-protocol-version = "1.19"
"""
Expand All @@ -120,9 +120,9 @@ def git(*arguments: str) -> bytes:

monkeypatch.setattr(check_release_metadata, "_git", git)

source = check_release_metadata.load_source_metadata("2.0.0")
assert source.version == "2.0.0"
assert source.registry_version == "2.0.0"
source = check_release_metadata.load_source_metadata("2.0.1")
assert source.version == "2.0.1"
assert source.registry_version == "2.0.1"


def test_normal_project_page_is_retained_as_rendered_evidence(monkeypatch: pytest.MonkeyPatch) -> None:
Expand Down