Skip to content

v4.1.0: command queues, Aspire E2E testing, mutable reference data and AI skills - #188

Merged
chullybun merged 50 commits into
mainfrom
aspire-testing
Oct 12, 2026
Merged

chullybun merged 50 commits into
mainfrom
aspire-testing

Conversation

@chullybun

@chullybun chullybun commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

CoreEx v4.1.0 release. The headline themes are command routing to per-domain Service Bus queues, consistent outbox partitioning with independent broker session routing, Aspire-based end-to-end testing, mutable reference data, Cosmos provisioning and relay support, and expanded AI workflow assets.

Framework

  • Command routing and keyed receiving: NamedDestinationProvider (AddNamedDestinationProvider()) routes commands to {Destination}-{domain} while events stay on the shared topic. A null/empty command domain self-addresses to IHostSettings.DomainName. Keyed receivers and WithKeyedSubscribedSubscriber support separate event-topic and command-queue receivers in one Subscribe host.
  • Outbox partitioning and Service Bus sessions:
    • Relational writers and relays share CoreEx:Host:Outbox:PartitionSize (default 4; range 1–256). The removed relay-only CoreEx:Host:Services:OutboxRelay:PartitionSize key fails with migration guidance. Keyless events use the stable $none sentinel rather than random partitioning.
    • Service Bus session mapping is a separate boundary: CoreEx:Host:ServiceBus:SessionIdPartitionSize, with exact-destination overrides under CoreEx:Host:ServiceBus:Destinations:{destination} for size and/or SessionIdStrategy. Producers publishing to the same destination must use consistent session policies; consumer session concurrency and physical broker partitions are separate concerns.
    • Effective settings appear in startup logs and detailed relay health data. Cosmos uses native logical partitions, not the relational numeric partition size.
  • Aspire testing (CoreEx.UnitTesting): distributed-application helpers for relational/Cosmos migration, Redis clear and Service Bus topology reset; AppHost endpoint links and hosted-service controls; test-only HTTP certificate handling; WireMock-based MockHost for third-party HTTP. External connection-string resources are visible in Aspire's graph; Cosmos exposes a credential-free account endpoint.
  • Cosmos provisioning (CoreEx.Cosmos.Provisioning; dev/test only): DbEx-style console with create/drop/reset/data command combinations, prompts, parameterized seed data, per-stage output and secret-safe settings. Reset deletes/recreates declared containers but does not implicitly create the database. $-prefixed seed groups are merged/upserted; other groups insert; ^ groups generate identifiers. MigrateCosmosDataAsync integrates provisioning with tests. Lease containers are explicitly provisioned, never created by runtime relays. Local and CI infrastructure use the Linux vNext emulator in HTTPS/Gateway mode.
  • Mutable reference data: CodeGen mutability (None, CreateUpdate, CreateUpdateDelete) generates validated create/replace/patch/activate/deactivate/delete surfaces for EF or Cosmos repositories. EfDbReferenceData and CosmosDbReferenceData provide persistence; Cosmos enforces unique type/code pairs. Active items cannot be deleted. Generated post-commit cache invalidation is best-effort and logged; cache removal serializes with in-flight loads.
    • Breaking generation change: the former read-only ReferenceDataService becomes ReferenceDataProvider; mutation services are generated only for mutable entities. Remove stale generated files and update registration references. The non-generic AddReferenceDataOrchestrator() is preferred.
    • PreCheckAsync is an optional, non-atomic veto before activate/deactivate/delete. Authorization and checks for codes already in use remain consumer responsibilities; CoreEx does not provide automatic cascades.
  • API/client support: NonNullable marks mandatory nullable DTO properties correctly in OpenAPI. Fluent HTTP response validation and cached-result helpers support typed clients and adapters.

Correctness and reliability fixes

  • Cosmos scoped change tracking preserves persisted server-managed values and unmapped extension data, detects no-op updates, and invalidates snapshots across model types. Version-checked snapshot publication and post-write eviction prevent concurrent reads from re-caching stale data after mutations or clears.
  • Cosmos relays stop correctly on disposal and recover when monitored/lease containers are replaced. Fresh leases explicitly replay the existing outbox backlog rather than inheriting the SDK's “from now” default; this prevents first-start and recovery from skipping pending events. Existing checkpoints take precedence; explicitly configured StartTime remains supported.
  • Relational relay failures do not block sibling partitions. Lease-expiry cancellation is distinguished from shutdown, failed batches use a separate bounded cleanup token, and resiliency observes per-partition outcomes. Hosted-service cancellation/lease handling is hardened.
  • Service Bus cancellation coverage uses an isolated subscription so abandoned poison messages from another test cannot starve its expected message.
  • WebApi/JsonMergePatch result-based patch overloads return not-found for missing targets; CompareProperty uses the compared property's label. EF updates exclude server-managed audit/tenant fields from default mapping.

Samples

  • Products Brand and Customers CustomerType demonstrate mutable reference data on PostgreSQL/EF and Cosmos respectively; host registrations use ReferenceDataProvider.
  • Products.Subscribe consumes both the shared events topic and the session-enabled contoso-products command queue. Shopping publishing hosts demonstrate a destination-specific session bucket count of 2 for that queue, independently of global size 4 and relational outbox size 4.
  • Customers adds a provisioning console and change-feed relay. Aspire exercises real customer CRUD/reference-data outbox publication on an isolated observation subscription, alongside the checkout/inventory-confirmation/SendGrid flow.
  • Shopping validates basket customers through a Customers adapter and exposes a customer-scoped basket query. E2E Runner adds Customers updates, provisioning support and improved failure links.
  • Orders development endpoints move to HTTPS 7330 / HTTP 5330, with matching runner configuration, to avoid Windows dynamic/excluded-port collisions.
  • Aspire checkout tests wait for all participating relay/subscriber hosts, use bounded inventory-confirmation polling, and report identifiers, last status and the command path on timeout. This is harness hardening, not a claim that every earlier relay delay was diagnosed.

CoreEx.Template, AI assets and documentation

  • Templates use named command destinations, keyed subscribers and code-based Service Bus topology. Cosmos is a supported data-provider option; Service Bus-disabled combinations are guarded.
  • coreex-aspire generates AppHost, MockHost and Test.Aspire projects with pre-start provisioning and post-start setup.
  • New command publish/subscribe and Aspire skills, prompts, Claude commands and scoped instructions; mutable reference-data, partitioning/session-boundary, Cosmos and client-validation guidance updated across related assets and sample/package documentation.

CI / versioning

  • Version bumped to 4.1.0 with CHANGELOG entries; DbEx dependencies updated to 3.3.0.
  • CI includes Customers CodeGen/provisioning and a net10.0-only Aspire E2E step after sample tests. Template validation packs, installs, scaffolds and builds supported scenarios rather than relying on compilation of raw template content.

chullybun and others added 9 commits September 24, 2026 15:51
Added Reset-ServiceBusEmulator.ps1 to automate recovery of the Service Bus emulator and SQL Server containers. Registered the script in CoreEx.slnx. Updated AppHost.cs to model infra as Aspire connection-string resources with icons and explicit project references for dashboard topology. Synced appsettings.Development.json connection strings to match Aspire resource names. Upgraded UnitTestEx packages to 5.12.0. Improved EventExpectations and UnitTestExExpectations.ChangeLog with updated XML docs, constructor signatures, and runtime type checks for TesterBaseCore.
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…in database retry pipeline

- Add Contoso.Test.Aspire E2E test project exercising the full Aspire AppHost topology.
- Add WithAspireTester unit-testing helper alongside FusionCache Redis-connection-string cache
  clearing and Service Bus admin-client-based queue/subscription reset/purge helpers, replacing
  the Service Bus emulator Config.json-driven approach.
- Add per-database IsTransientException/HandleDbException classification (SqlServer + Postgres),
  reviewed against Azure SQL/Postgres transient error code lists.
- Add opt-in DatabaseArgsBase.RetryOnTransient + RetryResiliencePipeline, wired through
  DatabaseInvoker via a generalized RetryResiliency<TOwner>.Create<TResult> (Result<T>-aware)
  pipeline. Exhausted-retry failures convert directly to a Result (no throw) when the caller's
  result type is ROP-shaped; otherwise a single unwrapped exception (no AggregateException
  wrapping) is rethrown, preserving the existing TransformException conversion layer.
- Add unit test coverage for the retry pipeline (DatabaseInvokerRetryTests, RetryResiliencyTests).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…ic type argument

The semaphore-protected 'slow path' in ReferenceDataHybridCache.GetOrCreateAsync relied on
C# generic type inference to pick the T used for the underlying IHybridCache.GetOrCreateByKeyAsync<T>
call. Since the inner lambda's return expression resolved to the factory delegate's declared
IReferenceDataCollection return type, T was always inferred as the interface - never the concrete
collection type (e.g. UnitOfMeasureCollection) - unlike the 'fast path' read, which already used a
reflection-built typed invoker specifically to avoid this.

With a serializing distributed cache (FusionCache + Redis), this caused intermittent
System.Text.Json.NotSupportedException: 'The collection type IReferenceDataCollection is abstract,
an interface, or is read only, and could not be instantiated and populated' whenever the slow path's
internal L2 check deserialized a previously-written entry using the interface as the generic argument -
observed as FUSION [DC] deserialization errors in the Aspire E2E test logs.

Fix: add a GetOrCreateByKeyInvoker (mirroring the existing TryGetByKeyInvoker) so the slow path also
invokes the underlying cache with the concrete collection type, never the interface.

Added a regression test (GetOrCreateAsync_CacheMiss_NeverUsesInterfaceAsUnderlyingCacheGenericType)
using a type-recording IHybridCache stand-in; verified it fails against the pre-fix code and passes
against the fix. Full CoreEx.RefData.Test.Unit suite (137 tests) passes on net8.0/net9.0/net10.0.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…trust

Adds Contoso.Aspire.MockHost (WireMock.Net) as a test-only Aspire resource, wired into the
AppHost's DI container, and disables ASP.NET Core dev-cert validation for the AppHost's own
HttpClient defaults - dotnet dev-certs https --trust is not fully supported on Linux, so the
'api' resource's https endpoint dev cert is not OS-trusted on Linux CI runners; this covers both
the health check probe and AspireTesterBase's CreateHttpClient(), which resolve HttpClient via the
same DI container.

Also reorders two E2ETest.cs checkpoints (discount before basket-item-add) to match the actual
sync timing, and picks up the Spectre.Console/WireMock.Net package version housekeeping in
Directory.Packages.props and CoreEx.slnx needed by the new project.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
- Move the templated Aspire project out of src/ into a sibling aspire/
  directory (src, tests, aspire at the same level) since it is not a
  to-be-deployed project.
- Wire AppHost.cs resources first, then reference them from each project,
  mirroring samples/aspire/Contoso.Aspire/AppHost.cs.
- Add UnitTestExExtensions.Aspire.cs (CoreEx.UnitTesting) providing the
  WireMockConsole/AddMockHostProject/AddEndpoints Aspire helpers; delete
  the templated Extensions.cs, which these replace.
- Emit two new sibling projects into aspire/: app-name.Aspire.MockHost
  (WireMock.Net stub host) and solution-name.Test.Aspire (NUnit,
  WithAspireTester-based HostTests.cs).
- Apply the same MockHost simplification to samples/aspire/Contoso.Aspire.MockHost
  and wire it into Contoso.Aspire/AppHost.cs.
- Add Development.json connection strings matching Contoso.Aspire.
- Bump the templated AppHost's Aspire.AppHost.Sdk to 13.5.4 to match
  UnitTestEx.Aspire's Aspire.Hosting.AppHost requirement.
- Harden tools/validate-template-pack.ps1's local NuGet feed handling:
  copy missing third-party packages into the trusted local feed instead
  of adding a second, potentially stale, private source.
- Update samples/docs/aspire.md and testing.md to document the MockHost
  and Contoso.Test.Aspire; fix a stale AppHost.cs path in
  .github/copilot-instructions.md; update coreex-scaffold skill docs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…or SendGrid mocking

- New INotificationAdapter (Application) / NotificationAdapter (Infrastructure) backed by a
  SendGrid v3 Mail Send typed HTTP client (no SDK dependency).
- New BasketCheckedOutSubscriber (Contoso.Shopping.Subscribe) subscribing to
  contoso.shopping.basket.checkedout.v1, delegating to the notification adapter.
- Register SendGrid typed client + options in Subscribe/Api Program.cs and appsettings.json.
- Wire the Aspire AppHost to route the Subscribe host's SendGrid BaseAddress through the
  WireMock-based MockHost, stubbing v3/mail/send with 202 Accepted for local/dev E2E runs.
- Refactor CoreEx.UnitTesting Aspire helpers (ClearRedisCacheAsync, ResetAzureServiceBusAsync,
  etc.) to operate on DistributedApplication directly, and extend WithAspireTester with
  OnBeforeStartAsync/OnAfterStartAsync hooks supporting the MockHost wiring.
- New SendGridHttpClientTests (Test.Unit) and SubscriberTests.BasketCheckedOut (Test.Subscribe)
  proving the SendGrid API is invoked via mocked HTTP.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…ueues

- Add NamedDestinationProvider: events to a shared topic, commands to per-domain queues ({Destination}-{domain}); null command domain self-addresses to the host domain.
- Add AddNamedDestinationProvider() DI extension.
- Tighten EventData/EventFormatter DomainName semantics (owner of the message contract; commands default to host domain, throw if none).
- Add optional serviceKey to WithReceiver/WithSessionReceiver so multiple receivers can coexist.
- Samples: add session-enabled contoso-products queue, register the provider in publishing hosts, Products.Subscribe consumes both topic subscription and command queue.
- Update tests (unit, relay queue test, command destination assertions) and docs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
…ervice Bus topology

- Api/Subscribe host templates register AddNamedDestinationProvider().
- Subscribe host uses keyed receiver/subscriber/hosted service (hosted-subscriber-events).
- New Test.Common ServiceBus class; Relay/Subscribe/Aspire tests use ResetAzureServiceBusAsync.
- Aspire test template uses OnBeforeStartAsync/OnAfterStartAsync.
- Sample hosted service keys renamed to hosted-subscriber-*; docs and validator updated.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
- New skills coreex-command-publish-e2e, coreex-command-subscribe-e2e and coreex-aspire
  (with prompts, Claude commands, aspire instructions) registered in the template pack.
- Update catalogs, README, agent routing, subscriber/adapter/test skills and stale docs.
- CI: add Aspire E2E solution filter and net10.0-only test step after sample tests.
- Bump version to 4.1.0 and add CHANGELOG entries.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 29, 2026 22:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Generated runtime topology and standalone service discovery are incomplete, while two unbounded asynchronous timing assumptions make the new E2E test unreliable.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
What changed in this PR

Introduces domain-targeted command queues, keyed Service Bus receivers, Aspire end-to-end testing, transient database retries, and supporting templates/documentation for v4.1.0.

Changes:

  • Adds NamedDestinationProvider, multi-destination publishing, keyed receivers, and command queue samples.
  • Adds Aspire orchestration, test helpers, MockHost integration, and E2E coverage.
  • Adds database retry support, SendGrid notifications, templates, skills, documentation, and release updates.
File Description
.claude/​commands/​coreex-aspire.md Adds Aspire command.
.claude/​commands/​coreex-command-publish-e2e.md Adds command-publishing workflow.
.claude/​commands/​coreex-command-subscribe-e2e.md Adds command-subscriber workflow.
.claude/​commands/​coreex-subscriber-e2e.md Narrows subscriber workflow scope.
.github/​agents/​coreex-expert.agent.md Updates agent routing guidance.
.github/​copilot-instructions.md Updates repository guidance.
.github/​coreex-ai-workflows.md Documents new workflows.
.github/​instructions/​coreex-aspire.instructions.md Adds Aspire conventions.
.github/​instructions/​coreex-event-subscribers.instructions.md Updates subscriber conventions.
.github/​instructions/​coreex-host-setup.instructions.md Updates host setup guidance.
.github/​instructions/​coreex-tests.instructions.md Updates testing guidance.
.github/​prompts/​coreex-aspire.prompt.md Adds Aspire prompt.
.github/​prompts/​coreex-command-publish-e2e.prompt.md Adds command-publishing prompt.
.github/​prompts/​coreex-command-subscribe-e2e.prompt.md Adds command-subscriber prompt.
.github/​prompts/​coreex-subscriber-e2e.prompt.md Updates subscriber prompt.
.github/​skills/​coreex-adapter/​SKILL.md Extends adapter guidance.
.github/​skills/​coreex-aspire/​SKILL.md Adds Aspire skill.
.github/​skills/​coreex-aspire/​references/​workflow.md Documents Aspire workflow.
.github/​skills/​coreex-command-publish-e2e/​SKILL.md Adds command-publishing skill.
.github/​skills/​coreex-command-publish-e2e/​references/​workflow.md Documents publishing workflow.
.github/​skills/​coreex-command-subscribe-e2e/​SKILL.md Adds command-subscriber skill.
.github/​skills/​coreex-command-subscribe-e2e/​references/​workflow.md Documents subscriber workflow.
.github/​skills/​coreex-scaffold/​SKILL.md Updates scaffolding guidance.
.github/​skills/​coreex-scaffold/​references/​workflow.md Updates scaffold workflow.
.github/​skills/​coreex-subscriber-e2e/​SKILL.md Refines event subscriber skill.
.github/​skills/​coreex-subscriber-e2e/​references/​workflow.md Refines subscriber workflow.
.github/​skills/​coreex-subscriber/​SKILL.md Updates subscriber guidance.
.github/​skills/​coreex-test-relay/​SKILL.md Updates relay testing.
.github/​skills/​coreex-test-subscribe/​SKILL.md Updates subscriber testing.
.github/​skills/​coreex-test-subscribe/​references/​workflow.md Updates test workflow.
.github/​workflows/​CI.yml Adds Aspire E2E CI.
AGENTS.md Updates repository context.
CHANGELOG.md Records v4.1.0 changes.
CoreEx.Samples.Build.slnf Adds Aspire sample projects.
CoreEx.Samples.Test.E2E.slnf Adds E2E solution filter.
CoreEx.slnx Adds new projects.
Directory.Packages.props Updates package versions.
README.md Documents new capabilities.
Version.props Bumps version to 4.1.0.
samples/​aspire/​Contoso.Aspire.MockHost/​Contoso.Aspire.MockHost.csproj Adds mock-host project.
samples/​aspire/​Contoso.Aspire.MockHost/​Program.cs Starts WireMock host.
samples/​aspire/​Contoso.Aspire/​AppHost.cs Orchestrates sample services.
samples/​aspire/​Contoso.Aspire/​Contoso.Aspire.csproj Updates AppHost dependencies.
samples/​aspire/​Contoso.Aspire/​appsettings.Development.json Configures Aspire resources.
samples/​aspire/​Contoso.Test.Aspire/​Contoso.Test.Aspire.csproj Adds Aspire test project.
samples/​aspire/​Contoso.Test.Aspire/​E2ETest.cs Adds cross-domain E2E test.
samples/​aspire/​Contoso.Test.Aspire/​GlobalUsing.cs Adds test imports.
samples/​aspire/​Contoso.Test.Aspire/​ServiceBus.cs Defines E2E broker topology.
samples/​docs/​aspire.md Documents Aspire usage.
samples/​docs/​hosts-layer.md Updates host documentation.
samples/​docs/​local-dev.md Updates local setup.
samples/​docs/​testing.md Updates testing documentation.
samples/​src/​Contoso.Customers.Api/​Program.cs Registers named destinations.
samples/​src/​Contoso.Orders.Api/​Program.cs Registers named destinations.
samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs Adds product activation.
samples/​src/​Contoso.Products.Api/​Program.cs Registers named destinations.
samples/​src/​Contoso.Products.Subscribe/​Program.cs Adds keyed command receiver.
samples/​src/​Contoso.Shopping.Api/​Program.cs Adds clients and routing.
samples/​src/​Contoso.Shopping.Api/​appsettings.Development.json Configures Products discovery.
samples/​src/​Contoso.Shopping.Application/​Adapters/​Notifications/​INotificationAdapter.cs Defines notification adapter.
samples/​src/​Contoso.Shopping.Infrastructure/​Adapters/​Notifications/​NotificationAdapter.cs Implements notifications.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridContent.cs Adds SendGrid content model.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridEmailAddress.cs Adds email address model.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridHttpClient.cs Adds SendGrid client.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridMailRequest.cs Adds mail request model.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridOptions.cs Adds SendGrid options.
samples/​src/​Contoso.Shopping.Infrastructure/​Clients/​SendGrid/​SendGridPersonalization.cs Adds personalization model.
samples/​src/​Contoso.Shopping.Infrastructure/​GlobalUsing.cs Adds notification imports.
samples/​src/​Contoso.Shopping.Subscribe/​GlobalUsing.cs Adds subscriber imports.
samples/​src/​Contoso.Shopping.Subscribe/​Program.cs Wires notification handling.
samples/​src/​Contoso.Shopping.Subscribe/​Subscribers/​BasketCheckedOutSubscriber.cs Sends checkout notifications.
samples/​src/​Contoso.Shopping.Subscribe/​appsettings.Development.json Configures discovered Products API.
samples/​src/​Contoso.Shopping.Subscribe/​appsettings.json Adds SendGrid settings.
samples/​tests/​Contoso.Products.Test.Subscribe/​SubscriberTests.ReservationCancel.cs Tests cancellation commands.
samples/​tests/​Contoso.Products.Test.Subscribe/​SubscriberTests.ReservationConfirm.cs Tests confirmation commands.
samples/​tests/​Contoso.Products.Test.Subscribe/​SubscriberTests.cs Updates subscriber setup.
samples/​tests/​Contoso.Shopping.Test.Api/​MutateTests.Basket.cs Tests basket command publishing.
samples/​tests/​Contoso.Shopping.Test.Api/​MutateTests.cs Resets broker topology.
samples/​tests/​Contoso.Shopping.Test.Common/​ServiceBus.cs Defines shared topology.
samples/​tests/​Contoso.Shopping.Test.Relay/​RelayTests.cs Tests command queue relay.
samples/​tests/​Contoso.Shopping.Test.Subscribe/​GlobalUsing.cs Adds subscriber test imports.
samples/​tests/​Contoso.Shopping.Test.Subscribe/​SubscriberTests.BasketCheckedOut.cs Tests checkout notification.
samples/​tests/​Contoso.Shopping.Test.Subscribe/​SubscriberTests.cs Configures subscriber tests.
samples/​tests/​Contoso.Shopping.Test.Unit/​Clients/​SendGrid/​SendGridHttpClientTests.cs Tests SendGrid client.
samples/​tests/​Contoso.Shopping.Test.Unit/​EntryPoint.cs Registers test clients.
samples/​tests/​Contoso.Shopping.Test.Unit/​GlobalUsing.cs Adds test imports.
servicebus/​Config.json Adds command queue topology.
servicebus/​Reset-ServiceBusEmulator.ps1 Updates emulator reset.
src/​CoreEx.Azure.Messaging.ServiceBus/​AGENTS.md Documents keyed receiving.
src/​CoreEx.Azure.Messaging.ServiceBus/​CoreExServiceBusExtensions.DependencyInjection.cs Adds keyed receiver registration.
src/​CoreEx.Database.Postgres/​Outbox/​PostgresOutboxRelay.cs Reuses transient classification.
src/​CoreEx.Database.Postgres/​PostgresDatabase.cs Classifies transient errors.
src/​CoreEx.Database.SqlServer/​Outbox/​SqlServerOutboxRelay.cs Reuses transient classification.
src/​CoreEx.Database.SqlServer/​SqlServerDatabase.cs Classifies transient errors.
src/​CoreEx.Database/​Abstractions/​DatabaseArgsBase.cs Adds retry configuration.
src/​CoreEx.Database/​Abstractions/​DatabaseInvoker.cs Executes retry pipelines.
src/​CoreEx.Database/​Abstractions/​DatabaseInvokerResiliency.cs Defines default retry policy.
src/​CoreEx.Database/​Database.cs Exposes transient classification.
src/​CoreEx.Database/​GlobalUsing.cs Adds resilience imports.
src/​CoreEx.Database/​IDatabase.cs Extends database contract.
src/​CoreEx.Database/​Outbox/​DatabaseOutboxRelayBase.cs Delegates transient checks.
src/​CoreEx.Events/​AGENTS.md Documents destination routing.
src/​CoreEx.Events/​CoreExEventsExtensions.DependencyInjection.cs Registers named provider.
src/​CoreEx.Events/​EventData.cs Updates message metadata docs.
src/​CoreEx.Events/​EventFormatter.cs Preserves supplied metadata.
src/​CoreEx.Events/​Publishing/​FixedDestinationProvider.cs Updates provider implementation.
src/​CoreEx.Events/​Publishing/​IDestinationProvider.cs Extends destination API.
src/​CoreEx.Events/​Publishing/​NamedDestinationProvider.cs Implements command routing.
src/​CoreEx.Events/​Publishing/​README.md Documents routing behavior.
src/​CoreEx.RefData/​ReferenceDataHybridCache.TypedInvoker.cs Adds typed cache invocation.
src/​CoreEx.RefData/​ReferenceDataHybridCache.cs Uses concrete collection types.
src/​CoreEx.Template/​CoreEx.Template.csproj Packs new template assets.
src/​CoreEx.Template/​README.md Documents Aspire template.
src/​CoreEx.Template/​content/​CoreEx.Api/​src/​app-name.Api/​Program.cs Uses named destinations.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​.template.config/​template.json Defines Aspire template symbols.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire.MockHost/​Program.cs Templates WireMock host.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire.MockHost/​app-name.Aspire.MockHost.csproj Templates mock project.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​AGENTS.md Templates Aspire guidance.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​AppHost.cs Templates AppHost wiring.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​Properties/​launchSettings.json Templates launch settings.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​app-name.Aspire.csproj Templates AppHost project.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​appsettings.Development.json Templates development settings.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​app-name.Aspire/​appsettings.json Templates AppHost settings.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​solution-name.Test.Aspire/​GlobalUsing.cs Templates test imports.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​solution-name.Test.Aspire/​HostTests.cs Templates Aspire tests.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​aspire/​solution-name.Test.Aspire/​solution-name.Test.Aspire.csproj Templates E2E test project.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​AGENTS.md Updates legacy Aspire guidance.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​AppHost.cs Updates legacy AppHost.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​Extensions.cs Removes superseded helpers.
src/​CoreEx.Template/​content/​CoreEx.Aspire/​src/​app-name.Aspire/​appsettings.Development.json Updates legacy settings.
src/​CoreEx.Template/​content/​CoreEx.Core/​.template.config/​template.json Adds conditional topology files.
src/​CoreEx.Template/​content/​CoreEx.Core/​AGENTS.md Documents test topology.
src/​CoreEx.Template/​content/​CoreEx.Core/​README.md Updates generated README.
src/​CoreEx.Template/​content/​CoreEx.Core/​_Directory.Packages.props Adds Aspire package versions.
src/​CoreEx.Template/​content/​CoreEx.Core/​servicebus/​Config.json Empties generated topology.
src/​CoreEx.Template/​content/​CoreEx.Core/​tests/​app-name.Test.Common/​GlobalUsing.cs Adds Service Bus imports.
src/​CoreEx.Template/​content/​CoreEx.Core/​tests/​app-name.Test.Common/​ServiceBus.cs Templates broker topology.
src/​CoreEx.Template/​content/​CoreEx.Core/​tests/​app-name.Test.Common/​app-name.Test.Common.csproj Adds messaging dependency.
src/​CoreEx.Template/​content/​CoreEx.Relay/​tests/​solution-name.Test.Relay/​RelayTests.cs Resets relay topology.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​src/​app-name.Subscribe/​AGENTS.md Documents keyed receivers.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​src/​app-name.Subscribe/​Program.cs Templates keyed subscriber.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​tests/​solution-name.Test.Subscribe/​GlobalUsing.cs Updates test imports.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​tests/​solution-name.Test.Subscribe/​HostTests.cs Updates hosted-service tests.
src/​CoreEx.Template/​content/​CoreEx.Subscribe/​tests/​solution-name.Test.Subscribe/​SubscriberTests.cs Updates subscriber tests.
src/​CoreEx.UnitTesting/​CoreEx.UnitTesting.csproj Adds Aspire testing dependency.
src/​CoreEx.UnitTesting/​Events/​EventExpectations.cs Supports revised tester base.
src/​CoreEx.UnitTesting/​GlobalUsing.cs Adds Aspire dependencies.
src/​CoreEx.UnitTesting/​UnitTestExExpectations.ChangeLog.cs Updates expectation compatibility.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.Aspire.cs Adds AppHost helpers.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.Caching.cs Adds Redis reset helper.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.Postgres.cs Adds Aspire migration helper.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.ServiceBus.cs Adds topology reset helpers.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.SqlServer.cs Adds Aspire migration helper.
src/​CoreEx.UnitTesting/​UnitTestExExtensions.WithAspireTester.cs Adds Aspire tester support.
src/​CoreEx/​CoreEx.csproj Adds service discovery dependency.
src/​CoreEx/​CoreExExtensions.ApplicationBuilder.cs Enables HTTP service discovery.
src/​CoreEx/​Hosting/​RetryResiliency.cs Adds reusable retry policy.
tests/​CoreEx.Azure.Messaging.ServiceBus.Test.Unit/​ServiceBusReceiverTests.cs Tests keyed receiver behavior.
tests/​CoreEx.Database.Test.Unit/​DatabaseInvokerRetryTests.cs Tests database retries.
tests/​CoreEx.Events.Test.Unit/​EventFormatterTests.cs Tests metadata preservation.
tests/​CoreEx.Events.Test.Unit/​Publishing/​NamedDestinationProviderTests.cs Tests destination routing.
tests/​CoreEx.RefData.Test.Unit/​ReferenceDataHybridCacheTests.cs Tests typed cache behavior.
tests/​CoreEx.Test.Unit/​Hosting/​RetryResiliencyTests.cs Tests retry policies.
tools/​validate-template-pack.ps1 Validates new templates.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/CoreEx.Template/content/CoreEx.Core/servicebus/Config.json
Comment thread samples/aspire/Contoso.Test.Aspire/E2ETest.cs
Comment thread samples/aspire/Contoso.Test.Aspire/E2ETest.cs Outdated
Comment thread samples/src/Contoso.Shopping.Api/appsettings.Development.json Outdated
… non-flaky

- Restart the Cosmos emulator after the core tests and wait for readiness before the sample tests.
- PartitionPickerTests: assert on the average overlap over many pairs rather than a single random pair (~2% failure rate).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 29, 2026 23:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Standalone service discovery is broken, the Aspire E2E contains an asynchronous replication race, and the activation endpoint is missing established API safeguards.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Enable idempotency middleware and document POST 404 response

samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs:42

This idempotent POST can return NotFoundException, but it neither opts into the idempotency middleware nor documents the 404 response, unlike the established mutating-controller pattern. Retries therefore bypass response deduplication and the generated OpenAPI omits a real outcome.

Aspire launches project resources with 'dotnet run --no-build', which fails for
the multi-targeted sample hosts ("Your project targets multiple frameworks"), so
the hosts exited at startup and the E2E test timed out.

- Contoso.Test.Aspire.runsettings sets DOTNET_TARGET_FRAMEWORK=net10.0 for the test run.
- samples/Directory.Build.props also clears TargetFrameworks when it is set.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 01:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Standalone host resolution regresses, the new activation endpoint lacks focused integration coverage, and asynchronous E2E assertions remain timing-dependent.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Add integration coverage for product activation API

samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs:43

Add a matching ProductMutateTests.Activate.cs integration test for this new API operation. Every other Product mutation has operation-specific API coverage, while the broad Aspire happy path does not verify not-found/no-op behavior or the activated outbox event.

Medium severity Add fallback products-api endpoint for standalone Subscribe runs

samples/​src/​Contoso.Shopping.Subscribe/​appsettings.Development.json:41

This logical Aspire URI has no fallback endpoint when the Subscribe host is launched directly, so the documented non-Aspire dotnet run workflow (samples/docs/local-dev.md:158-176) can no longer resolve products-api. Add local Services:products-api endpoints; Aspire's WithReference configuration can then override those endpoints during orchestration.

TestSynchronizer incremented its enter/exit counts before recording the last name/type, so tests
polling the count could read a null value. Record the values first.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 01:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Generated local Service Bus topology is unavailable outside tests, and the new Aspire E2E contains timing races that can fail intermittently.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
Previously missed (3)

In code that hasn't changed since last review

Medium severity Missing focused API tests for product activation

samples/​src/​Contoso.Products.Api/​Controllers/​ProductController.cs:43

This adds a production API operation, but Contoso.Products.Test.Api has no activation test; the only coverage is the much broader Aspire happy path. Add focused API tests for activation success (including persistence/event output), already-active idempotency, and not-found behavior so endpoint/service regressions are isolated.

Medium severity Shopping subscriber registration conflicts with keyed guidance

samples/​tests/​Contoso.Shopping.Test.Subscribe/​SubscriberTests.BasketCheckedOut.cs:27

This new event-subscriber test resolves an unkeyed subscriber, while the updated coreex-test-subscribe guidance says event subscribers use the subscriber-events key and the templates now generate that pattern. Because these samples are the model for the new skills, align Shopping's receiver and tests with the keyed event registration (or narrow the guidance) to avoid generating code that disagrees with its reference implementation.

Low severity Undocumented public database retry API and release behavior

src/​CoreEx.Database/​Abstractions/​DatabaseArgsBase.cs:31

This introduces a public opt-in database retry API (plus provider transient classifications and relay behavior changes), but neither the PR summary nor the v4.1.0 changelog mentions it. That is a material release feature unrelated to the stated command/Aspire scope; document its behavior and compatibility impact in this release or split it into a separately reviewed change.

The E2E hosts call each other (shopping -> products) over https using the dev certificate, which
is untrusted on Linux runners (UntrustedRoot). Export it and add it to the system trust store.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 14:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

- Contoso Aspire E2E now detects the stubbed SendGrid call by polling the
  WireMock admin request journal (bounded 30x1s) instead of scraping logs.
- CoreEx.Core / CoreEx.Aspire templates emit tests/ and aspire/
  Directory.Build.props (NoWarn IDE1006, imports the solution-root props);
  validate-template-pack.ps1 asserts both.
- Instructions/skills: patch/subset contracts must be standalone (no
  inheriting a sibling and suppressing members); Aspire test guidance uses
  the admin-API polling pattern; new end-of-file rule (exactly one trailing
  newline).
- Strip extra trailing blank lines from 14 existing files.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 00:29
Explicitly start change feed from the beginning unless StartTime is configured, preventing first-start and container-reset recovery from skipping pending outbox events. Add a regression for events written before relay startup and correct the documented SDK default.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 10, 2026 18:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Its unusually broad framework, generator, infrastructure, template, and E2E changes warrant human approval after the latest CI run completes.

0 open findings

🧠 Review effort: Balanced

Refresh README capabilities, packaged skill and command inventories, expert routing, and pinned installation guidance. Complete v4.1.0 release notes and upgrade guidance, including coordinated outbox and Service Bus session mapping rollouts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 16:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Command destination casing, default-token cancellation handling, and the outbox partition migration warning have unresolved correctness or operational issues.

2 open findings

🧠 Review effort: Balanced

Comment thread src/CoreEx.Events/Publishing/NamedDestinationProvider.cs
Comment thread CHANGELOG.md
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 17:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Nested DataMap<T> non-nullability is not reflected in OpenAPI, and generated Aspire health tests currently accept unhealthy hosts.

0 open findings

2 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Traverse DataMap value types in OpenAPI schema generation

samples/​src/​Contoso.Products.Contracts/​MovementRequestProduct.cs:11

MovementRequestProduct is reached only through DataMap<MovementRequestProduct>. The OpenAPI walker does not traverse generic arguments for this CoreEx dictionary type, and its dictionary members are not schema properties, so this annotation remains nullable in the generated component schema. Traverse dictionary/additional-properties value types (or all generic arguments) and add a DataMap<T> regression test.

🧠 Review effort: Balanced

@chullybun

Copy link
Copy Markdown
Collaborator Author

Addressed both concerns in the latest review summary locally; nothing committed or pushed yet, pending review.

  • Nested DataMap<T> non-nullability: the OpenAPI walker now traverses generic arguments for CoreEx/custom wrappers as well as framework wrappers, with the visited-type guard applied before traversal to handle recursive maps. Added an actual generated-document regression using a recursive DataMap<NonNullableMapValue>: the annotated property failed before the fix (nullable: true) and now emits nullable: false, while an unannotated property remains nullable.
  • Generated Aspire health tests: API, Relay, and Subscribe checks now require HTTP 200. HTTP 503 is retried for up to 60 attempts with one-second delays; persistent 503 and other unexpected statuses fail with the resource name/path in the assertion. Updated template documentation and scaffold validation assertions to prevent restoring the permissive BeOneOf(200, 503) behavior.

Validation: all six selected OpenAPI/NSwag tests pass on net8.0, net9.0, and net10.0; the template-pack validator passes all 26 scaffold/build scenarios, including full-stack Aspire. No live Aspire environment was reset or exercised.

These concerns were posted in the review summary, not as new inline threads, so there is no additional thread to resolve.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 18:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The scaffold overrides the canonical reference-data route, and two edited files retain documentation or ordering inconsistencies.

1 open finding

🧠 Review effort: Balanced

Prefer lambda selectors for conditional-nullability mapper projections and link the shared rule from application and infrastructure mapping guidance.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 19:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cancellation classification is incorrect for default tokens, and scoped cache locks do not prevent cross-request stale reference-data publication.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

Align the ref-data schema description with the Entity configuration table.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 19:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new required ICosmosDb member is not identified as a source break, and a generated global-using file remains unsorted.

1 open finding

🧠 Review effort: Balanced

Comment thread src/CoreEx.Cosmos/CosmosDb.cs
Ignore tokenless cancellation when attributing exceptions to a caller token, and coordinate reference-data cache loads with invalidation across request scopes.

Add regressions for default-token cancellation and cross-scope stale cache publication.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Eric Sibly <eric.sibly@avanade.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 19:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new /ref scaffold route conflicts with bundled guidance that still documents /api/refdata, causing generated workflow instructions to target incorrect endpoints.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

@chullybun
chullybun merged commit 2a0ba32 into main Oct 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants