Skip to content

Azure blob storage projection - #550

Merged
alexeyzimarev merged 48 commits into
Eventuous:devfrom
quezlatch:aspire-and-blob-storage
Aug 19, 2026
Merged

Azure blob storage projection#550
alexeyzimarev merged 48 commits into
Eventuous:devfrom
quezlatch:aspire-and-blob-storage

Conversation

@quezlatch

@quezlatch quezlatch commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Azure blob storage

Adds a new projection target for persisting state to Azure Blob Storage. The implementation provides a flexible way to project events into blob-stored state objects.

  • Added new projection support for Azure Blob Storage
  • Enables event sourcing projections to store state as blobs
  • Includes configurable blob naming and serialisation options
  • Retry option in case of race conditions

PR prepared with the help of Mistral Vibe.

quezlatch and others added 21 commits June 19, 2026 08:30
…StorageBlobsProjector

- Create new test project following Eventuous.Tests.Azure.ServiceBus structure
- Add Testcontainers.Azurite package to Directory.Packages.props
- Add IntegrationFixture with Azurite and KurrentDB containers
- Test all On method variants (sync/async, state/context) for new and existing blobs
- Test concurrent modification scenario (412 Precondition Failed)
- Test no handler scenario (returns Ignored)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
…face intent

- Add helper methods: SetupContainer, SetupExistingBlob, GetBlobState, AssertSuccess, AssertIgnored
- Rename projector classes to surface handler patterns (SyncStateProjector, etc.)
- Group tests by handler variant with clear section comments
- Test names now follow [Variant]_[Scenario]_[ExpectedBehavior] pattern
- Reduce LOC from ~450 to ~330 (-27%)
- Remove fixture parameter from CreateContext (unused)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
…obServiceClient and container name

- Add StorageBlobsProjector(BlobServiceClient, string containerName) constructor
- Update test helper methods to work with container names instead of BlobContainerClient
- Add GetContainer() helper to get BlobContainerClient from fixture
- Update all test projector classes with new constructor overload
- Update all tests to use fixture.BlobServiceClient with container names

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add Azure Blob Storage projector and .NET Aspire Azure sample
✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

Description

• Add Azure Blob Storage projector for persisting projection state with optimistic concurrency.
• Add Azurite-based integration tests covering handler variants and race-condition retries.
• Add .NET Aspire Azure sample wiring SQL, Service Bus, and Blob storage emulators.
Diagram

graph TD
  AppHost["Aspire AppHost"] --> Bookings["Bookings API"] --> Sql[("Azure SQL")]
  Bookings --> Blobs[("Blob Storage")]
  Sql --> Projector["StorageBlobsProjector"] --> Blobs
  Payments["Payments API"] --> Gateway["Eventuous Gateway"] --> Bus[("Service Bus")]
  Bus --> Bookings
  AppHost --> Payments --> Sql
  AppHost --> Bus
  AppHost --> Blobs
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use a database-backed read model (SQL/Cosmos) instead of blobs
  • ➕ Richer querying/secondary indexes without custom blob reads
  • ➕ More mature concurrency/transaction semantics for multi-document updates
  • ➖ Higher operational and cost footprint for simple “document per key” projections
  • ➖ More schema/migration work than blob-stored JSON
2. Use Azure Table Storage for key/value projection state
  • ➕ Natural fit for per-aggregate or per-user keyed state
  • ➕ Built-in ETag concurrency similar to blobs
  • ➖ Less flexible for larger nested documents vs a single JSON blob
  • ➖ Different SDK/semantics; might require more mapping code
3. Use blob leasing instead of ETag retries for write contention
  • ➕ Explicit single-writer semantics can reduce retry storms under heavy contention
  • ➕ Clearer failure mode when multiple consumers compete on the same key
  • ➖ More complexity (lease acquisition/renewal/release) and potential lease orphaning
  • ➖ Slower for low-contention workloads where optimistic writes work well

Recommendation: The chosen approach (ETag-based optimistic concurrency with optional retries) is a good default for projections that write one blob per key and can tolerate occasional retry. Consider adding jitter/backoff to RaceRetries (or documenting recommended values) if high write contention is expected; otherwise the current design is appropriately simple and integrates cleanly with existing Eventuous handler registration patterns.

Files changed (39) +1611 / -1

Enhancement (23) +902 / -0
AppHost.csCompose Aspire distributed app with SQL, Service Bus emulator, and Blob storage +39/-0

Compose Aspire distributed app with SQL, Service Bus emulator, and Blob storage

• Defines the distributed application topology: Azure SQL Server container, Service Bus emulator with queue, and Azure Storage emulator with a blob container. Wires Bookings and Payments projects to these resources and adds Scalar API reference aggregation.

samples/azure/Bookings.AppHost/AppHost.cs

Bookings.Payments.csprojCreate Payments web project with Azure client dependencies and telemetry +44/-0

Create Payments web project with Azure client dependencies and telemetry

• Adds a new net web project for the Azure Payments sample, including Azure.Storage.Blobs + Microsoft.Extensions.Azure, OpenTelemetry exporters/instrumentation, Serilog, and Swashbuckle. References Eventuous components needed for SQL Server store, gateways, and Azure Service Bus integration.

samples/azure/Bookings.Payments/Bookings.Payments.csproj

Payments.csAdd gateway transform from payment events to integration messages +31/-0

Add gateway transform from payment events to integration messages

• Implements a gateway transform that maps PaymentRecorded events into BookingPaymentRecorded integration events and targets the PaymentsIntegration stream for Service Bus production.

samples/azure/Bookings.Payments/Integration/Payments.cs

Program.csBootstrap Payments API with Swagger, telemetry, and Eventuous services +28/-0

Bootstrap Payments API with Swagger, telemetry, and Eventuous services

• Sets up Serilog logging, Swagger (Swashbuckle), OpenTelemetry, and registers Eventuous services for command discovery. Exposes OpenAPI JSON under a custom route and enables Prometheus scraping.

samples/azure/Bookings.Payments/Program.cs

Registrations.csWire Payments Eventuous stack (SQL store, gateway, Service Bus producer, Blob client) +37/-0

Wire Payments Eventuous stack (SQL store, gateway, Service Bus producer, Blob client)

• Registers Azure clients for Service Bus and BlobServiceClient via Microsoft.Extensions.Azure. Configures SQL Server event store + checkpoint store, command service, ServiceBusProducer, and a gateway subscription that publishes integration events.

samples/azure/Bookings.Payments/Registrations.cs

BookingsCommandService.csAdd booking command handlers for the Azure sample +32/-0

Add booking command handlers for the Azure sample

• Implements a CommandService for Booking aggregate with handlers for booking a room and recording a payment. Uses NodaTime to build stay periods and injects a room availability service.

samples/azure/Bookings/Application/BookingsCommandService.cs

BookingsQueryService.csAdd query service wrapper for blob-backed MyBookings projection +7/-0

Add query service wrapper for blob-backed MyBookings projection

• Provides a small service that loads per-user bookings by delegating to a keyed MyBookingsProjection instance.

samples/azure/Bookings/Application/BookingsQueryService.cs

Commands.csDefine booking command DTOs used by HTTP API and command service +17/-0

Define booking command DTOs used by HTTP API and command service

• Adds BookRoom and RecordPayment command records for the Azure sample APIs.

samples/azure/Bookings/Application/Commands.cs

BookingDocument.csIntroduce BookingDocument read model stored in blob projections +16/-0

Introduce BookingDocument read model stored in blob projections

• Defines the document shape used by blob projections to represent booking details and payment state.

samples/azure/Bookings/Application/Queries/BookingDocument.cs

BookingStateProjection.csAdd blob projector for per-booking BookingDocument state +27/-0

Add blob projector for per-booking BookingDocument state

• Implements a StorageBlobsProjector-based projection that folds booking events into a BookingDocument stored in the "bookings-container" blob container.

samples/azure/Bookings/Application/Queries/BookingStateProjection.cs

MyBookings.csDefine per-user MyBookings document model +10/-0

Define per-user MyBookings document model

• Adds a user-centric bookings list document (immutable list) suitable for blob storage projection and query endpoints.

samples/azure/Bookings/Application/Queries/MyBookings.cs

MyBookingsProjection.csAdd blob projector for per-user booking lists with fallback reads +45/-0

Add blob projector for per-user booking lists with fallback reads

• Projects RoomBooked/BookingCancelled into a per-user MyBookings blob, with a custom blob id derived from event context or existing stream state. Adds a helper method to load the document from Blob Storage with a 404 -> null behavior.

samples/azure/Bookings/Application/Queries/MyBookingsProjection.cs

Bookings.csprojCreate Bookings web project with blob projection and Azure dependencies +35/-0

Create Bookings web project with blob projection and Azure dependencies

• Adds a new net web project for the Azure Bookings sample, including Azure blob client packages, OpenTelemetry/Serilog/Swagger, and a reference to the new Eventuous.Azure.Storage.Blobs project.

samples/azure/Bookings/Bookings.csproj

CommandApi.csAdd booking command HTTP endpoints +28/-0

Add booking command HTTP endpoints

• Implements a CommandHttpApiBase controller exposing endpoints to book a room and (for demo) record a payment directly.

samples/azure/Bookings/HttpApi/Bookings/CommandApi.cs

QueryApi.csAdd booking aggregate query endpoint via IEventReader +18/-0

Add booking aggregate query endpoint via IEventReader

• Adds a controller endpoint that loads BookingState from the event store by stream id.

samples/azure/Bookings/HttpApi/Bookings/QueryApi.cs

Logging.csAdd shared Serilog console logging configuration +22/-0

Add shared Serilog console logging configuration

• Provides a reusable Serilog configuration used by the Azure sample services, including overrides for noisy namespaces.

samples/azure/Bookings/Infrastructure/Logging.cs

Telemetry.csAdd OpenTelemetry metrics/tracing configuration with optional OTLP +41/-0

Add OpenTelemetry metrics/tracing configuration with optional OTLP

• Configures OpenTelemetry metrics (Prometheus exporter, optional OTLP) and tracing (Zipkin fallback when OTLP is absent), including Eventuous instrumentation.

samples/azure/Bookings/Infrastructure/Telemetry.cs

Payments.csAdd Service Bus-driven integration handler to record payments +35/-0

Add Service Bus-driven integration handler to record payments

• Defines an Eventuous subscription handler that consumes BookingPaymentRecorded integration events and issues a RecordPayment command against the Booking aggregate.

samples/azure/Bookings/Integration/Payments.cs

Program.csBootstrap Bookings API with controllers, query route, telemetry, and Spyglass +44/-0

Bootstrap Bookings API with controllers, query route, telemetry, and Spyglass

• Sets up JSON options for NodaTime, Swagger, OpenTelemetry, Eventuous, and Spyglass. Adds a minimal API query endpoint for per-user bookings loaded from blob projection state.

samples/azure/Bookings/Program.cs

Registrations.csWire Bookings Eventuous stack (SQL store, blob projections, Service Bus subscription) +59/-0

Wire Bookings Eventuous stack (SQL store, blob projections, Service Bus subscription)

• Registers default event serializer with NodaTime support, Azure clients (Service Bus + BlobServiceClient), SQL Server event store/checkpoints, and two subscriptions: SQL-based projections using blob projectors, and Service Bus subscription for payment integration.

samples/azure/Bookings/Registrations.cs

Eventuous.Azure.Storage.Blobs.csprojAdd new Eventuous.Azure.Storage.Blobs library project +39/-0

Add new Eventuous.Azure.Storage.Blobs library project

• Introduces a new Azure Blobs integration library with Azure.Storage.Blobs dependency and a packaged README. Includes InternalsVisibleTo for its new test project and links shared tooling sources.

src/Azure/src/Eventuous.Azure.Storage.Blobs/Eventuous.Azure.Storage.Blobs.csproj

StorageBlobProjectorOptions.csAdd options object for blob projector serialization and retry behavior +33/-0

Add options object for blob projector serialization and retry behavior

• Defines configurable JSON serializer options, custom serialize/deserialize delegates, and a RaceRetries setting for handling write races.

src/Azure/src/Eventuous.Azure.Storage.Blobs/StorageBlobProjectorOptions.cs

StorageBlobsProjector.csImplement StorageBlobsProjector with ETag concurrency and optional retries +215/-0

Implement StorageBlobsProjector with ETag concurrency and optional retries

• Adds a generic event handler that materializes per-key state into JSON blobs, supports multiple handler registration patterns (sync/async, with/without context), and allows custom blob id selection. Implements optimistic concurrency via ETags for updates and If-None-Match for inserts, with configurable retries on 409/412 responses.

src/Azure/src/Eventuous.Azure.Storage.Blobs/StorageBlobsProjector.cs

Bug fix (1) +1 / -1
ServiceBusSubscription.csMake eventSerializer parameter optional in ServiceBusSubscription ctor +1/-1

Make eventSerializer parameter optional in ServiceBusSubscription ctor

• Updates the constructor signature to default eventSerializer to null, improving DI ergonomics and aligning with other optional dependencies.

src/Azure/src/Eventuous.Azure.ServiceBus/Subscriptions/ServiceBusSubscription.cs

Tests (4) +599 / -0
Eventuous.Tests.Azure.Storage.Blobs.csprojAdd Azure Storage.Blobs integration test project +19/-0

Add Azure Storage.Blobs integration test project

• Creates a dedicated test project for StorageBlobsProjector with dependencies on Azure.Storage.Blobs and Testcontainers.Azurite/KurrentDb, plus shared subscription test infrastructure.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/Eventuous.Tests.Azure.Storage.Blobs.csproj

IntegrationFixture.csAdd Azurite-backed fixture providing BlobServiceClient for integration tests +49/-0

Add Azurite-backed fixture providing BlobServiceClient for integration tests

• Starts an Azurite container for the test suite and exposes a BlobServiceClient. Configures the default event serializer used by tests.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/Fixtures/IntegrationFixture.cs

StorageBlobsProjectorTests.csAdd comprehensive tests for StorageBlobsProjector handler variants and races +517/-0

Add comprehensive tests for StorageBlobsProjector handler variants and races

• Validates new-blob creation and existing-blob updates across sync/async and context-aware handlers, including custom blob id generation. Adds concurrency tests for ETag mismatch failures and a retry-success scenario when RaceRetries is enabled.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/StorageBlobsProjectorTests.cs

TestEvent.csAdd typed test event with EventType registration for blob projector tests +14/-0

Add typed test event with EventType registration for blob projector tests

• Defines a TestEvent record annotated with EventType and registers known event types, supporting projector handler registration and type mapping in tests.

src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/TestEvent.cs

Documentation (1) +1 / -0
README.mdAdd placeholder README for Azure Storage.Blobs integration +1/-0

Add placeholder README for Azure Storage.Blobs integration

• Adds an initial (WIP) README file to be included in the NuGet package.

src/Azure/src/Eventuous.Azure.Storage.Blobs/README.md

Other (10) +108 / -0
Directory.Packages.propsAdd Aspire + Azure Blobs + Azurite and OTEL SqlClient package versions +8/-0

Add Aspire + Azure Blobs + Azurite and OTEL SqlClient package versions

• Introduces pinned package versions for Aspire Azure hosting components, Azure.Storage.Blobs, Microsoft.Extensions.Azure, Scalar.Aspire, Testcontainers.Azurite, and OpenTelemetry SqlClient instrumentation. This enables both the new blob projector feature and the Aspire sample to compile with consistent dependency versions.

Directory.Packages.props

Eventuous.slnxRegister Azure Storage.Blobs projects and new Azure Aspire sample projects +8/-0

Register Azure Storage.Blobs projects and new Azure Aspire sample projects

• Adds the new Eventuous.Azure.Storage.Blobs library and its test project to the solution structure. Also adds a new Samples/Azure folder containing the Aspire AppHost plus Bookings/Payments/domain projects.

Eventuous.slnx

Bookings.AppHost.csprojAdd Aspire AppHost project with Azure hosting packages +25/-0

Add Aspire AppHost project with Azure hosting packages

• Creates a net10.0 Aspire AppHost executable referencing the Bookings and Payments projects. Adds package references for Aspire Azure ServiceBus/Sql/Storage and Scalar.Aspire.

samples/azure/Bookings.AppHost/Bookings.AppHost.csproj

appsettings.Development.jsonDevelopment logging defaults for AppHost +8/-0

Development logging defaults for AppHost

• Adds basic logging level configuration for local development runs of the Aspire AppHost.

samples/azure/Bookings.AppHost/appsettings.Development.json

appsettings.jsonAppHost logging configuration (Aspire.Hosting.Dcp warnings) +9/-0

AppHost logging configuration (Aspire.Hosting.Dcp warnings)

• Sets default log levels and reduces Aspire DCP noise by elevating it to Warning.

samples/azure/Bookings.AppHost/appsettings.json

Bookings.Domain.csprojAdd Azure sample domain project linking existing KurrentDB sample sources +16/-0

Add Azure sample domain project linking existing KurrentDB sample sources

• Creates a domain project for the Azure sample by referencing core Eventuous domain/shared projects and linking domain source files from the existing kurrentdb sample directory. Adds NodaTime dependency.

samples/azure/Bookings.Domain/Bookings.Domain.csproj

appsettings.jsonPayments sample configuration placeholders for Aspire-provided connection strings +14/-0

Payments sample configuration placeholders for Aspire-provided connection strings

• Adds ConnectionStrings placeholders for the Aspire-provisioned SQL, Service Bus emulator, and blobs endpoints, plus basic logging/host settings.

samples/azure/Bookings.Payments/appsettings.json

appsettings.jsonBookings sample configuration placeholders for Aspire connection strings +8/-0

Bookings sample configuration placeholders for Aspire connection strings

• Adds Aspire-provided ConnectionStrings placeholders for SQL, Service Bus emulator, and blob storage, plus host configuration.

samples/azure/Bookings/appsettings.json

Directory.Build.propsForce net10.0 target framework for Azure sample tree +7/-0

Force net10.0 target framework for Azure sample tree

• Adds a sample-scoped build props file that imports the repo defaults and sets target framework(s) to net10.0 for the Azure sample projects.

samples/azure/Directory.Build.props

aspire.config.jsonDeclare Bookings.AppHost as the Aspire appHost entrypoint +5/-0

Declare Bookings.AppHost as the Aspire appHost entrypoint

• Adds Aspire configuration pointing tooling at the AppHost project path.

samples/azure/aspire.config.json

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Missing .NoContext() in StorageBlobsProjector ✓ Resolved 📘 Rule violation ☼ Reliability
Description
New library code awaits multiple async I/O operations without applying .NoContext(), which breaks
the repository convention for ConfigureAwait(false) and can increase deadlock risk in library
consumers. This affects blob download/upload and internal async flows in the projector.
Code

src/Azure/src/Eventuous.Azure.Storage.Blobs/StorageBlobsProjector.cs[R163-213]

+        public async ValueTask<EventHandlingStatus> Handle(IMessageConsumeContext context) {
+            typedContext = context as MessageConsumeContext<TEvent> ?? new MessageConsumeContext<TEvent>(context);
+            var blobId = GetBlobId == null
+                ? context.Stream.GetId()
+                : await GetBlobId(typedContext);
+            var blobName = projector.GetBlobName(blobId, typedContext);
+
+            blobClient = projector.GetBlobContainerClient(blobName);
+
+            return await ModifyBlobWithRetries(projector._raceRetries);
+        }
+
+        private async Task<EventHandlingStatus> ModifyBlobWithRetries(int retries) {
+            try {
+                await ModifyBlob();
+                return EventHandlingStatus.Success;
+            } catch (RequestFailedException ex) when (ex.Status == 412 || ex.Status == 409) {
+                return retries > 0 ? await ModifyBlobWithRetries(retries - 1) : EventHandlingStatus.Failure;
+            }
+        }
+
+        private async Task ModifyBlob() {
+            try {
+                BlobDownloadResult blobContent = await blobClient.DownloadContentAsync();
+
+                var content = blobContent.Content;
+                var current = projector.Deserialize(content);
+
+                var uploadOptions = new BlobUploadOptions {
+                    Conditions = new BlobRequestConditions { IfMatch = blobContent!.Details.ETag }
+                };
+                await UploadUpdated(current, uploadOptions);
+            } catch (RequestFailedException ex) when (ex.Status == 404) {
+                // Blob doesn't exist, start with a new instance
+                var insertOptions = new BlobUploadOptions {
+                    Conditions = new BlobRequestConditions { IfNoneMatch = ETag.All }
+                };
+                await UploadUpdated(new T(), insertOptions);
+            }
+        }
+
+        private async Task UploadUpdated(T current, BlobUploadOptions uploadOptions) {
+            var task = EventHandler(typedContext, current);
+            var updated = task.IsCompletedSuccessfully
+                ? task.Result
+                : await task;
+            var json = projector.Serialize(updated);
+
+            using var stream = new MemoryStream(json);
+            var response = await blobClient.UploadAsync(stream, uploadOptions, typedContext.CancellationToken);
+        }
Evidence
PR Compliance ID 1 requires async I/O awaits to apply .NoContext(). In StorageBlobsProjector.cs,
several blob SDK calls and internal async operations are awaited without .NoContext() (e.g.,
download/upload and retry paths).

CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Operations and Apply NoContext(): CLAUDE.md: Use Asynchronous APIs for All I/O Op...

Comment thread src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs
Comment thread src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/StorageBlobsProjectorTests.cs Outdated
Comment thread samples/azure/Bookings/Program.cs Outdated
Comment thread src/Azure/src/Eventuous.Azure.Storage.Blobs/StorageBlobsProjector.cs Outdated
Comment thread src/Azure/src/Eventuous.Azure.Storage.Blobs/StorageBlobsProjector.cs Outdated
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Test Results

 46 files  + 24   46 suites  +24   11m 57s ⏱️ -34s
427 tests + 58  427 ✅ + 58  0 💤 ±0  0 ❌ ±0 
770 runs  +390  770 ✅ +390  0 💤 ±0  0 ❌ ±0 

Results for commit 4edc5c9. ± Comparison against base commit 3cb68c2.

This pull request removes 5 and adds 63 tests. Note that renamed tests count towards both.
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(08/03/2026 14:31:31 +00:00)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(08/03/2026 14:31:31)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(d6ea070b-8d6b-46d6-9e04-bb9b4acedc59)
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 4, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-08-03T14:31:52.5505416+00:00 })
Eventuous.Tests.Subscriptions.SequenceTests ‑ ShouldReturnFirstBefore(CommitPosition { Position: 0, Sequence: 1, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 6, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 8, Timestamp: 2026-08-03T14:31:52.5505416+00:00 }, CommitPosition { Position: 0, Sequence: 2, Timestamp: 2026-08-03T14:31:52.5505416+00:00 })
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(08/19/2026 16:41:17 +00:00)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(08/19/2026 16:41:17)
Eventuous.Tests.Azure.ServiceBus.IsSerialisableByServiceBus ‑ Passes(b086e253-c9da-484f-be33-d105b5047b41)
Eventuous.Tests.KurrentDB.Store.Read ‑ ShouldReadStreamToEnd
Eventuous.Tests.KurrentDB.Store.Read ‑ ShouldReadStreamToEndFromPosition
Eventuous.Tests.KurrentDB.Store.Read ‑ ShouldReadStreamToEndWithExactPageMultiple
Eventuous.Tests.KurrentDB.Store.Read ‑ ShouldRejectInvalidPageSizeReadingToEnd
Eventuous.Tests.KurrentDB.Store.Read ‑ ShouldReturnNothingWhenReadingMissingStreamToEnd
Eventuous.Tests.KurrentDB.Store.Read ‑ ShouldThrowWhenReadingMissingStream
Eventuous.Tests.KurrentDB.Store.Read ‑ ShouldThrowWhenReadingMissingStreamBackwards
…

♻️ This comment has been updated with latest results.

@alexeyzimarev

Copy link
Copy Markdown
Contributor

Thanks so much for this @quezlatch — really nice work, and I appreciate you building it on top of your Service Bus piece. The blob projector is clean, the ETag-based optimistic concurrency is exactly right, and I love that you backed it with proper Azurite testcontainer tests covering the concurrent-modification cases. The Aspire sample is genuinely cool too — distributed debugging "just working" is a great thing to show off. 🙂

A few things before I can merge, mostly housekeeping rather than anything wrong with the code:

  • Would you mind splitting it into two PRs? The blob storage projector and the Aspire sample are both great, but they're independent, and separating them makes each much easier to review and land. Totally fine to keep them as a stack if that's easier for you.
  • The README is still wip 😄 — since the csproj packs it as the NuGet package readme, could you flesh it out with a short intro and a usage snippet before we publish? Happy to help wordsmith it.
  • Docs: new public API needs a matching PR over in eventuous-docs. If you point me at roughly what you'd want covered I can help get that started.
  • One thing worth documenting: the projector keeps no stream position in the blob, so on a projection rebuild (or redelivery) additive handlers will re-apply — e.g. the sample's MyBookings would add a booking twice. That's a reasonable trade-off for this style of projection, but let's call it out clearly so users aren't surprised.

Couple of tiny optional nits: the AddAzureClients(async builder => …) calls in the sample registrations are effectively async void (no real await in there) — dropping the async is cleaner; and DownloadContentAsync could take the context's cancellation token.

None of this is a big deal — the hard part is done and it's looking great. Thanks again for contributing! 🙏

@quezlatch

Copy link
Copy Markdown
Contributor Author

I will take a stab at the docs as well. Assuming it should go in the Infrastructure section. Not sure whether it's time for a Azure subsection yet though

@quezlatch quezlatch changed the title Aspire and azure blob storage Azure blob storage projection Jul 16, 2026
@quezlatch

Copy link
Copy Markdown
Contributor Author

#556 is stacked on top of this

@alexeyzimarev alexeyzimarev 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.

Thanks for the update. I found one correctness issue that blocks the idempotency guarantee, plus a packed README type error. I reproduced the idempotency issue with two Azurite regression tests; both modes returned Success for a non-consecutive duplicate. Please address the inline comments.

Comment thread src/Azure/src/Eventuous.Azure.Storage.Blobs/BlobStorageProjector.cs Outdated
Comment thread src/Azure/src/Eventuous.Azure.Storage.Blobs/README.md Outdated
@quezlatch

Copy link
Copy Markdown
Contributor Author

I've addressed those issues. Hopefully it looks better now. I've also updated the docs and merged to the stacked #556 PR

@quezlatch
quezlatch requested a review from alexeyzimarev August 4, 2026 20:19
alexeyzimarev and others added 6 commits August 19, 2026 18:03
Eventuous#568)

* fix(persistence): make reads stream and add paged read-to-end

KurrentDB ReadEvents/ReadEventsBackwards materialized the entire requested
range (raw ResolvedEvent[] plus deserialized StreamEvent[]) before the first
yield, so IAsyncEnumerable consumers got O(stream) memory instead of
streaming. Rewrite both as true streaming iterators that map exceptions per
enumerator advance and hold at most one deserialized event at a time.

Add IEventReader.ReadStreamToEnd extension that reads a stream to the end in
pages, so count: int.MaxValue stops being the read-to-end idiom, and make
ReadStream delegate to it, fixing page advancement for truncated streams.
Document the memory semantics on IEventReader.

New contract tests exposed two pre-existing provider bugs, also fixed:
- Sqlite reads never threw StreamNotFound for a missing stream; empty read
  results are now verified with StreamExists in SqlEventStoreBase
- Postgres and SqlServer overflowed reading backwards from
  StreamReadPosition.End (long.MaxValue into an INT parameter); the client
  parameter is now clamped to the 32-bit position range

Closes Eventuous#567

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(persistence): harden paged reads per review findings

Address review findings on the streaming reads change:

- KurrentDBEventStore reads now deliver the requested count even when
  non-deserializable system events are skipped, issuing follow-up reads from
  the last received position. A short read now reliably means the stream end,
  which ReadStreamToEnd's paging termination depends on.
- ReadStreamToEnd rejects non-positive page sizes instead of spinning forever
  on readers that complete immediately for count <= 0.
- TieredEventReader no longer throws StreamNotFound when reading past the end
  of an existing stream; it throws only when both tiers report the stream
  missing.
- RedisStore distinguishes a missing stream from a read past the stream end
  by checking key existence when a read returns nothing.
- IEventReader docs now state the short-read and past-end contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(persistence): bound tiered reads and make Redis reads inclusive

Address the second review round:

- TieredEventReader bounds the archive gap request and the combined result
  to the requested count, so a read across a real archive/hot boundary no
  longer yields more events than asked for. Reading backwards past a hot
  store that bottoms out at revision 0 no longer crashes constructing a
  negative read position.
- RedisStore reads use an inclusive range read (XRANGE) instead of the
  exclusive XREAD, matching the IEventReader position contract and the paged
  read extensions that advance from the last revision + 1 — pages no longer
  silently skip the event at the page boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(redis): make stream positions round-trip for store-written entries

Address the third review round:

- The append_events Redis function now assigns explicit entry IDs
  (millisecond-0, bumping the millisecond past the last entry when needed)
  instead of auto-generated ones, so every position the store writes
  round-trips through the millisecond*10+sequence encoding and paged reads
  no longer silently truncate same-millisecond bursts.
- Reading a legacy entry whose auto-generated ID carries a sequence number
  above 9 now throws NotSupportedException with a clear message instead of
  silently garbling the position.
- Relax IEventReader/ReadStreamToEnd memory docs to promise memory
  proportional to count/page size rather than capped at one page, matching
  the tiered reader which briefly holds up to two bounded pages.
- Stabilize the Redis test fixture: abortConnect=false stops the first
  connection attempt from aborting when it races the freshly started
  container.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(redis): fail loudly on legacy entry IDs hidden behind a page boundary

A legacy auto-generated entry ID with sequence 10+ sorts below the decoded
form of the position that follows sequence 9, so a paged read could skip it
before the read-side guard ever materialized it. Reads now probe the gap
between the requested position and its decoded ID and throw
NotSupportedException when unreachable legacy entries exist there.

Also reverts the test fixture connection hardening, moved to a separate PR
to keep this one focused.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(redis): state the legacy position detection boundary

Document that the read-side gap probe is complete for every position this
store version can produce, and why positions minted by pre-fix versions from
unrepresentable entries are inherently ambiguous (the encoding maps both
legacy 12345-20 and valid 12347-0 to 123470). Add a test pinning that any
read from the start of a legacy burst stream rejects the first
unrepresentable entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(redis): reject resumed reads on streams with unrepresentable entry IDs

A position minted by a pre-fix reader from a legacy entry with a
multi-carry sequence number decodes past other legacy entries, which a
resumed read then silently skipped. Since every entry that can hide from a
position has a sequence number above 9, a stream is safe for resumed reads
exactly when it holds no such entry. Reads from a non-zero position now
verify that server-side (check_stream_clean function, clean verdict cached
in a hash; entries written by the current store always carry sequence 0)
and conservatively reject dirty streams with NotSupportedException naming
the offending entry, replacing the single-carry gap probe. The caveat and
the read-from-start migration guidance are documented on the public
ReadEvents API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(redis): make position validation incremental and its cache sound

Address the seventh review round:

- Replace the server-side clean-stream function and its permanent name-keyed
  Redis marker with client-side validation in the store: the stream is
  scanned in bounded XRANGE pages that observe the caller's cancellation
  token, so the Redis server is never blocked for the length of the stream.
- The verdict is cached per store instance, anchored on the first entry ID:
  Redis only accepts appends with increasing entry IDs, so a validated
  prefix can't gain entries, later reads only scan the delta above the last
  validated ID, a recreated stream (different first entry) triggers a full
  rescan, and a missing stream records no verdict — deleting, recreating,
  restoring, or importing legacy entries can no longer inherit a stale
  clean verdict.
- Legacy-entry seeding in tests shares one connection handle instead of
  opening one per appended entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(redis): drop the position validation cache, validate per read

Address the eighth review round: no cache anchored on observable stream
state is sound, because Redis exposes no immutable per-key generation
identity — a stream restored with the same first entry defeated the
first-entry anchor, and the cache grew unboundedly per stream name.
Resumed reads now validate the prefix below the decoded position on every
call, in bounded cancellable pages, scanning only entries the read itself
won't materialize. Stateless validation can't go stale, holds no memory,
and closes the head-read/scan TOCTOU; the per-read cost is documented on
the public API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(redis): honor the exception contract for unsigned ID sequences

Redis stream ID components are unsigned 64-bit values; parsing the sequence
with long.Parse raised OverflowException for sequences beyond long range
instead of the documented NotSupportedException. Both the revision
conversion and the validation scan now parse as ulong and range-check, and
the millisecond part is range-checked before the signed conversion.

Also document the operational requirement that pre-fix writers are quiesced
before resumed reads are used: an old writer racing the gap between prefix
validation and the data read can append an unrepresentable entry below the
requested position, which only the next resumed read can reject.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(redis): reject positions overflowing at the encoding boundary

At milliseconds == long.MaxValue / 10 only sequences up to
long.MaxValue % 10 fit the signed position; sequence 8 and 9 wrapped to a
negative revision instead of throwing the documented NotSupportedException.
Also widen the documented quiescence requirement to every writer that
doesn't use this store version's explicit entry ID scheme, including
external XADD with auto-generated IDs, not only pre-0.16 store versions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(persistence): stop paged reads advancing past the maximum revision

An event at revision long.MaxValue that fills an exact page made
ReadStreamToEnd compute lastRevision + 1, wrapping negative and throwing
from the StreamReadPosition constructor after yielding the event. The
maximum revision is the end of the representable position space, so the
paged read now completes there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
A read that faults or is cancelled before the first response parks the
failure in two places: the message channel, which the enumerator
observes, and the public ReadState task, which nothing awaits. The
faulted ReadState task was then collected unobserved and surfaced on the
finalizer thread as a TaskScheduler.UnobservedTaskException.

Every read is affected, not just cancelled ones: the leak window is
"fault before the first response", so connection failures, auth
failures, deadline expiry and server unavailability all leak too.

Client 1.4.1 observes the fault at the source, and also fixes two
sibling sinks that no consumer can reach from outside: SharingProvider's
call-invoker boxes and the batch appender's fire-and-forget send loop.
Measured over 20 reads that fail before the first response, against a
closed port: 59 unobserved exceptions on 1.4.0 (18 from ReadState, 21
from SharingProvider retries, 20 from disposed boxes), 0 on 1.4.1.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ventuous#572)

* fix(test): stop Redis fixture aborting on first connection attempt

The Redis test fixture connects right after the container reports ready, and
the first connection attempt occasionally races the server, failing the whole
fixture initialization with RedisConnectionException before any test runs.
abortConnect=false makes the multiplexer keep retrying instead of aborting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Connect the Redis test fixture asynchronously with a shared multiplexer

Address the review note: replace the synchronous ConnectionMultiplexer.Connect
inside InitializeAsync with an awaited ConnectAsync. Connect once and share
the multiplexer across tests instead of opening a new connection per
GetDatabase call, and dispose it with the fixture; abortConnect=false is
preserved so the first attempt keeps retrying when it races the freshly
started container.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
- Remove leftovers from the extracted Aspire sample: orphan package
  versions and an unrelated ServiceBusSubscription signature change
- Consolidate JSON serialization config into BlobStorageProjectorOptions.JsonOptions,
  dropping the constructor serializerOptions parameter
- Warn when ByGlobalPosition idempotency receives events with global
  position 0, and document that the mode requires real global positions
- Add copyright headers, follow .editorconfig accessibility and naming
  conventions, inline the misnamed GetBlobContainerClient, drop the
  redundant On<TEvent> overload and the manual ValueTask fast-path
- Remove dead event store scaffolding and KurrentDB references from the
  test project, dedupe concurrent-modification test lambdas
- Fix README: stale IOptions claim, blob naming example, container
  existence requirement

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Catch 404 only for the projection blob read (filtered to BlobNotFound)
  and 412/409 only for the conditional upload (filtered to
  ConditionNotMet/BlobAlreadyExists), so exceptions thrown by the
  user-supplied event handler are never misclassified as ETag races or
  missing-blob conditions and the handler is never re-invoked for them
- Percent-encode Stream and MessageId blob metadata values: Azure
  requires ASCII metadata, while stream names and message ids can be
  arbitrary strings (e.g. Booking-Ålesund previously failed uploads
  with InvalidMetadata)
- Add tests for both: handler-thrown RequestFailedException propagates
  without retries, and Unicode stream names project successfully with
  encoded metadata

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deserialization and upload preparation (JSON serialization through the
user-configurable options, upload options and metadata construction) now
run outside the try blocks, so each catch classifies exclusively its own
SDK call: DownloadContentAsync for the missing-blob path and UploadAsync
for the concurrency-conflict path. Exceptions from user-supplied JSON
converters can no longer be misread as blob conditions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@alexeyzimarev alexeyzimarev 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.

Cleanup applied and independent review passed (3 rounds, clean). Approving to supersede my earlier change request.

@alexeyzimarev
alexeyzimarev merged commit 0f19628 into Eventuous:dev Aug 19, 2026
14 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.

3 participants