From 218ed825edd80c983bddd7329ce2622ad60e8160 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 9 Jan 2026 14:42:55 +0000
Subject: [PATCH 01/58] chore(deps): update dependency reqnroll.xunit to 3.3.1
(#683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index b856f939..bbdb9186 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -43,7 +43,7 @@
-
+
From 6b99bc3eaf1be97f2a4eeedea282f33165fd3239 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 15 Jan 2026 16:06:52 +0000
Subject: [PATCH 02/58] chore(deps): update actions/setup-dotnet digest to
baa11fb (#684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/aot-compatibility.yml | 2 +-
.github/workflows/ci.yml | 4 ++--
.github/workflows/code-coverage.yml | 2 +-
.github/workflows/dotnet-format.yml | 2 +-
.github/workflows/e2e.yml | 2 +-
.github/workflows/release.yml | 2 +-
6 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/aot-compatibility.yml b/.github/workflows/aot-compatibility.yml
index 4ba1698c..34217081 100644
--- a/.github/workflows/aot-compatibility.yml
+++ b/.github/workflows/aot-compatibility.yml
@@ -52,7 +52,7 @@ jobs:
submodules: recursive
- name: Setup .NET SDK
- uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
+ uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
with:
global-json-file: global.json
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d92c7a84..8d4ea0f3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,7 +29,7 @@ jobs:
submodules: recursive
- name: Setup .NET SDK
- uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
+ uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
with:
global-json-file: global.json
@@ -73,7 +73,7 @@ jobs:
submodules: recursive
- name: Setup .NET SDK
- uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
+ uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
with:
global-json-file: global.json
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index c221b591..3234c041 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -27,7 +27,7 @@ jobs:
fetch-depth: 0
- name: Setup .NET SDK
- uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
+ uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
with:
global-json-file: global.json
diff --git a/.github/workflows/dotnet-format.yml b/.github/workflows/dotnet-format.yml
index e4326e04..313230e7 100644
--- a/.github/workflows/dotnet-format.yml
+++ b/.github/workflows/dotnet-format.yml
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup .NET SDK
- uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
+ uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
with:
global-json-file: global.json
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index fa02626e..196a2dc3 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -22,7 +22,7 @@ jobs:
fetch-depth: 0
- name: Setup .NET SDK
- uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
+ uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
with:
global-json-file: global.json
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9fe639f3..cc14db04 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -44,7 +44,7 @@ jobs:
fetch-depth: 0
- name: Setup .NET SDK
- uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5
+ uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
with:
global-json-file: global.json
From 3d5ce76da2cb76915b326d320b1b1f3545cb6362 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 15 Jan 2026 16:07:09 +0000
Subject: [PATCH 03/58] chore(deps): update github/codeql-action digest to
cdefb33 (#685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/codeql-analysis.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index af80cac1..4f9af0e4 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
+ uses: github/codeql-action/init@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
+ uses: github/codeql-action/autobuild@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4
+ uses: github/codeql-action/analyze@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4
From bf102f4a7a9b3b495b6e13d3fa575a34f6a9a32c Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 15 Jan 2026 16:07:38 +0000
Subject: [PATCH 04/58] chore(deps): update dependency reqnroll.xunit to 3.3.2
(#686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index bbdb9186..90dd8322 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -43,7 +43,7 @@
-
+
From 0847724f75ec9995d5204515e0f5d9276c6c8562 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 22 Jan 2026 08:03:38 +0000
Subject: [PATCH 05/58] chore(deps): update spec digest to df30ead (#687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/spec b/spec
index 400fa303..df30ead6 160000
--- a/spec
+++ b/spec
@@ -1 +1 @@
-Subproject commit 400fa3038d4469f0cbf77af3e79fedda406afc21
+Subproject commit df30ead675391ddbcd388a015aae6b40447ac420
From c5a95543b20e751a46949f85e4bbed6aa9984e9f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 22 Jan 2026 08:04:38 +0000
Subject: [PATCH 06/58] chore(deps): update actions/cache action to v5.0.2
(#688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/aot-compatibility.yml | 2 +-
.github/workflows/ci.yml | 4 ++--
.github/workflows/code-coverage.yml | 2 +-
.github/workflows/e2e.yml | 2 +-
.github/workflows/release.yml | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/aot-compatibility.yml b/.github/workflows/aot-compatibility.yml
index 34217081..b1afd6c2 100644
--- a/.github/workflows/aot-compatibility.yml
+++ b/.github/workflows/aot-compatibility.yml
@@ -57,7 +57,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-${{ matrix.arch }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8d4ea0f3..3b9ec81c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
@@ -78,7 +78,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index 3234c041..341b2d66 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -32,7 +32,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 196a2dc3..10abc2a5 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -27,7 +27,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cc14db04..29d9bbba 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -49,7 +49,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
From 4047da0069ad9ee41d9c26f3f575fb314b78e6ad Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 22 Jan 2026 08:05:00 +0000
Subject: [PATCH 07/58] chore(deps): update dependency
opentelemetry.instrumentation.aspnetcore to 1.15.0 (#689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
samples/AspNetCore/Samples.AspNetCore.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/AspNetCore/Samples.AspNetCore.csproj b/samples/AspNetCore/Samples.AspNetCore.csproj
index 723bbd4b..f98b1ba5 100644
--- a/samples/AspNetCore/Samples.AspNetCore.csproj
+++ b/samples/AspNetCore/Samples.AspNetCore.csproj
@@ -14,7 +14,7 @@
-
+
From 120d12d498037585b3a40e9a3f18e3efa25e836b Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 26 Jan 2026 07:58:16 +0000
Subject: [PATCH 08/58] chore(deps): update github/codeql-action digest to
19b2f06 (#690)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/codeql-analysis.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 4f9af0e4..32b20b69 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4
+ uses: github/codeql-action/init@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4
+ uses: github/codeql-action/autobuild@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4
+ uses: github/codeql-action/analyze@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4
From 097c80edc139918ce286779eb3032ec86d6b33d3 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 26 Jan 2026 07:58:32 +0000
Subject: [PATCH 09/58] chore(deps): update spec digest to 1115b78 (#691)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/spec b/spec
index df30ead6..1115b785 160000
--- a/spec
+++ b/spec
@@ -1 +1 @@
-Subproject commit df30ead675391ddbcd388a015aae6b40447ac420
+Subproject commit 1115b785d2cd08d16d63e96bdb6a0ac78a65e6ee
From 4a29a23f609530877a80ba73a1c82c1b2b8ccb1f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 26 Jan 2026 07:58:57 +0000
Subject: [PATCH 10/58] chore(deps): update opentelemetry-dotnet monorepo to
1.15.0 (#692)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Directory.Packages.props | 4 ++--
samples/AspNetCore/Samples.AspNetCore.csproj | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 90dd8322..119e21c3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -41,8 +41,8 @@
-
-
+
+
diff --git a/samples/AspNetCore/Samples.AspNetCore.csproj b/samples/AspNetCore/Samples.AspNetCore.csproj
index f98b1ba5..aa37b6e6 100644
--- a/samples/AspNetCore/Samples.AspNetCore.csproj
+++ b/samples/AspNetCore/Samples.AspNetCore.csproj
@@ -13,9 +13,9 @@
-
+
-
+
From 2072376bfa194ba3a262a11565ca04bb727a55c8 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 28 Jan 2026 20:27:53 +0000
Subject: [PATCH 11/58] chore(deps): update github/codeql-action digest to
b20883b (#694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/codeql-analysis.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 32b20b69..78fddef0 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4
+ uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4
+ uses: github/codeql-action/autobuild@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4
+ uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4
From aed93d2279a1f9e916775b0dc9a346f19da1c703 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 28 Jan 2026 20:28:12 +0000
Subject: [PATCH 12/58] chore(deps): update dependency reqnroll.xunit to 3.3.3
(#695)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 119e21c3..fc2457d7 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -43,7 +43,7 @@
-
+
From 4401e9d178baaa940b418fcf89dc4fbb970525e8 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 28 Jan 2026 20:28:27 +0000
Subject: [PATCH 13/58] chore(deps): update actions/attest-build-provenance
action to v3.2.0 (#696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/release.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 29d9bbba..d8cab21a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -73,7 +73,7 @@ jobs:
run: dotnet nuget push "src/**/*.nupkg" --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json
- name: Generate artifact attestation
- uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3.1.0
+ uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
with:
subject-path: "src/**/*.nupkg"
From 705a64962ab284b4ab2e8ae550b9fb65547d1858 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 4 Feb 2026 22:01:00 +0000
Subject: [PATCH 14/58] chore(deps): update actions/checkout digest to de0fac2
(#697)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/aot-compatibility.yml | 2 +-
.github/workflows/ci.yml | 4 ++--
.github/workflows/code-coverage.yml | 2 +-
.github/workflows/codeql-analysis.yml | 2 +-
.github/workflows/dotnet-format.yml | 2 +-
.github/workflows/e2e.yml | 2 +-
.github/workflows/release.yml | 2 +-
7 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/aot-compatibility.yml b/.github/workflows/aot-compatibility.yml
index b1afd6c2..92341877 100644
--- a/.github/workflows/aot-compatibility.yml
+++ b/.github/workflows/aot-compatibility.yml
@@ -46,7 +46,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
submodules: recursive
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3b9ec81c..7ef1cc20 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
submodules: recursive
@@ -67,7 +67,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
submodules: recursive
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index 341b2d66..fd9a70dd 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -22,7 +22,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 78fddef0..164382d5 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -38,7 +38,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
diff --git a/.github/workflows/dotnet-format.yml b/.github/workflows/dotnet-format.yml
index 313230e7..4bfbcb2a 100644
--- a/.github/workflows/dotnet-format.yml
+++ b/.github/workflows/dotnet-format.yml
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Check out code
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 10abc2a5..515e1e61 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -17,7 +17,7 @@ jobs:
contents: read
pull-requests: write
steps:
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d8cab21a..a64b815b 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,7 +39,7 @@ jobs:
if: ${{ fromJSON(needs.release-please.outputs.release_created || false) }}
steps:
- - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
From 150dad9a51667e1b0b88f8de408dfb016878ffb3 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 4 Feb 2026 22:01:26 +0000
Subject: [PATCH 15/58] chore(deps): update github/codeql-action digest to
6bc82e0 (#698)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/codeql-analysis.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 164382d5..f34bc281 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4
+ uses: github/codeql-action/init@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4
+ uses: github/codeql-action/autobuild@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4
+ uses: github/codeql-action/analyze@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4
From c78209d946c7806d9ae98230989d60dcd3ae3b38 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 4 Feb 2026 22:01:44 +0000
Subject: [PATCH 16/58] chore(deps): update actions/cache action to v5.0.3
(#699)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/aot-compatibility.yml | 2 +-
.github/workflows/ci.yml | 4 ++--
.github/workflows/code-coverage.yml | 2 +-
.github/workflows/e2e.yml | 2 +-
.github/workflows/release.yml | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/aot-compatibility.yml b/.github/workflows/aot-compatibility.yml
index 92341877..c6d8b9bb 100644
--- a/.github/workflows/aot-compatibility.yml
+++ b/.github/workflows/aot-compatibility.yml
@@ -57,7 +57,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
+ uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-${{ matrix.arch }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7ef1cc20..60845ab7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
+ uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
@@ -78,7 +78,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
+ uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index fd9a70dd..66b0707e 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -32,7 +32,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
+ uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 515e1e61..7953e381 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -27,7 +27,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
+ uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a64b815b..76869a40 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -49,7 +49,7 @@ jobs:
global-json-file: global.json
- name: Cache NuGet packages
- uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
+ uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props', 'global.json') }}
From f895540d9153db2b2d216f1eb9454376c03412a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Sat, 21 Feb 2026 07:40:22 +0000
Subject: [PATCH 17/58] ci: remove CodeQL analysis workflow file (#700)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
.github/workflows/codeql-analysis.yml | 72 ---------------------------
1 file changed, 72 deletions(-)
delete mode 100644 .github/workflows/codeql-analysis.yml
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index f34bc281..00000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,72 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches: [ "main" ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ "main" ]
- schedule:
- - cron: '19 5 * * 3'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'csharp', 'actions' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
- # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
-
- # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
- # queries: security-extended,security-and-quality
-
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4
-
- # ℹ️ Command-line programs to run using the OS shell.
- # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
-
- # If the Autobuild fails above, remove it and uncomment the following three lines.
- # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
-
- # - run: |
- # echo "Run, Build Application using script"
- # ./location_of_script_within_repo/buildscript.sh
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4
From 53152c35f0733f90347a6e9f64e06be83427f0bc Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat, 21 Feb 2026 08:21:43 +0000
Subject: [PATCH 18/58] chore(deps): update spec digest to eefdf43 (#701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/spec b/spec
index 1115b785..eefdf439 160000
--- a/spec
+++ b/spec
@@ -1 +1 @@
-Subproject commit 1115b785d2cd08d16d63e96bdb6a0ac78a65e6ee
+Subproject commit eefdf439c5a5b69ccde036c3c6959a4a6c17e08c
From 29fd9c167a41cee1099aff9618d40b24ef9bdce6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Silva?=
<2493377+askpt@users.noreply.github.com>
Date: Mon, 23 Feb 2026 17:04:43 +0000
Subject: [PATCH 19/58] refactor: improve code quality (#706)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* chore(tests): use 'using' declaration for CancellationTokenSource in test files
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
* chore(EventExecutor): make _defaultClientName readonly
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
* chore(InMemoryProvider): simplify flag initialization logic in constructor and UpdateFlagsAsync method
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
* chore(EventExecutor): streamline event handler removal logic
chore(LoggingHook): refine number value handling in logging
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---------
Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com>
---
src/OpenFeature/EventExecutor.cs | 10 ++++------
src/OpenFeature/Hooks/LoggingHook.cs | 4 ++--
.../Providers/Memory/InMemoryProvider.cs | 19 +++----------------
.../MultiProviderEventTests.cs | 2 +-
.../ProviderExtensionsTests.cs | 5 +++--
.../OpenFeatureClientTests.cs | 2 +-
.../OpenFeature.Tests/OpenFeatureHookTests.cs | 4 ++--
7 files changed, 16 insertions(+), 30 deletions(-)
diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs
index f65f41a1..8de21769 100644
--- a/src/OpenFeature/EventExecutor.cs
+++ b/src/OpenFeature/EventExecutor.cs
@@ -15,7 +15,7 @@ internal sealed partial class EventExecutor : IAsyncDisposable
private readonly List _activeSubscriptions = [];
/// placeholder for anonymous clients
- private static Guid _defaultClientName = Guid.NewGuid();
+ private static readonly Guid _defaultClientName = Guid.NewGuid();
private readonly Dictionary> _apiHandlers = [];
private readonly Dictionary>> _clientHandlers = [];
@@ -93,12 +93,10 @@ internal void RemoveClientHandler(string client, ProviderEventTypes type, EventH
lock (this._lockObj)
{
- if (this._clientHandlers.TryGetValue(clientName, out var clientEventHandlers))
+ if (this._clientHandlers.TryGetValue(clientName, out var clientEventHandlers)
+ && clientEventHandlers.TryGetValue(type, out var eventHandlers))
{
- if (clientEventHandlers.TryGetValue(type, out var eventHandlers))
- {
- eventHandlers.Remove(handler);
- }
+ eventHandlers.Remove(handler);
}
}
}
diff --git a/src/OpenFeature/Hooks/LoggingHook.cs b/src/OpenFeature/Hooks/LoggingHook.cs
index 77bf703f..e4e48727 100644
--- a/src/OpenFeature/Hooks/LoggingHook.cs
+++ b/src/OpenFeature/Hooks/LoggingHook.cs
@@ -153,11 +153,11 @@ public override string ToString()
if (value.IsBoolean)
return value.AsBoolean.ToString();
- if (value.IsNumber)
+ if (value.IsNumber && value.AsDouble != null)
{
// Value.AsDouble will attempt to cast other numbers to double
// There is an implicit conversation for int/long to double
- if (value.AsDouble != null) return value.AsDouble.ToString();
+ return value.AsDouble.ToString();
}
if (value.IsDateTime)
diff --git a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs
index 617a2ecb..46dce917 100644
--- a/src/OpenFeature/Providers/Memory/InMemoryProvider.cs
+++ b/src/OpenFeature/Providers/Memory/InMemoryProvider.cs
@@ -26,14 +26,7 @@ public override Metadata GetMetadata()
/// dictionary of Flags
public InMemoryProvider(IDictionary? flags = null)
{
- if (flags == null)
- {
- this._flags = new Dictionary();
- }
- else
- {
- this._flags = new Dictionary(flags); // shallow copy
- }
+ this._flags = flags == null ? [] : new Dictionary(flags); // shallow copy
}
///
@@ -43,14 +36,8 @@ public InMemoryProvider(IDictionary? flags = null)
public async Task UpdateFlagsAsync(IDictionary? flags = null)
{
var changed = this._flags.Keys.ToList();
- if (flags == null)
- {
- this._flags = new Dictionary();
- }
- else
- {
- this._flags = new Dictionary(flags); // shallow copy
- }
+ this._flags = flags == null ? [] : new Dictionary(flags); // shallow copy
+
changed.AddRange(this._flags.Keys.ToList());
var @event = new ProviderEventPayload
{
diff --git a/test/OpenFeature.Providers.MultiProvider.Tests/MultiProviderEventTests.cs b/test/OpenFeature.Providers.MultiProvider.Tests/MultiProviderEventTests.cs
index d41e91cf..bc792dd5 100644
--- a/test/OpenFeature.Providers.MultiProvider.Tests/MultiProviderEventTests.cs
+++ b/test/OpenFeature.Providers.MultiProvider.Tests/MultiProviderEventTests.cs
@@ -319,7 +319,7 @@ private static async Task EmitEventToProvider(FeatureProvider provider, Provider
private static async Task> ReadEvents(Channel
internal virtual ProviderStatus Status { get; set; } = ProviderStatus.NotReady;
+ ///
+ /// Tracks which Api instance this provider is currently bound to.
+ /// A provider should not be registered with more than one API instance simultaneously (spec 1.8.4).
+ ///
+ private Api? _boundApiInstance;
+
+ ///
+ /// Attempts to bind this provider to the given API instance.
+ /// Uses for thread-safe check-and-set.
+ ///
+ /// The API instance to bind to.
+ /// true if the provider was successfully bound (or was already bound to the same instance);
+ /// false if the provider is already bound to a different API instance.
+ internal bool TryBindApiInstance(Api api)
+ {
+ var previous = Interlocked.CompareExchange(ref this._boundApiInstance, api, null);
+ return previous is null || ReferenceEquals(previous, api);
+ }
+
+ ///
+ /// Clears the API instance binding, allowing this provider to be registered with another API instance.
+ ///
+ internal void UnbindApiInstance()
+ {
+ this._boundApiInstance = null;
+ }
+
///
///
/// This method is called before a provider is used to evaluate flags. Providers can overwrite this method,
diff --git a/src/OpenFeature/Isolated/OpenFeatureFactory.cs b/src/OpenFeature/Isolated/OpenFeatureFactory.cs
new file mode 100644
index 00000000..bcc4e13a
--- /dev/null
+++ b/src/OpenFeature/Isolated/OpenFeatureFactory.cs
@@ -0,0 +1,36 @@
+#if NET8_0_OR_GREATER
+using System.Diagnostics.CodeAnalysis;
+#endif
+
+namespace OpenFeature.Isolated;
+
+///
+/// Factory for creating isolated instances of the OpenFeature API.
+///
+///
+/// This factory provides a method to create new, independent instances of the OpenFeature API with fully isolated state. Each isolated instance maintains its own providers, evaluation context, hooks, event handlers,
+/// and transaction context propagators. It does not share state with the global singleton or with any other isolated instance.
+///
+public static class OpenFeatureFactory
+{
+ ///
+ /// Creates a new, independent instance of the OpenFeature API with fully isolated state.
+ ///
+ /// Each isolated instance maintains its own providers, evaluation context, hooks, event handlers,
+ /// and transaction context propagators. It does not share state with the global
+ /// singleton or with any other isolated instance.
+ ///
+ ///
+ ///
+ ///
+ /// A single provider instance should not be bound to more than one API instance at a time.
+ /// Attempting to do so will result in an .
+ ///
+ ///
+ /// A new, independent instance.
+ /// Specification 1.8 - Isolated API Instances
+#if NET8_0_OR_GREATER
+ [Experimental(Constant.FeatureDiagnosticCodes.IsolatedApi)]
+#endif
+ public static Api CreateIsolated() => new Api();
+}
diff --git a/src/OpenFeature/ProviderRepository.cs b/src/OpenFeature/ProviderRepository.cs
index 5539d0ea..d6230e8d 100644
--- a/src/OpenFeature/ProviderRepository.cs
+++ b/src/OpenFeature/ProviderRepository.cs
@@ -193,6 +193,13 @@ private async Task ShutdownIfUnusedAsync(
return;
}
+ // Clear ownership while still under the write lock — the provider is confirmed unused.
+ // This prevents a race where async shutdown clears ownership after a re-registration.
+ if (targetProvider != null)
+ {
+ targetProvider.UnbindApiInstance();
+ }
+
await this.SafeShutdownProviderAsync(targetProvider, cancellationToken).ConfigureAwait(false);
}
@@ -270,6 +277,12 @@ internal async Task ShutdownAsync(Action? afterError
// Set a default provider so the Api is ready to be used again.
this._defaultProvider = new NoOpFeatureProvider();
this._featureProviders.Clear();
+
+ // Clear ownership under the write lock for all providers being shut down.
+ foreach (var provider in providers)
+ {
+ provider.UnbindApiInstance();
+ }
}
finally
{
diff --git a/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj b/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj
index bc98f8a3..f7851637 100644
--- a/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj
+++ b/test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj
@@ -14,6 +14,7 @@
false
NU1903
OpenFeature.AotCompatibility
+ $(NoWarn);OFISO001
@@ -27,8 +28,4 @@
-
-
-
-
diff --git a/test/OpenFeature.AotCompatibility/Program.cs b/test/OpenFeature.AotCompatibility/Program.cs
index 5529eef2..8c963365 100644
--- a/test/OpenFeature.AotCompatibility/Program.cs
+++ b/test/OpenFeature.AotCompatibility/Program.cs
@@ -3,6 +3,7 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenFeature.Constant;
+using OpenFeature.Isolated;
using OpenFeature.Model;
using OpenFeature.Providers.MultiProvider;
using OpenFeature.Providers.MultiProvider.Models;
@@ -27,6 +28,9 @@ private static async Task Main(string[] args)
// Test basic API functionality
await TestBasicApiAsync();
+ // Test isolated API instances
+ await TestIsolatedApiAsync();
+
// Test MultiProvider AOT compatibility
await TestMultiProviderAotCompatibilityAsync();
@@ -49,6 +53,41 @@ private static async Task Main(string[] args)
}
}
+ private static async Task TestIsolatedApiAsync()
+ {
+ Console.WriteLine("\nTesting isolated API instances...");
+
+ // Create an isolated instance
+ var isolated = OpenFeatureFactory.CreateIsolated();
+ Console.WriteLine($"✓- Isolated API instance created: {isolated.GetType().Name}");
+
+ // Verify it is distinct from the singleton
+ if (ReferenceEquals(isolated, Api.Instance))
+ {
+ throw new InvalidOperationException("Isolated instance should not be the same as the singleton.");
+ }
+ Console.WriteLine("✓- Isolated instance is distinct from singleton");
+
+ // Set a provider on the isolated instance
+ var provider = new TestProvider();
+ await isolated.SetProviderAsync(provider);
+ Console.WriteLine($"✓- Provider set on isolated instance: {isolated.GetProviderMetadata()?.Name}");
+
+ // Create a client and evaluate a flag
+ var client = isolated.GetClient("isolated-client");
+ var result = await client.GetBooleanValueAsync("test-flag", false);
+ Console.WriteLine($"✓- Isolated client flag evaluation: {result}");
+
+ // Set context on isolated instance and verify independence
+ var context = EvaluationContext.Builder().Set("scope", "isolated").Build();
+ isolated.SetContext(context);
+ Console.WriteLine($"✓- Isolated context set with {context.Count} attributes");
+
+ // Shutdown the isolated instance
+ await isolated.ShutdownAsync();
+ Console.WriteLine("✓- Isolated instance shut down successfully");
+ }
+
private static async Task TestBasicApiAsync()
{
Console.WriteLine("\nTesting basic API functionality...");
diff --git a/test/OpenFeature.Tests/Isolated/IsolatedApiTests.cs b/test/OpenFeature.Tests/Isolated/IsolatedApiTests.cs
new file mode 100644
index 00000000..ed90ac5e
--- /dev/null
+++ b/test/OpenFeature.Tests/Isolated/IsolatedApiTests.cs
@@ -0,0 +1,378 @@
+using NSubstitute;
+using OpenFeature.Constant;
+using OpenFeature.Isolated;
+using OpenFeature.Model;
+using OpenFeature.Tests.Internal;
+
+namespace OpenFeature.Tests.Isolated;
+
+///
+/// Tests for isolated API instances (spec section 1.8).
+/// Each test creates its own isolated instances and cleans them up,
+/// so no shared state fixture is needed.
+///
+#if NET8_0_OR_GREATER
+[System.Diagnostics.CodeAnalysis.Experimental(FeatureDiagnosticCodes.IsolatedApi)]
+#endif
+public class IsolatedApiTests
+{
+ [Fact]
+ [Specification("1.8.1", "The API MUST expose a factory function which creates and returns a new, independent instance of the API.")]
+ public void CreateIsolated_Should_Return_New_Instance()
+ {
+ var isolated = OpenFeatureFactory.CreateIsolated();
+
+ Assert.NotNull(isolated);
+ Assert.NotSame(Api.Instance, isolated);
+ }
+
+ [Fact]
+ [Specification("1.8.1", "The API MUST expose a factory function which creates and returns a new, independent instance of the API.")]
+ public void CreateIsolated_Should_Return_Distinct_Instances()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+
+ Assert.NotSame(isolated1, isolated2);
+ }
+
+ [Fact]
+ [Specification("1.8.2", "Instances returned by the factory function MUST conform to the same API contract as the global singleton.")]
+ public async Task Isolated_Instance_Should_Support_Full_Api_Contract()
+ {
+ var isolated = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ // Provider management
+ var provider = new TestProvider();
+ await isolated.SetProviderAsync(provider);
+ Assert.Equal(provider, isolated.GetProvider());
+ Assert.Equal(provider.GetMetadata()?.Name, isolated.GetProviderMetadata()?.Name);
+
+ // Client creation
+ var client = isolated.GetClient("test-client");
+ Assert.NotNull(client);
+ Assert.Equal("test-client", client.GetMetadata().Name);
+
+ // Hooks
+ var hook = Substitute.For();
+ isolated.AddHooks(hook);
+ Assert.Contains(hook, isolated.GetHooks());
+
+ // Context
+ var context = EvaluationContext.Builder().Set("key", "value").Build();
+ isolated.SetContext(context);
+ Assert.Equal(context, isolated.GetContext());
+
+ // Event handling (just verify it doesn't throw)
+ isolated.AddHandler(ProviderEventTypes.ProviderReady, _ => { });
+
+ // Transaction context
+ var propagator = Substitute.For();
+ isolated.SetTransactionContextPropagator(propagator);
+ }
+ finally
+ {
+ await isolated.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.1", "The API MUST expose a factory function which creates and returns a new, independent instance of the API.")]
+ public async Task Isolated_Provider_Should_Not_Affect_Singleton()
+ {
+ Api.ResetApi();
+ var isolated = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider = new TestProvider("isolated-provider");
+ await isolated.SetProviderAsync(provider);
+
+ // The singleton should still have the default NoOp provider
+ Assert.Equal(NoOpProvider.NoOpProviderName, Api.Instance.GetProviderMetadata()?.Name);
+ Assert.Equal("isolated-provider", isolated.GetProviderMetadata()?.Name);
+ }
+ finally
+ {
+ await isolated.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.1", "The API MUST expose a factory function which creates and returns a new, independent instance of the API.")]
+ public async Task Isolated_Provider_Should_Not_Affect_Other_Isolated_Instance()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider1 = new TestProvider("provider-1");
+ var provider2 = new TestProvider("provider-2");
+ await isolated1.SetProviderAsync(provider1);
+ await isolated2.SetProviderAsync(provider2);
+
+ Assert.Equal("provider-1", isolated1.GetProviderMetadata()?.Name);
+ Assert.Equal("provider-2", isolated2.GetProviderMetadata()?.Name);
+ }
+ finally
+ {
+ await isolated1.ShutdownAsync();
+ await isolated2.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.1", "The API MUST expose a factory function which creates and returns a new, independent instance of the API.")]
+ public void Isolated_Hooks_Should_Not_Affect_Other_Instances()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+
+ var hook1 = Substitute.For();
+ var hook2 = Substitute.For();
+
+ isolated1.AddHooks(hook1);
+ isolated2.AddHooks(hook2);
+
+ Assert.Contains(hook1, isolated1.GetHooks());
+ Assert.DoesNotContain(hook2, isolated1.GetHooks());
+
+ Assert.Contains(hook2, isolated2.GetHooks());
+ Assert.DoesNotContain(hook1, isolated2.GetHooks());
+ }
+
+ [Fact]
+ [Specification("1.8.1", "The API MUST expose a factory function which creates and returns a new, independent instance of the API.")]
+ public void Isolated_Context_Should_Not_Affect_Other_Instances()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+
+ var context1 = EvaluationContext.Builder().Set("instance", "one").Build();
+ var context2 = EvaluationContext.Builder().Set("instance", "two").Build();
+
+ isolated1.SetContext(context1);
+ isolated2.SetContext(context2);
+
+ Assert.Equal(context1, isolated1.GetContext());
+ Assert.Equal(context2, isolated2.GetContext());
+ }
+
+ [Fact]
+ [Specification("1.8.4", "A provider instance SHOULD NOT be registered with more than one API instance simultaneously.")]
+ public async Task Same_Provider_Should_Throw_When_Bound_To_Multiple_Api_Instances()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider = new TestProvider("shared-provider");
+ await isolated1.SetProviderAsync(provider);
+
+ var exception = await Assert.ThrowsAsync(
+ () => isolated2.SetProviderAsync(provider));
+
+ Assert.Equal("This provider instance is already bound to a different API instance. " +
+ "A provider should not be registered with more than one API instance simultaneously.", exception.Message);
+ }
+ finally
+ {
+ await isolated1.ShutdownAsync();
+ await isolated2.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.4", "A provider instance SHOULD NOT be registered with more than one API instance simultaneously.")]
+ public async Task Same_Provider_Should_Throw_When_Bound_To_Multiple_Api_Instances_Domain()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider = new TestProvider("shared-provider");
+ await isolated1.SetProviderAsync("domain-1", provider);
+
+ var exception = await Assert.ThrowsAsync(
+ () => isolated2.SetProviderAsync("domain-2", provider));
+
+ Assert.Equal("This provider instance is already bound to a different API instance. " +
+ "A provider should not be registered with more than one API instance simultaneously.", exception.Message);
+ }
+ finally
+ {
+ await isolated1.ShutdownAsync();
+ await isolated2.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.4", "A provider instance SHOULD NOT be registered with more than one API instance simultaneously.")]
+ public async Task Concurrent_Binding_Should_Allow_Only_One_Api_Instance()
+ {
+ const int instanceCount = 10;
+ var provider = new TestProvider("contested-provider");
+ var instances = Enumerable.Range(0, instanceCount).Select(_ => OpenFeatureFactory.CreateIsolated()).ToList();
+ try
+ {
+ var tasks = instances.Select(api => api.SetProviderAsync(provider)).ToList();
+ var results = await Task.WhenAll(tasks.Select(async t =>
+ {
+ try { await t; return (Exception?)null; }
+ catch (InvalidOperationException ex) { return ex; }
+ }));
+
+ var successes = results.Count(r => r is null);
+ var failures = results.Count(r => r is InvalidOperationException);
+
+ Assert.Equal(1, successes);
+ Assert.Equal(instanceCount - 1, failures);
+ }
+ finally
+ {
+ foreach (var api in instances)
+ {
+ await api.ShutdownAsync();
+ }
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.4", "A provider instance SHOULD NOT be registered with more than one API instance simultaneously.")]
+ public async Task Provider_Can_Be_Rebound_After_Shutdown()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider = new TestProvider("rebindable-provider");
+ await isolated1.SetProviderAsync(provider);
+
+ // Shut down first instance — this frees the provider
+ await isolated1.ShutdownAsync();
+
+ // Now the provider should be bindable to another instance
+ await isolated2.SetProviderAsync(provider);
+ Assert.Equal("rebindable-provider", isolated2.GetProviderMetadata()?.Name);
+ }
+ finally
+ {
+ await isolated2.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.4", "A provider instance SHOULD NOT be registered with more than one API instance simultaneously.")]
+ public async Task Same_Provider_Can_Be_Bound_To_Multiple_Domains_Within_Same_Api()
+ {
+ var isolated = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider = new TestProvider("multi-domain-provider");
+ await isolated.SetProviderAsync("domain-a", provider);
+ await isolated.SetProviderAsync("domain-b", provider);
+
+ Assert.Equal(provider, isolated.GetProvider("domain-a"));
+ Assert.Equal(provider, isolated.GetProvider("domain-b"));
+ }
+ finally
+ {
+ await isolated.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ public async Task Shutdown_Isolated_Should_Not_Affect_Singleton()
+ {
+ Api.ResetApi();
+ var singletonProvider = new TestProvider("singleton-provider");
+ await Api.Instance.SetProviderAsync(singletonProvider);
+
+ var isolated = OpenFeatureFactory.CreateIsolated();
+ var isolatedProvider = new TestProvider("isolated-provider");
+ await isolated.SetProviderAsync(isolatedProvider);
+
+ // Shutting down isolated instance
+ await isolated.ShutdownAsync();
+
+ // Singleton should be unaffected
+ Assert.Equal("singleton-provider", Api.Instance.GetProviderMetadata()?.Name);
+
+ // Cleanup
+ await Api.Instance.ShutdownAsync();
+ }
+
+ [Fact]
+ public async Task Shutdown_Isolated_Should_Not_Affect_Other_Isolated()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider1 = new TestProvider("provider-1");
+ var provider2 = new TestProvider("provider-2");
+ await isolated1.SetProviderAsync(provider1);
+ await isolated2.SetProviderAsync(provider2);
+
+ await isolated1.ShutdownAsync();
+
+ // isolated2 should be unaffected
+ Assert.Equal("provider-2", isolated2.GetProviderMetadata()?.Name);
+ }
+ finally
+ {
+ await isolated2.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ public async Task Provider_Can_Be_Rebound_After_Replacement()
+ {
+ var isolated1 = OpenFeatureFactory.CreateIsolated();
+ var isolated2 = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var providerA = new TestProvider("provider-a");
+ var providerB = new TestProvider("provider-b");
+
+ await isolated1.SetProviderAsync(providerA);
+
+ // Replace providerA with providerB in isolated1 — providerA should be unbound
+ await isolated1.SetProviderAsync(providerB);
+
+ // Allow some time for the async shutdown to complete
+ await Task.Delay(100);
+
+ // providerA should now be free to bind to isolated2
+ await isolated2.SetProviderAsync(providerA);
+ Assert.Equal("provider-a", isolated2.GetProviderMetadata()?.Name);
+ }
+ finally
+ {
+ await isolated1.ShutdownAsync();
+ await isolated2.ShutdownAsync();
+ }
+ }
+
+ [Fact]
+ [Specification("1.8.2", "Instances returned by the factory function MUST conform to the same API contract as the global singleton.")]
+ public async Task Isolated_Client_Should_Evaluate_Flags()
+ {
+ var isolated = OpenFeatureFactory.CreateIsolated();
+ try
+ {
+ var provider = new TestProvider("eval-provider");
+ await isolated.SetProviderAsync(provider);
+
+ var client = isolated.GetClient();
+
+ // TestProvider inverts boolean defaults
+ var result = await client.GetBooleanValueAsync("test-flag", false);
+ Assert.True(result);
+ }
+ finally
+ {
+ await isolated.ShutdownAsync();
+ }
+ }
+}
From b3fa7f41c926a93a69ac2d599089ce4045d62770 Mon Sep 17 00:00:00 2001
From: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
Date: Thu, 30 Apr 2026 15:13:29 -0400
Subject: [PATCH 58/58] chore(main): release 2.13.0 (#744)
Signed-off-by: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 14 ++++++++++++++
README.md | 4 ++--
build/Common.prod.props | 2 +-
version.txt | 2 +-
5 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 7a48e52a..d18e9443 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.12.0"
+ ".": "2.13.0"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec858651..13fd99ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## [2.13.0](https://github.com/open-feature/dotnet-sdk/compare/v2.12.0...v2.13.0) (2026-04-30)
+
+
+### 🐛 Bug Fixes
+
+* add missing variant dimension in MetricsHook metrics ([#742](https://github.com/open-feature/dotnet-sdk/issues/742)) ([dad175d](https://github.com/open-feature/dotnet-sdk/commit/dad175dd353e2a733a0473b644633ce571ccb21a))
+* fix 'occured' -> 'occurred' in XML doc comment ([#743](https://github.com/open-feature/dotnet-sdk/issues/743)) ([178f836](https://github.com/open-feature/dotnet-sdk/commit/178f8369c224038d329b775d6edc22a3fce97240))
+* **security:** update dependency opentelemetry.exporter.opentelemetryprotocol to 1.15.3 [security] ([#746](https://github.com/open-feature/dotnet-sdk/issues/746)) ([ec94acf](https://github.com/open-feature/dotnet-sdk/commit/ec94acf183faa0cf74c58aa6509d0fbc5c4b8d13))
+
+
+### ✨ New Features
+
+* Add isolated API instance functionality ([#740](https://github.com/open-feature/dotnet-sdk/issues/740)) ([7802950](https://github.com/open-feature/dotnet-sdk/commit/7802950d15b666f85c8582b698c913d072af23a0))
+
## [2.12.0](https://github.com/open-feature/dotnet-sdk/compare/v2.11.1...v2.12.0) (2026-04-06)
diff --git a/README.md b/README.md
index d944390e..e221ee07 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,8 @@
[](https://github.com/open-feature/spec/releases/tag/v0.8.0)
[
-
-](https://github.com/open-feature/dotnet-sdk/releases/tag/v2.12.0)
+
+](https://github.com/open-feature/dotnet-sdk/releases/tag/v2.13.0)
[](https://cloud-native.slack.com/archives/C0344AANLA1)
[](https://codecov.io/gh/open-feature/dotnet-sdk)
diff --git a/build/Common.prod.props b/build/Common.prod.props
index 7dc19f15..29b87ac7 100644
--- a/build/Common.prod.props
+++ b/build/Common.prod.props
@@ -9,7 +9,7 @@
- 2.12.0
+ 2.13.0
git
https://github.com/open-feature/dotnet-sdk
OpenFeature is an open standard for feature flag management, created to support a robust feature flag ecosystem using cloud native technologies. OpenFeature will provide a unified API and SDK, and a developer-first, cloud-native implementation, with extensibility for open source and commercial offerings.
diff --git a/version.txt b/version.txt
index d8b69897..fb2c0766 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-2.12.0
+2.13.0