diff --git a/Egil.Orleans.Messaging/README.md b/Egil.Orleans.Messaging/README.md
index 6efa65a2..3699bebe 100644
--- a/Egil.Orleans.Messaging/README.md
+++ b/Egil.Orleans.Messaging/README.md
@@ -1825,8 +1825,9 @@ this.RegisterStreamManager()
async (message, cursor) => await UpdateProjectionAsync(message));
```
-The Event Hubs extensions support Orleans **10.3.1 and 10.4.0** with the same
-package binary. On 10.4, enriched tokens share the built-in Event Hubs comparison
+All OM packages require Orleans **10.4.0 or later**. Upgrade the Orleans packages
+in your host together when upgrading OM. Enriched tokens share the built-in Event
+Hubs comparison
domain: equal sequence numbers and event indexes compare equally regardless of
enrichment. Checkpoints still belong to their provider and complete `StreamId`;
this does not make positions from different streams interchangeable. The JSON
diff --git a/Egil.Orleans.Messaging/docs/eventhubs-token-compatibility.md b/Egil.Orleans.Messaging/docs/eventhubs-token-compatibility.md
index 29f0516e..d7e94950 100644
--- a/Egil.Orleans.Messaging/docs/eventhubs-token-compatibility.md
+++ b/Egil.Orleans.Messaging/docs/eventhubs-token-compatibility.md
@@ -1,36 +1,34 @@
-# Event Hubs token compatibility
+# Event Hubs token compatibility on Orleans 10.4
-The stable core and Event Hubs extension retain their Orleans 10.3.1 minimum.
-Only Event Hubs compilation uses the two 10.4 streaming/provider assemblies,
-acquired through `PackageDownload`; restore, runtime and pack graphs retain 10.3.1.
+All OM packages require Orleans 10.4.0 or later. The preview Journaling integration
+uses Microsoft.Orleans.Journaling 10.4.0-alpha.1. Consumers must upgrade their Orleans
+packages together; no mixed 10.3/10.4 runtime or compile-reference strategy is supported.
+Shared dependency pins and coordinated package verification are owned by #285.
-The enriched token overrides 10.4's protected compatibility-domain getter with
+The enriched token overrides the protected compatibility-domain getter with
`typeof(EventHubSequenceToken)`, matching standard provider tokens in both comparison
-directions. On 10.3.1, the CLR introduces an unused virtual slot because the base
-getter does not exist. No explicit override mapping or missing base call is emitted.
-This is established by actual packed-binary execution, not nuspec metadata alone.
-Aliases and JSON discriminator values are unchanged. Provider plus full `StreamId`
-remain the checkpoint identity. Generic-token public equality remains separate from
-the provider's historical exact V1 checkpoint normalization at recovery boundaries.
+directions. Aliases, field IDs and JSON discriminator values are unchanged. Provider
+plus full `StreamId` remain the checkpoint identity. Generic-token public equality
+remains separate from the provider's historical exact V1 checkpoint normalization at
+recovery boundaries; upgrading the runtime does not discard existing checkpoint data.
+
+Factory and cache recovery tests now run in the normal Event Hubs test project:
```powershell
-pwsh -File Egil.Orleans.Messaging/scripts/test-eventhubs-compatibility.ps1
+dotnet test Egil.Orleans.Messaging/Egil.Orleans.Messaging.slnx -c Release
```
-The script packs once, checks the dependency minimum, uses a fresh package cache and
-runs existing Event Hubs tests as external consumers on 10.3.1 and 10.4.0. Additional
-10.4 cases cover the inherited per-event factory, real pooled provider cache recovery
-for standard/enriched/historical exact V1 tokens, genuine eviction misses and generic
-token rejection. Existing cases cover persisted JSON fixtures, Orleans serialization,
-custom containers, null per-event tokens, metadata and outbox request context. SHA-256
-verification checks both the unchanged package and each consumer's copied assembly against the packed DLL. Each run gets a unique feed and cache even when the output parent is reused.
+Coverage includes symmetric equality/order/hash, sequence-before-index ordering,
+persisted standard/enriched provider/full-stream checkpoint lookup, Orleans and JSON
+serialization, default/custom/null-token containers, per-event metadata, inherited
+factory cloning, historical exact V1 recovery through the real pooled provider cache,
+and genuine eviction misses. The cache fixtures create separate broker events through
+the enriched adapter.
-Before the override, both symmetric equality cases failed on the packed 10.4
-consumer; after the override, both version consumers passed. Broker connectivity and
-a live pulling-agent subscription handshake remain coordinated validation work for
-#285; this in-process suite does not claim either. No claim covers future versions or
-NativeAOT.
+Before the override, standard/enriched symmetric equality failed on Orleans 10.4;
+the override restores both comparison directions. Actual StreamManager resume and
+the live pulling-agent subscription handshake remain coordinated validation work for
+#285. In-process cache tests do not claim broker connectivity or live handshake proof.
Issue: https://github.com/egil/framework/issues/282
Upstream: https://github.com/dotnet/orleans/pull/11155
-
diff --git a/Egil.Orleans.Messaging/scripts/test-eventhubs-compatibility.ps1 b/Egil.Orleans.Messaging/scripts/test-eventhubs-compatibility.ps1
deleted file mode 100644
index 05943c36..00000000
--- a/Egil.Orleans.Messaging/scripts/test-eventhubs-compatibility.ps1
+++ /dev/null
@@ -1,65 +0,0 @@
-param([string] $OutputDirectory)
-$ErrorActionPreference = 'Stop'
-$projectRoot = Split-Path $PSScriptRoot
-if (!$OutputDirectory) { $OutputDirectory = [IO.Path]::GetTempPath() }
-$OutputDirectory = Join-Path ([IO.Path]::GetFullPath($OutputDirectory)) ('om-eventhubs-' + [guid]::NewGuid().ToString('N'))
-$feed = Join-Path $OutputDirectory 'feed'
-New-Item -ItemType Directory -Force $feed | Out-Null
-function Invoke-Dotnet {
- & dotnet @args
- if ($LASTEXITCODE -ne 0) { throw "dotnet failed with exit code $LASTEXITCODE" }
-}
-Invoke-Dotnet pack (Join-Path $projectRoot 'src/Egil.Orleans.Messaging/Egil.Orleans.Messaging.csproj') -c Release -o $feed
-Invoke-Dotnet pack (Join-Path $projectRoot 'src/Egil.Orleans.Messaging.Streams.EventHubs/Egil.Orleans.Messaging.Streams.EventHubs.csproj') -c Release -o $feed
-Add-Type -AssemblyName System.IO.Compression.FileSystem
-$package = Get-ChildItem $feed -Filter 'Egil.Orleans.Messaging.Streams.EventHubs.*.nupkg' | Select-Object -First 1
-$archive = [IO.Compression.ZipFile]::OpenRead($package.FullName)
-try {
- $entry = $archive.Entries | Where-Object FullName -Like '*.nuspec' | Select-Object -First 1
- $reader = [IO.StreamReader]::new($entry.Open())
- try { [xml] $nuspec = $reader.ReadToEnd() } finally { $reader.Dispose() }
- $dllEntry = $archive.GetEntry('lib/net10.0/Egil.Orleans.Messaging.Streams.EventHubs.dll')
- $dllStream = $dllEntry.Open()
- try { $expectedDllHash = (Get-FileHash -InputStream $dllStream -Algorithm SHA256).Hash } finally { $dllStream.Dispose() }
- $version = $nuspec.package.metadata.version
- $providerDependency = $nuspec.package.metadata.dependencies.group.dependency | Where-Object id -EQ 'Microsoft.Orleans.Streaming.EventHubs'
- if ($providerDependency.version -ne '10.3.1') { throw "Unexpected provider minimum: $($providerDependency.version)" }
-} finally { $archive.Dispose() }
-$hashBefore = (Get-FileHash $package.FullName -Algorithm SHA256).Hash
-$sources = (Join-Path $projectRoot 'test/Egil.Orleans.Messaging.Streams.EventHubs.Tests').Replace('\', '/')
-$recoverySources = (Join-Path $projectRoot 'test/EventHubs.Compatibility/*.cs').Replace('\', '/')
-foreach ($orleansVersion in '10.3.1', '10.4.0') {
- $consumer = Join-Path $OutputDirectory $orleansVersion
- New-Item -ItemType Directory -Force $consumer | Out-Null
- $recovery = if ($orleansVersion -eq '10.4.0') { '' } else { '' }
- @"
-
-
- net10.0enableenable
- falsetrueExe
- true
-
-
- $recovery
-
-
-
-
-
-
-
-
-
-
-"@ | Set-Content (Join-Path $consumer 'Tests.csproj')
- @"
-
-"@ | Set-Content (Join-Path $consumer 'NuGet.Config')
- Invoke-Dotnet restore (Join-Path $consumer 'Tests.csproj') --configfile (Join-Path $consumer 'NuGet.Config') --packages (Join-Path $OutputDirectory 'packages')
- Invoke-Dotnet test (Join-Path $consumer 'Tests.csproj') -c Release --no-restore
- $loadedDll = Join-Path $consumer 'bin/Release/net10.0/Egil.Orleans.Messaging.Streams.EventHubs.dll'
- if ((Get-FileHash $loadedDll -Algorithm SHA256).Hash -ne $expectedDllHash) { throw 'Consumer did not load the packed assembly.' }
-}
-if ((Get-FileHash $package.FullName -Algorithm SHA256).Hash -ne $hashBefore) { throw 'Package changed between consumer runs.' }
-Write-Host "Same artifact verified on both Orleans versions: $hashBefore"
-Write-Host "Evidence directory: $OutputDirectory"
diff --git a/Egil.Orleans.Messaging/test/EventHubs.Compatibility/EventHubs104RecoveryTests.cs b/Egil.Orleans.Messaging/test/Egil.Orleans.Messaging.Streams.EventHubs.Tests/EventHubs/EventHubsRecoveryTests.cs
similarity index 97%
rename from Egil.Orleans.Messaging/test/EventHubs.Compatibility/EventHubs104RecoveryTests.cs
rename to Egil.Orleans.Messaging/test/Egil.Orleans.Messaging.Streams.EventHubs.Tests/EventHubs/EventHubsRecoveryTests.cs
index 590f902f..2cf8b512 100644
--- a/Egil.Orleans.Messaging/test/EventHubs.Compatibility/EventHubs104RecoveryTests.cs
+++ b/Egil.Orleans.Messaging/test/Egil.Orleans.Messaging.Streams.EventHubs.Tests/EventHubs/EventHubsRecoveryTests.cs
@@ -7,7 +7,7 @@
namespace Egil.Orleans.Messaging.Tests.Streams.EventHubs;
-public sealed class EventHubs104RecoveryTests
+public sealed class EventHubsRecoveryTests
{
[Fact]
public void Inherited_event_factory_retains_enrichment()
@@ -78,9 +78,15 @@ public void Generic_tokens_remain_distinct_in_public_equality()
Assert.Throws(() => token.CompareTo(genericV2));
}
- public enum CheckpointKind { Standard, Enriched, HistoricalV1 }
+ public enum CheckpointKind
+ {
+ Standard,
+ Enriched,
+ HistoricalV1
+ }
private sealed class UnrelatedProviderToken(long sequenceNumber, int eventIndex) : EventSequenceToken(sequenceNumber, eventIndex);
+
private static (PooledQueueCache Cache, StreamId Stream) CreateCache()
{
var services = new ServiceCollection();