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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ for immutable context, operation policies, shielded cleanup and recovery.

## Development

The installed `durable-workflow-replay-conformance` command runs an offline
Python replay shard. Its `sdk-python` artifact pin must match the installed
distribution version reported by `importlib.metadata.version("durable-workflow")`.
The report records that installed identity separately from the declared platform
tuple and fails if metadata is missing or the SDK pin differs. Replay success
does not prove installation or execution of the other declared artifacts; the
aggregate runner supplies its own verified installation receipts.

```bash
pip install -e '.[dev]'
ruff check src/ tests/
Expand Down
51 changes: 35 additions & 16 deletions src/durable_workflow/replay_conformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import sys
from collections.abc import Mapping, Sequence
from datetime import datetime, timezone
from importlib import metadata
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -204,6 +205,17 @@ def compose_report(
started_at = _timestamp()
versions = _canonical_artifact_metadata(dict(artifact_versions or {}))
sources = _canonical_artifact_metadata(dict(artifact_sources or {}))
try:
installed_version = metadata.version("durable-workflow")
except metadata.PackageNotFoundError:
installed_version = None
identity = {
"runtime": "sdk-python",
"package": "durable-workflow",
"version": installed_version,
"version_source": "installed_distribution_metadata" if installed_version else "unavailable",
"declared_version": _artifact_metadata(versions, "sdk-python"),
}
scenario_results: dict[str, dict[str, Any]] = {}
findings: list[dict[str, Any]] = []
finding_links: dict[str, list[dict[str, Any]]] = {}
Expand All @@ -212,7 +224,7 @@ def compose_report(
scenario_results,
findings,
finding_links,
_published_artifact_scenario(versions, sources),
_published_artifact_scenario(versions, sources, installed_version),
)

for family, scenario_id in COMPLETED_SCENARIOS.items():
Expand Down Expand Up @@ -242,6 +254,9 @@ def compose_report(
has_failures = any(
scenario.get("status") != "pass" for scenario in scenario_results.values()
)
for scenario in scenario_results.values():
scenario["implementation_identity"] = dict(identity)
scenario["artifact_tuple_evidence"] = "declared"

return {
"schema": RESULT_SCHEMA,
Expand All @@ -252,6 +267,8 @@ def compose_report(
"finished_at": _timestamp(),
"artifact_versions": versions,
"artifact_sources": sources,
"artifact_tuple_evidence": "declared",
"implementation_identity": identity,
"runtime_matrix": {
"runtimes": ["sdk-python"],
},
Expand Down Expand Up @@ -307,6 +324,7 @@ def _append_scenario(
def _published_artifact_scenario(
artifact_versions: Mapping[str, str],
artifact_sources: Mapping[str, str],
installed_version: str | None,
) -> dict[str, Any]:
missing_versions: list[str] = []
missing_sources: list[str] = []
Expand Down Expand Up @@ -338,14 +356,14 @@ def _published_artifact_scenario(
untrusted_sources[artifact] = source

missing = sorted(set(missing_versions + missing_sources))
status = (
"pass"
if not missing
and not rejected_versions
and not forbidden_sources
and not untrusted_sources
else "fail"
)
declarations_valid = not missing and not rejected_versions and not forbidden_sources and not untrusted_sources
declared_version = _artifact_metadata(artifact_versions, "sdk-python")
identity_error = None
if not installed_version:
identity_error = "installed_sdk_metadata_missing"
elif declared_version != installed_version:
identity_error = "installed_sdk_version_mismatch"
status = "pass" if declarations_valid and identity_error is None else "fail"

return _scenario(
"published_artifact_install_only",
Expand All @@ -361,8 +379,14 @@ def _published_artifact_scenario(
"rejected_versions": rejected_versions,
"forbidden_sources": forbidden_sources,
"untrusted_sources": untrusted_sources,
"published_artifacts_only": status == "pass",
"published_install_tuple_proven": status == "pass",
"declared_artifacts_published_only": bool(declarations_valid),
"declared_sdk_version": declared_version,
"installed_sdk_version": installed_version,
"sdk_identity_error": identity_error,
"sdk_version_matches": bool(installed_version and declared_version == installed_version),
"artifact_tuple_evidence": "declared",
"published_artifacts_only": False,
"published_install_tuple_proven": False,
},
)

Expand Down Expand Up @@ -589,11 +613,6 @@ def _scenario(
"scenario_id": scenario_id,
"status": status,
"published_artifact_versions": dict(artifact_versions),
"implementation_identity": {
"runtime": "sdk-python",
"package": "durable-workflow",
"version": _artifact_metadata(artifact_versions, "sdk-python"),
},
"runtime_matrix": {
"runtimes": ["sdk-python"],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"$schema": "https://durable-workflow.github.io/schemas/replay-regression-v1.json",
"fixture_schema": "durable-workflow.replay-regression/v1",
"id": "installed-replay-sdk-identity-mismatch",
"protocol_version": "1.20",
"bindings": ["python"],
"workflow": {
"type": "sdk.replay-conformance-report",
"input": [{
"artifact_versions": {
"server": "2.6.1",
"cli": "2.2.3",
"workflow-php": "2.5.12",
"sdk-python": "2.5.9",
"waterline": "2.10.0"
},
"artifact_sources": {
"server": "published_docker_image",
"cli": "github_release_asset",
"workflow-php": "packagist_package",
"sdk-python": "pypi_package",
"waterline": "packagist_package"
}
}]
},
"command_sequence": [{
"type": "replay_conformance_report",
"outcome": "fail",
"sdk_identity_error": "installed_sdk_version_mismatch",
"declared_sdk_version": "2.5.9",
"published_install_tuple_proven": false
}],
"expected": {
"command_sequence": [{
"type": "replay_conformance_report",
"outcome": "fail",
"sdk_identity_error": "installed_sdk_version_mismatch",
"declared_sdk_version": "2.5.9",
"published_install_tuple_proven": false
}]
},
"provenance": {
"issue": "https://github.com/durable-workflow/sdk-python/issues/139",
"observed_installed_sdk": "2.5.10",
"observed_old_outcome": "pass",
"scope": "offline replay conformance report; other artifacts are declarations"
}
}
47 changes: 44 additions & 3 deletions tests/test_replay_conformance.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from __future__ import annotations

import json
from importlib import metadata
from pathlib import Path
from typing import Any

import pytest

from durable_workflow.replay_conformance import (
COVERAGE_SCOPE,
RESULT_SCHEMA,
Expand All @@ -25,6 +28,11 @@ def artifact_versions() -> dict[str, str]:
}


@pytest.fixture(autouse=True)
def installed_sdk_metadata(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(metadata, "version", lambda package: "0.4.73")


def artifact_sources() -> dict[str, str]:
return {
"server": "docker_image",
Expand Down Expand Up @@ -81,7 +89,7 @@ def test_compose_report_emits_python_replay_conformance_shard() -> None:
assert indexed[scenario_id]["status"] == "pass"


def test_report_proves_published_artifact_install_policy() -> None:
def test_report_validates_declared_tuple_without_claiming_installation_proof() -> None:
report = compose_report(
artifact_versions=artifact_versions(),
artifact_sources=artifact_sources(),
Expand All @@ -90,13 +98,46 @@ def test_report_proves_published_artifact_install_policy() -> None:
observed = published["observed_outputs"]

assert published["status"] == "pass"
assert observed["published_artifacts_only"] is True
assert observed["published_install_tuple_proven"] is True
assert observed["published_artifacts_only"] is False
assert observed["published_install_tuple_proven"] is False
assert observed["declared_artifacts_published_only"] is True
assert observed["artifact_tuple_evidence"] == "declared"
assert observed["installed_sdk_version"] == "0.4.73"
assert observed["sdk_version_matches"] is True
assert observed["sdk_identity_error"] is None
assert report["implementation_identity"]["version"] == "0.4.73"
assert observed["missing_artifacts"] == []
assert observed["artifact_sources"]["server"] == "docker_image"
assert observed["artifact_sources"]["sdk-python"] == "pypi_package"


def test_report_rejects_declared_sdk_version_mismatch(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(metadata, "version", lambda package: "2.5.10")
report = compose_report(artifact_versions=artifact_versions(), artifact_sources=artifact_sources())
install = scenarios(report)["published_artifact_install_only"]
assert report["outcome"] == "fail"
assert install["status"] == "fail"
assert install["observed_outputs"]["installed_sdk_version"] == "2.5.10"
assert install["observed_outputs"]["declared_sdk_version"] == "0.4.73"
assert install["observed_outputs"]["sdk_identity_error"] == "installed_sdk_version_mismatch"
assert install["observed_outputs"]["published_install_tuple_proven"] is False
assert all(scenario["implementation_identity"]["version"] == "2.5.10" for scenario in report["scenario_results"])
assert report["finding_links"]["published_artifact_install_only"]


def test_report_rejects_missing_installed_sdk_metadata(monkeypatch: pytest.MonkeyPatch) -> None:
def missing_metadata(package: str) -> str:
raise metadata.PackageNotFoundError(package)

monkeypatch.setattr(metadata, "version", missing_metadata)
report = compose_report(artifact_versions=artifact_versions(), artifact_sources=artifact_sources())
install = scenarios(report)["published_artifact_install_only"]
assert report["outcome"] == "fail"
assert install["observed_outputs"]["sdk_identity_error"] == "installed_sdk_metadata_missing"
assert install["implementation_identity"]["version"] is None
assert install["observed_outputs"]["published_install_tuple_proven"] is False


def test_report_rejects_missing_versions_and_sources() -> None:
report = compose_report(
artifact_versions={"sdk-python": "0.4.73"},
Expand Down
26 changes: 26 additions & 0 deletions tests/test_replay_regression_corpus.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
from collections.abc import Mapping, Sequence
from importlib import metadata
from pathlib import Path
from typing import Any
from unittest.mock import AsyncMock
Expand All @@ -11,6 +12,7 @@
from durable_workflow import Replayer, Worker, serializer, workflow
from durable_workflow.client import PROTOCOL_VERSION, Client, WorkflowExecution, WorkflowStreamAppendItem
from durable_workflow.errors import NonDeterministicReplayError, WorkflowCancelled, WorkflowPayloadDecodeError
from durable_workflow.replay_conformance import compose_report
from durable_workflow.workflow import (
LocalActivityExecutionAborted,
WorkflowContext,
Expand Down Expand Up @@ -398,6 +400,27 @@ def _execute_fixture(fixture: dict[str, Any]) -> list[dict[str, Any]]:
assert isinstance(workflow, dict)
workflow_type = workflow.get("type")
assert isinstance(workflow_type, str) and workflow_type
if workflow_type == "sdk.replay-conformance-report":
# This public command executes the SDK's offline replay scenarios. Its
# regression input is artifact declarations, not an invented history.
assert "history" not in fixture
inputs = workflow.get("input")
assert isinstance(inputs, list) and len(inputs) == 1 and isinstance(inputs[0], dict)
report = compose_report(**inputs[0])
install = next(scenario for scenario in report["scenario_results"]
if scenario["scenario_id"] == "published_artifact_install_only")
observed = install["observed_outputs"]
commands = [{
"type": "replay_conformance_report",
"outcome": report["outcome"],
"sdk_identity_error": observed.get("sdk_identity_error"),
"declared_sdk_version": observed.get("declared_sdk_version"),
"published_install_tuple_proven": observed["published_install_tuple_proven"],
}]
_assert_matches(fixture["command_sequence"], commands, f"{fixture['id']}.command_sequence")
_assert_matches(fixture["expected"], {"command_sequence": commands}, f"{fixture['id']}.expected")
assert report["implementation_identity"]["version"] == metadata.version("durable-workflow")
return commands
assert workflow_type in WORKFLOW_TYPES, (
f"replay fixture workflow {workflow_type!r} has no Python implementation; "
"register its reproducer workflow in WORKFLOWS"
Expand Down Expand Up @@ -489,6 +512,9 @@ def test_checked_in_replay_regression_corpus_uses_official_replayer(
assert isinstance(workflow, dict)
expected = fixture.get("expected")
assert isinstance(expected, dict) and expected
if workflow.get("type") == "sdk.replay-conformance-report":
_execute_fixture(fixture)
return
expected_error = expected.get("error")
expected_replay_error = fixture.get("expected_replay_error")
if isinstance(expected_replay_error, dict):
Expand Down
Loading