Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
69ca3ca
Decode canonical cooperative cancellation identity and ranges
durable-workflow-ops Sep 30, 2026
18ec381
Replay canonical cancellation at durable calls and recover cleanup
durable-workflow-ops Sep 30, 2026
a3c909c
Add fenced cooperative cancellation request and delivery client APIs
durable-workflow-ops Sep 30, 2026
febb590
Connect cooperative cancellation delivery to Worker replay and local …
durable-workflow-ops Oct 1, 2026
797e393
Drain cooperative local cleanup and fence it at shutdown deadline
durable-workflow-ops Oct 1, 2026
119747b
Qualify cooperative cancellation against a connected candidate Server
durable-workflow-ops Oct 1, 2026
8c6bd87
Supervise cooperative remote activity callbacks with bounded ownershi…
durable-workflow-ops Oct 1, 2026
9f36749
Use the SDK ServerError status in connected publication fence assertions
durable-workflow-ops Oct 1, 2026
88f0e31
Finish fenced remote claims without delaying worker shutdown on obser…
durable-workflow-ops Oct 1, 2026
ec794e2
Qualify real Python activity process death and attempt reclaim
durable-workflow-ops Oct 1, 2026
b3c45b6
Prove expired attempt remains unchanged after heartbeat
durable-workflow-ops Oct 1, 2026
e69cf85
Use Worker backpressure policy in connected claim fixture
durable-workflow-ops Oct 1, 2026
07e2030
test: align repair cadence and fixture imports
durable-workflow-ops Oct 1, 2026
2b28cca
fix: defer Python parent claims while child cleanup waits
durable-workflow-ops Oct 1, 2026
1763e72
feat: expose immutable cancellation context in Python replay
durable-workflow-ops Oct 1, 2026
aeb54ff
Preserve typed child cancellation policies in commands and cold replay
durable-workflow-ops Oct 1, 2026
c17ee56
Validate original remote cancellation stop receipts in the Python client
durable-workflow-ops Oct 2, 2026
07b2fb0
Own cooperative activity callbacks in independent spawn supervisors
durable-workflow-ops Oct 2, 2026
16ae1f0
Retain exact Native overlay provenance in Python source qualification
durable-workflow-ops Oct 2, 2026
19febbf
Stop and join cooperative Python remote callbacks before acknowledgin…
durable-workflow-ops Oct 2, 2026
edf890a
Prove connected Python callback stops and original cancellation receipts
durable-workflow-ops Oct 2, 2026
003ff07
Compare public cancellation history by immutable receipt payload
durable-workflow-ops Oct 2, 2026
8eabc65
Add source prepared local activity transport with original claim budgets
durable-workflow-ops Oct 2, 2026
5150044
feat: admit and supervise prepared local callbacks
durable-workflow-ops Oct 2, 2026
0a7c68b
Consume atomic prepared local activity groups with joined cancellation
durable-workflow-ops Oct 2, 2026
e256cbd
Retain prepared group refusals and mixed admission history
durable-workflow-ops Oct 2, 2026
f927f69
Preserve prepared application heartbeat progress
durable-workflow-ops Oct 2, 2026
a6b243e
Use the canonical prepared heartbeat details envelope
durable-workflow-ops Oct 2, 2026
f740388
Accept activity cancellation waits with explicit claim release
durable-workflow-ops Oct 2, 2026
bf93195
Format pending cancellation response cases
durable-workflow-ops Oct 2, 2026
de0c476
Preserve remote activity cancellation policies through Python replay
durable-workflow-ops Oct 2, 2026
8c1e937
Add deterministic cancellation remaining time during replay
durable-workflow-ops Oct 2, 2026
0e1c427
Qualify remaining budgets across prepared cleanup recovery
durable-workflow-ops Oct 2, 2026
afa7cfa
Support prepared local Activity cancellation policies
durable-workflow-ops Oct 3, 2026
fce32d5
Accept historical local policy omission in qualification
durable-workflow-ops Oct 3, 2026
d41e76d
Format historical policy qualification assertion
durable-workflow-ops Oct 3, 2026
928ded1
Keep configured poll windows separate from HTTP grace
durable-workflow-ops Oct 3, 2026
6b98cdf
Merge published Python 2.3.8 polling fix into cancellation candidate
durable-workflow-ops Oct 3, 2026
3cddb86
Refuse unqualified cancellation scope replay before Python applicatio…
durable-workflow-ops Oct 3, 2026
b086c44
Refuse unqualified scoped delivery replay
durable-workflow-ops Oct 3, 2026
363ade8
Format scoped delivery admission markers
durable-workflow-ops Oct 3, 2026
4a33482
Preserve scoped cancellation origins in child run contexts
durable-workflow-ops Oct 4, 2026
3c77a01
Merge remote-tracking branch 'origin/main' into feat/cooperative-canc…
durable-workflow-ops Oct 4, 2026
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
79 changes: 73 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ on:
pull_request:
branches: [main]
workflow_dispatch:
inputs:
server_commit:
description: Exact public Server candidate SHA, or empty for its default branch
type: string
default: ""
cooperative_qualification:
description: Qualify the candidate cooperative protocol 1.20
type: boolean
default: false
native_commit:
description: Optional exact public Native source overlay SHA for candidate qualification
type: string
default: ""

permissions:
contents: read
Expand Down Expand Up @@ -145,6 +158,11 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 25
needs: [lint, test]
env:
DURABLE_WORKFLOW_WORKER_PROTOCOL_VERSION: ${{ inputs.cooperative_qualification && '1.20' || '1.19' }}
DURABLE_WORKFLOW_COOPERATIVE_QUALIFICATION: ${{ inputs.cooperative_qualification && '1' || '0' }}
DURABLE_WORKFLOW_NATIVE_SOURCE: ${{ github.workspace }}/native
COMPOSE_FILE: docker-compose.test.yml
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
Expand All @@ -153,7 +171,32 @@ jobs:
with:
python-version: "3.12"
- name: Check out public Server integration source
run: python sdk-python/scripts/ci/checkout-public-repository.py server server
env:
SERVER_COMMIT: ${{ inputs.server_commit }}
run: python sdk-python/scripts/ci/checkout-public-repository.py server server --commit "$SERVER_COMMIT"
- name: Require exact cooperative candidate identities
env:
COOPERATIVE_REQUESTED: ${{ inputs.cooperative_qualification }}
SERVER_COMMIT: ${{ inputs.server_commit }}
NATIVE_COMMIT: ${{ inputs.native_commit }}
run: |
if [ "$COOPERATIVE_REQUESTED" = true ]; then
[[ "$SERVER_COMMIT" =~ ^[0-9a-f]{40}$ ]]
fi
if [ -n "$NATIVE_COMMIT" ]; then
test "$COOPERATIVE_REQUESTED" = true
[[ "$NATIVE_COMMIT" =~ ^[0-9a-f]{40}$ ]]
fi
- name: Check out optional exact public Native source
if: ${{ inputs.native_commit != '' }}
env:
NATIVE_COMMIT: ${{ inputs.native_commit }}
run: python sdk-python/scripts/ci/checkout-public-repository.py workflow native --commit "$NATIVE_COMMIT"
- name: Select readonly Native source qualification
if: ${{ inputs.native_commit != '' }}
run: |
printf 'COMPOSE_FILE=docker-compose.test.yml:docker-compose.native-cancellation.yml\n' >> "$GITHUB_ENV"
printf 'DURABLE_WORKFLOW_NATIVE_SOURCE_QUALIFICATION=1\n' >> "$GITHUB_ENV"
- run: pip install -e '.[dev]'
working-directory: sdk-python
- name: Configure isolated Docker project
Expand All @@ -162,31 +205,55 @@ jobs:
- name: Start Server stack
working-directory: sdk-python
run: |
docker compose --project-name "$COMPOSE_PROJECT_NAME" -f docker-compose.test.yml \
docker compose --project-name "$COMPOSE_PROJECT_NAME" \
up -d --build --wait --timeout 300
- name: Retain exact source and published image authority
working-directory: sdk-python
env:
NATIVE_COMMIT: ${{ inputs.native_commit }}
run: |
docker compose exec -T server cat /app/.package-provenance > integration-package-provenance.txt
docker compose images --format json > integration-images.json
jq --null-input --arg sdk "$GITHUB_SHA" --arg server "$(git -C ../server rev-parse HEAD)" --arg native "$NATIVE_COMMIT" \
'{qualification:"source",sdk_commit:$sdk,server_commit:$server,native_source_overlay:($native | if length > 0 then . else null end)}' \
> integration-source-provenance.json
- name: Select and probe integration endpoint
working-directory: sdk-python
run: python scripts/ci/configure-integration-endpoint.py
- name: Run integration tests
shell: bash
working-directory: sdk-python
env:
DURABLE_WORKFLOW_AUTH_TOKEN: test-token
run: pytest tests/integration/ -v
run: pytest tests/integration/ -v --capture=tee-sys --junitxml=integration-results.xml 2>&1 | tee integration-scenarios.log
- name: Retain connected scenario results
if: ${{ always() && github.server_url == 'https://github.com' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: connected-integration-${{ github.sha }}
path: |
sdk-python/integration-results.xml
sdk-python/integration-scenarios.log
sdk-python/integration-source-provenance.json
sdk-python/integration-package-provenance.txt
sdk-python/integration-images.json
if-no-files-found: warn
retention-days: ${{ inputs.cooperative_qualification && 90 || 7 }}
- name: Emit integration diagnostics
if: failure()
working-directory: sdk-python
run: |
for service in bootstrap server worker; do
for service in bootstrap server worker repair; do
echo "::group::$service logs"
docker compose --project-name "$COMPOSE_PROJECT_NAME" -f docker-compose.test.yml \
docker compose --project-name "$COMPOSE_PROJECT_NAME" \
logs --no-color --tail 200 "$service" || true
echo "::endgroup::"
done
- name: Teardown
if: always()
working-directory: sdk-python
run: |
docker compose --project-name "$COMPOSE_PROJECT_NAME" -f docker-compose.test.yml \
docker compose --project-name "$COMPOSE_PROJECT_NAME" \
down -v --rmi local

target-branch-qualification:
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,26 @@ pytest tests/ -m "not integration"
Integration tests use Docker:

```bash
export COMPOSE_PROJECT_NAME=sdk-python-local
docker compose -f docker-compose.test.yml up -d --build --wait
pytest tests/integration/ -v
SERVER_PORT=$(docker compose -f docker-compose.test.yml port server 8080 | sed 's/.*://')
DURABLE_WORKFLOW_SERVER_URL="http://127.0.0.1:$SERVER_PORT" DURABLE_WORKFLOW_AUTH_TOKEN=test-token pytest tests/integration/ -v
docker compose -f docker-compose.test.yml down -v
```

Candidate cooperative cancellation qualification is explicit. In a manual CI
run, supply an exact public `server_commit` and set `cooperative_qualification`
to true. CI verifies that checkout, builds the candidate Server, enables protocol
1.20, runs the connected cases and retains JUnit, raw observations, image
authority and exact source provenance. An optional exact `native_commit` mounts
that public Native checkout read-only into the test stack. The image's published
Composer authority stays intact and the evidence identifies the source overlay.
These are source qualification runs. For a local
candidate, set `DURABLE_WORKFLOW_WORKER_PROTOCOL_VERSION=1.20` before starting
Compose and `DURABLE_WORKFLOW_COOPERATIVE_QUALIFICATION=1` for pytest. These cases
fail if the runtime does not discover the required capability. Ordinary CI
keeps protocol 1.19 and skips this unpublished feature's connected cases.

## License

[MIT](LICENSE)
14 changes: 14 additions & 0 deletions docker-compose.native-cancellation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Source qualification only. Published Composer authority stays in the image.
services:
bootstrap:
volumes:
- ${DURABLE_WORKFLOW_NATIVE_SOURCE:?exact Native checkout}:/app/vendor/durable-workflow/workflow:ro
server:
volumes:
- ${DURABLE_WORKFLOW_NATIVE_SOURCE:?exact Native checkout}:/app/vendor/durable-workflow/workflow:ro
worker:
volumes:
- ${DURABLE_WORKFLOW_NATIVE_SOURCE:?exact Native checkout}:/app/vendor/durable-workflow/workflow:ro
repair:
volumes:
- ${DURABLE_WORKFLOW_NATIVE_SOURCE:?exact Native checkout}:/app/vendor/durable-workflow/workflow:ro
12 changes: 12 additions & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ services:
WORKFLOW_SERVER_AUTH_DRIVER: token
WORKFLOW_SERVER_AUTH_TOKEN: "test-token"
DW_WORKFLOW_TASK_TIMEOUT: 10
# Candidate cooperative qualification is explicit. Published defaults remain 1.19.
DW_WORKER_PROTOCOL_VERSION: "${DURABLE_WORKFLOW_WORKER_PROTOCOL_VERSION:-1.19}"
depends_on:
mysql:
condition: service_healthy
Expand Down Expand Up @@ -70,6 +72,16 @@ services:
redis:
condition: service_healthy

repair:
build:
context: ../server
# Match the production scheduler's unscoped, unthrottled repair cadence.
command: sh -c 'while true; do php artisan workflow:v2:repair-pass --json; sleep 10; done'
environment: *server-env
depends_on:
server:
condition: service_healthy

mysql:
image: mysql:8.0
environment:
Expand Down
Loading
Loading