Skip to content
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
cc6fd19
docs: add durable execution architecture decisions
j-d-ha Jul 29, 2026
291ee96
docs: clarify architecture decision records
j-d-ha Jul 29, 2026
f457075
docs: add examples to durable execution ADRs
j-d-ha Jul 29, 2026
5e416f1
docs: simplify durable execution ADRs
j-d-ha Jul 29, 2026
9f66adc
docs: add durable pipeline architecture decision
j-d-ha Jul 30, 2026
297e52d
docs: add durable execution architecture decisions
j-d-ha Jul 29, 2026
e2e515e
docs: clarify architecture decision records
j-d-ha Jul 29, 2026
cdc80ab
docs: add examples to durable execution ADRs
j-d-ha Jul 29, 2026
02295b6
docs: simplify durable execution ADRs
j-d-ha Jul 29, 2026
c9e9524
docs: pressure-test durable pipeline ADR
j-d-ha Jul 30, 2026
8718d2c
docs: clarify durable envelope ownership
j-d-ha Jul 30, 2026
41e8a45
docs: accept durable pipeline architecture decision
j-d-ha Jul 30, 2026
23e4f84
chore(deps): pin durable execution packages
j-d-ha Jul 31, 2026
e593dd5
feat(core): expose invocation serializer
j-d-ha Jul 31, 2026
3358bb3
feat(core): enforce durable terminal lifecycle
j-d-ha Jul 31, 2026
e071986
docs: finalize durable handler contract
j-d-ha Jul 31, 2026
c71342c
feat: add durable execution package
j-d-ha Jul 31, 2026
3ef1467
feat: add durable handler API
j-d-ha Jul 31, 2026
c7c84b7
feat(source-generators): recognize durable handlers
j-d-ha Jul 31, 2026
48a1000
feat(source-generators): validate durable handlers
j-d-ha Jul 31, 2026
f09a991
feat(source-generators): emit durable adapters
j-d-ha Jul 31, 2026
8718423
test(source-generators): expand durable coverage
j-d-ha Jul 31, 2026
cdc0e00
test(core): verify durable terminal lifecycle
j-d-ha Jul 31, 2026
2f81fab
test(testing): add durable host integration
j-d-ha Jul 31, 2026
a32943f
test(aot): add durable publish smoke
j-d-ha Jul 31, 2026
0eab077
test(durable): verify public runtime APIs
j-d-ha Jul 31, 2026
6afab16
test(durable): cover workflow replay semantics
j-d-ha Jul 31, 2026
975a876
docs(examples): add durable execution sample
j-d-ha Jul 31, 2026
b3febc5
docs(durable): document package usage
j-d-ha Jul 31, 2026
7071841
test(packaging): add durable consumer matrix
j-d-ha Jul 31, 2026
05e80ab
docs: add durable execution guide
j-d-ha Jul 31, 2026
495f902
docs(durable): add deployment recipes
j-d-ha Aug 1, 2026
deecdbf
docs(decisions): defer durable templates
j-d-ha Aug 1, 2026
28283d8
ci: isolate durable package releases
j-d-ha Aug 1, 2026
5cb9889
chore(deps): update validation toolchain
j-d-ha Aug 1, 2026
e32641b
test(durable): support debug fallback assertion
j-d-ha Aug 1, 2026
8315958
chore(deps): update package versions in shared props file
j-d-ha Aug 1, 2026
54b7570
Merge branch 'main' into feat/add-durable-function-support
j-d-ha Aug 1, 2026
6eca813
fix: address durable execution review feedback
j-d-ha Aug 2, 2026
f72d8f4
fix(tests): suppress NU1608 warning for AutoFixture.AutoNSubstitute
j-d-ha Aug 2, 2026
952ecda
chore(deps): fix formatting in shared props file
j-d-ha Aug 2, 2026
7134d2f
fix(tests): add null-forgiving operator to suppress nullable warnings
j-d-ha Aug 2, 2026
edaec83
test(source-generators): add multi-targeting support and conditional …
j-d-ha Aug 2, 2026
36e4b9e
test(tests): guard compatibility package references
j-d-ha Aug 3, 2026
e6788a4
fix(tests): adjust lambda middleware formatting in unit test
j-d-ha Aug 3, 2026
e179845
refactor(durable)!: simplify durable handler adapter
j-d-ha Aug 3, 2026
1119938
test(aot): root libraries for publish analysis
j-d-ha Aug 3, 2026
309386b
refactor(durable)!: relax handler signature validation
j-d-ha Aug 3, 2026
f8f9cfb
fix(durable): bind cancellation tokens and reject duplicate events
j-d-ha Aug 3, 2026
00bf154
fix(source-generators): harden durable handler validation
j-d-ha Aug 3, 2026
795c63c
fix(source-generators): harden durable handler adapters
j-d-ha Aug 3, 2026
1dae505
fix(durable): address review findings
j-d-ha Aug 4, 2026
bada7a9
ci: restore DevOps template workflows
j-d-ha Aug 4, 2026
fde51be
ci(github): document DevOps template policy
j-d-ha Aug 4, 2026
c284488
ci: unify package releases
j-d-ha Aug 4, 2026
ebe2365
test(tests): restore multi-target integration coverage
j-d-ha Aug 4, 2026
88c7aa7
ci(github): stabilize required PR build check
j-d-ha Aug 4, 2026
11b2f55
ci(github): remove duplicate PR build
j-d-ha Aug 4, 2026
a228cb8
docs: preserve contributing formatting
j-d-ha Aug 4, 2026
c442e2e
feat(caveman-compress): add natural language compression tools
j-d-ha Aug 4, 2026
add3b43
ci(github): upgrade DevOps template workflows
j-d-ha Aug 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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tests/MinimalLambda.SourceGenerators.UnitTests/Snapshots/*.verified.cs whitespace=-trailing-space
4 changes: 3 additions & 1 deletion .github/AGENTS.md
Comment thread
j-d-ha marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Examples:
- Do not publish NuGet packages manually.
- Do not create GitHub releases directly.

Packages version synchronously:
Core packages version synchronously:

- `MinimalLambda`
- `MinimalLambda.Abstractions`
Expand All @@ -71,3 +71,5 @@ Packages version synchronously:
- `MinimalLambda.OpenTelemetry`
- `MinimalLambda.Templates`
- `MinimalLambda.Testing`

`MinimalLambda.DurableExecution` publishes independently from `durable-v<semver>` releases. Its package declares minimum compatible core version.
4 changes: 2 additions & 2 deletions .github/workflows/pr-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ concurrency:

jobs:
build:
uses: LayeredCraft/devops-templates/.github/workflows/pr-build.yaml@v10.3
Comment thread
j-d-ha marked this conversation as resolved.
uses: LayeredCraft/devops-templates/.github/workflows/pr-build.yaml@fa7aa89e829a2b7bb947f5c38ba456189831534a # v10.3
with:
solution: MinimalLambda.sln
dotnetVersion: 11.0.x
Comment thread
j-d-ha marked this conversation as resolved.
dotnetVersion: 11.0.100-preview.6.26359.118
buildConfiguration: Release
hasTests: false
useMtpRunner: true
Expand Down
25 changes: 19 additions & 6 deletions .github/workflows/pr-quality.yaml
Comment thread
j-d-ha marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ jobs:
quality:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 25

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
dotnet-version: 11.0.x
dotnet-version: 11.0.100-preview.6.26359.118

- name: Restore dependencies
run: dotnet restore MinimalLambda.sln
Expand All @@ -39,24 +39,37 @@ jobs:
run: dotnet tool restore

- name: Install Task
uses: go-task/setup-task@v2
uses: go-task/setup-task@01a4adf9db2d14c1de7a560f09170b6e0df736aa # v2

- name: Build
run: dotnet build MinimalLambda.sln --no-restore --configuration Release /p:TreatWarningsAsErrors=true

- name: Validate AOT compatibility
run: task build:aot-check

- name: Validate package compatibility
run: task build:package-compat

- name: Validate release artifact validator
env:
PYTHONDONTWRITEBYTECODE: '1'
run: python3 scripts/test-release-artifact-validator.py

- name: Run coverage
timeout-minutes: 10
Comment thread
j-d-ha marked this conversation as resolved.
run: |
dotnet test tests/MinimalLambda.UnitTests/MinimalLambda.UnitTests.csproj --framework net8.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.Envelopes.UnitTests/MinimalLambda.Envelopes.UnitTests.csproj --framework net8.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj --framework net8.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj --framework net9.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj --framework net10.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.SourceGenerators.UnitTests/MinimalLambda.SourceGenerators.UnitTests.csproj --framework net11.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.DurableExecution.UnitTests/MinimalLambda.DurableExecution.UnitTests.csproj --framework net10.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.Testing.UnitTests/Tests/MinimalLambda.Testing.UnitTests/MinimalLambda.Testing.UnitTests.csproj --framework net10.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi
dotnet test tests/MinimalLambda.OpenTelemetry.UnitTests/MinimalLambda.OpenTelemetry.UnitTests.csproj --framework net9.0 --configuration Release --no-build --results-directory ./coverage --coverage --coverage-output-format cobertura --no-progress --no-ansi

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v6
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
with:
files: ./coverage/**/*.cobertura.xml
disable_search: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-title-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ concurrency:

jobs:
pr-title-check:
uses: LayeredCraft/devops-templates/.github/workflows/pr-title-check.yml@v10.3
uses: LayeredCraft/devops-templates/.github/workflows/pr-title-check.yml@fa7aa89e829a2b7bb947f5c38ba456189831534a # v10.3
171 changes: 171 additions & 0 deletions .github/workflows/publish-durable-release.yaml
Comment thread
j-d-ha marked this conversation as resolved.
Outdated
Comment thread
j-d-ha marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
name: Publish Durable Release

on:
release:
types: [published]

permissions:
contents: read

jobs:
preflight:
if: startsWith(github.event.release.tag_name, 'durable-v')
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- name: Guard and resolve durable release tag
id: version
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
shell: bash
run: |
python3 scripts/validate-release-artifacts.py \
--lane durable \
--tag "$RELEASE_TAG" \
--tag-only
echo "version=${RELEASE_TAG#durable-v}" >> "$GITHUB_OUTPUT"

build:
needs: preflight
if: startsWith(github.event.release.tag_name, 'durable-v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
dotnet-version: 11.0.100-preview.6.26359.118
- name: Restore durable project
run: dotnet restore src/MinimalLambda.DurableExecution/MinimalLambda.DurableExecution.csproj
- name: Build durable project
run: >-
dotnet build src/MinimalLambda.DurableExecution/MinimalLambda.DurableExecution.csproj
--configuration Release
--no-restore
-p:Version="${{ needs.preflight.outputs.version }}"
-p:InformationalVersion="${{ needs.preflight.outputs.version }}"
- name: Pack durable project
run: >-
dotnet pack src/MinimalLambda.DurableExecution/MinimalLambda.DurableExecution.csproj
--configuration Release
--no-build
--output artifacts
-p:Version="${{ needs.preflight.outputs.version }}"
-p:InformationalVersion="${{ needs.preflight.outputs.version }}"
- name: Guard durable release manifest
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: >-
python3 scripts/validate-release-artifacts.py
--lane durable
--tag "$RELEASE_TAG"
--artifacts artifacts
- name: Restore package-only consumer from local durable artifact and published core minimum
env:
NUGET_PACKAGES: ${{ runner.temp }}/durable-pre-push-packages
shell: bash
run: |
set -euo pipefail
core_version=$(sed -n 's:.*<MinimalLambdaMinimumVersion>\([^<]*\)</MinimalLambdaMinimumVersion>.*:\1:p' src/MinimalLambda.DurableExecution/MinimalLambda.DurableExecution.csproj)
test -n "$core_version"
consumer="$RUNNER_TEMP/durable-pre-push-consumer"
mkdir "$consumer"
cd "$consumer"
dotnet new classlib --framework net10.0 --no-restore
dotnet add package MinimalLambda --version "[$core_version]" --no-restore
dotnet add package MinimalLambda.DurableExecution --version "[${{ needs.preflight.outputs.version }}]" --no-restore
dotnet restore --no-cache \
--source "$GITHUB_WORKSPACE/artifacts" \
--source https://api.nuget.org/v3/index.json
durable_version="${{ needs.preflight.outputs.version }}"
metadata="$NUGET_PACKAGES/minimallambda.durableexecution/${durable_version,,}/.nupkg.metadata"
python3 - "$metadata" "$GITHUB_WORKSPACE/artifacts" <<'PY'
import json
import pathlib
import sys

metadata = json.loads(pathlib.Path(sys.argv[1]).read_text())
actual = pathlib.Path(metadata["source"]).resolve()
expected = pathlib.Path(sys.argv[2]).resolve()
if actual != expected:
raise SystemExit(f"durable package restored from {actual}, expected local feed {expected}")
PY
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: durable-nuget-packages
path: |
artifacts/*.nupkg
artifacts/*.snupkg
if-no-files-found: error

push:
needs: [preflight, build]
if: startsWith(github.event.release.tag_name, 'durable-v')
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: durable-nuget-packages
path: artifacts
- name: Recheck manifest and reject existing NuGet version
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: >-
python3 scripts/validate-release-artifacts.py
--lane durable
--tag "$RELEASE_TAG"
--artifacts artifacts
--check-nuget
- name: Exchange GitHub OIDC token for temporary NuGet key
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1
id: nuget-login
with:
user: ${{ secrets.NUGET_USER }}
- name: Publish durable package and symbols without duplicate suppression
env:
NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
shell: bash
run: |
set -euo pipefail
# dotnet nuget push discovers and publishes adjacent .snupkg automatically.
for package in artifacts/*.nupkg; do
dotnet nuget push "$package" \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json
done

validate-published-pair:
needs: [preflight, push]
if: startsWith(github.event.release.tag_name, 'durable-v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
dotnet-version: 11.0.100-preview.6.26359.118
- name: Restore exact published core and durable pair
shell: bash
run: |
set -euo pipefail
core_version=$(sed -n 's:.*<MinimalLambdaMinimumVersion>\([^<]*\)</MinimalLambdaMinimumVersion>.*:\1:p' src/MinimalLambda.DurableExecution/MinimalLambda.DurableExecution.csproj)
test -n "$core_version"
consumer="$RUNNER_TEMP/durable-published-consumer"
mkdir "$consumer"
cd "$consumer"
dotnet new classlib --framework net10.0 --no-restore
dotnet add package MinimalLambda --version "[$core_version]" --no-restore
dotnet add package MinimalLambda.DurableExecution --version "[${{ needs.preflight.outputs.version }}]" --no-restore
for attempt in {1..10}; do
if dotnet restore --no-cache --source https://api.nuget.org/v3/index.json; then
exit 0
fi
if [[ "$attempt" == 10 ]]; then
exit 1
fi
sleep 30
done
111 changes: 97 additions & 14 deletions .github/workflows/publish-preview.yaml
Comment thread
j-d-ha marked this conversation as resolved.
Comment thread
j-d-ha marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Preview
name: Publish Core Preview

on:
push:
Expand All @@ -17,25 +17,108 @@ permissions:
contents: read

jobs:
build:
resolve:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
uses: LayeredCraft/devops-templates/.github/workflows/publish-preview.yml@v10.3
Comment thread
j-d-ha marked this conversation as resolved.
with:
solution: MinimalLambda.sln
dotnetVersion: 11.0.x
prereleaseIdentifier: preview
hasTests: false
buildConfiguration: Release
contents: read
pull-requests: read
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Resolve next version via Release Drafter
id: release-drafter
uses: release-drafter/release-drafter@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7
with:
config-name: release-drafter.yml
dry-run: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve core preview version
id: version
env:
RESOLVED_VERSION: ${{ steps.release-drafter.outputs.resolved_version }}
shell: bash
run: |
if [[ -z "$RESOLVED_VERSION" ]]; then
echo "::error::Release Drafter returned an empty resolved_version."
exit 1
fi
base="${RESOLVED_VERSION%%-*}"
version="${base}-preview.${{ github.run_number }}"
echo "version=$version" >> "$GITHUB_OUTPUT"

build:
needs: resolve
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
dotnet-version: 11.0.100-preview.6.26359.118
- name: Restore core package cohort
run: dotnet restore MinimalLambda.Packages.slnf
- name: Build core package cohort
run: >-
dotnet build MinimalLambda.Packages.slnf
--configuration Release
--no-restore
-p:Version="${{ needs.resolve.outputs.version }}"
- name: Pack core package cohort
run: >-
dotnet pack MinimalLambda.Packages.slnf
--configuration Release
--no-build
--output artifacts
-p:Version="${{ needs.resolve.outputs.version }}"
- name: Guard core preview manifest
run: >-
python3 scripts/validate-release-artifacts.py
--lane core-preview
--expected-version "${{ needs.resolve.outputs.version }}"
--preview-run-number "${{ github.run_number }}"
--artifacts artifacts
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: core-preview-nuget-packages
path: |
artifacts/*.nupkg
artifacts/*.snupkg
if-no-files-found: error

push:
needs: build
needs: [resolve, build]
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: LayeredCraft/devops-templates/.github/actions/nuget-push@v10.3
Comment thread
j-d-ha marked this conversation as resolved.
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: core-preview-nuget-packages
path: artifacts
- name: Recheck manifest and reject existing NuGet versions
run: >-
python3 scripts/validate-release-artifacts.py
--lane core-preview
--expected-version "${{ needs.resolve.outputs.version }}"
--preview-run-number "${{ github.run_number }}"
--artifacts artifacts
--check-nuget
- name: Exchange GitHub OIDC token for temporary NuGet key
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1
id: nuget-login
with:
nuget_user: ${{ secrets.NUGET_USER }}
user: ${{ secrets.NUGET_USER }}
- name: Publish core preview packages and symbols without duplicate suppression
env:
NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
shell: bash
run: |
set -euo pipefail
# dotnet nuget push discovers and publishes adjacent .snupkg automatically.
for package in artifacts/*.nupkg; do
dotnet nuget push "$package" \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json
done
Loading
Loading