Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ These two commits collectively add:
### Recently landed (since the original architectural commits)

- **AI-assisted coding workflow tooling** (PR #15, merged 2026-05-23) — `.claude/` and `.github/` gain a deliberate, demonstrable workflow layer. Three hooks (PreToolUse async-blocker, SessionStart STATUS.md inject, PostToolUse cross-reference checker). Three project slash commands (`/new-feature-slice`, `/sync-status`, `/check-rules`). One project agent (`architecture-reviewer`). Eight curated community skills, all audited via `skill-security-auditor` before install (skill-security-auditor itself, verification-before-completion, systematic-debugging, variant-analysis, test-driven-development, using-git-worktrees, writing-plans + executing-plans). Architecture map at [.claude/architecture-map.md](../.claude/architecture-map.md) — code-graph for both AI and human orientation. New GitHub layer: `PULL_REQUEST_TEMPLATE.md` (honest "How it was built" + "Verification" sections), `.github/AI_WORKFLOW.md` (companion to README's "How it was built"), `.coderabbit.yaml` (per-path conventions encoded). Codecov upload added to CI; README gains CI/CodeQL/codecov badges. Twelve community skills *deliberately not installed* (frontend tools when no frontend, ci-cd-pipeline-builder when CI works, etc.) — curation > breadth.
- **OrderService integration tests now actually pass in CI** (this PR, in flight) — the suite was added 2026-05-15 (`76b2f2f`) but never observed green in CI; every BUILD_AND_TEST run from then through 2026-05-22 was cancelled at the runner's max because the test job hung. Two root causes:
- **OrderService integration tests now actually pass in CI** (PR #17 merged 2026-05-23) — the suite was added 2026-05-15 (`76b2f2f`) but never observed green in CI; every BUILD_AND_TEST run from then through 2026-05-22 was cancelled at the runner's max because the test job hung. First CI green: 2026-05-23 commit `12a5071e` — Catalog integration in 58s, Order integration in 40s. Two root causes:
- **`.AutoProvision()` against a fake ASB endpoint hangs host startup** — `opts.UseAzureServiceBus(connectionString).AutoProvision()` is a transport-bootstrapper hook that runs *before* `DisableAllExternalWolverineTransports()` from a test factory's `ConfigureTestServices` takes effect. The fake `sb://fake.servicebus.windows.net/...` connection string would retry with backoff until the runner killed the job. Fix: gate `.AutoProvision()` on a `Wolverine:AutoProvision` config flag (defaults `true` so dev/prod are unchanged); test factory sets it to `false`. Applied across all 4 services with ASB (Order, Payment, Shipping, Notification) — preventive variant fix since only OrderService has integration tests today.
- **`OrderApiFactory.DisposeAsync` stopped the SQL Server container before the host shut down** — Wolverine's `DurableReceiver` background service kept polling the outbox tables during the teardown window and crashed the test host on connection refused. Fix: `await base.DisposeAsync()` before `_sqlServer.DisposeAsync()`.
- **Codecov upload added to the integration-tests job** (with `--collect "XPlat Code Coverage"` on both `dotnet test` steps + a flagged `integration` upload). Codecov badge was 18.28% post-merge of PR #15 because only unit-test coverage was being uploaded; with this in place the badge should reflect the ~58% aggregate baseline.
- **Codecov upload added to the integration-tests job** (with `--collect "XPlat Code Coverage"` on both `dotnet test` steps + a flagged `integration` upload). Codecov main-branch badge jumped from **18.28% → 56.14%** post-merge — close to (slightly under) the 58.5% baseline cited above. Small gap is Coverlet attribution differences between reportgenerator's own aggregation and Codecov's per-flag combine.
- Discovered + fixed using the new `systematic-debugging` skill (Phase 1-4 discipline) + `variant-analysis` skill (one bug, sibling files) from PR #15. First real use of the new tooling.
- **Demo deployment LIVE on Fly.io** — `CatalogService.Api` deployed to https://catalog-api-demo.fly.dev with Scalar UI at `/scalar/v1`. Single Fly Machine in `lax`, auto-stops when idle (~$5/mo with $25 prepaid credit cap, no card on file = hard ceiling). Scaffolding: `DemoMode` config flag in [Program.cs](../CatalogService/CatalogService.Api/Program.cs) gates Scalar + OpenAPI + skip-HTTPS-redirect + migrate-on-startup in non-Development environments; Redis registration is conditional on a `cache` connection string so HybridCache degrades to L1-only. New [Dockerfile.catalog](../Dockerfile.catalog), [.dockerignore](../.dockerignore), [fly.toml](../fly.toml), [.github/workflows/deploy-catalog-demo-fly.yml](../.github/workflows/deploy-catalog-demo-fly.yml) (Fly path — primary), and [.github/workflows/deploy-catalog-demo.yml](../.github/workflows/deploy-catalog-demo.yml) (AWS App Runner — alternative). Setup recipe in [docs/demo-deployment.md](demo-deployment.md); narrative + gotchas + EF migration decisions in [docs/demo-deployment-story.md](demo-deployment-story.md).
- **Smoke-test debugging arc** (commits `1cb5ea8` through `8019891`) — surfaced and captured five Aspire/Wolverine 13.x gotchas: SDK/package version alignment, globally-unique subscription names, `RunAsEmulator()` for Service Bus, `IsPublishMode` gate on App Insights, `WaitFor()` for service-startup gating, Wolverine middleware requires instance methods. Each fix paired with a CLAUDE.md rule update per the Debugging Discipline.
Expand All @@ -59,15 +59,15 @@ These two commits collectively add:
- **OrderService PlaceOrder parallelization** — N sequential gRPC round-trips collapsed into two parallel phases: `Task.WhenAll` validation, then `Task.WhenAll` stock reservation. Phase split is intentional — a validation failure no longer leaves stranded reservations (previously, line 3 failing meant lines 1-2 had already reserved). gRPC calls don't touch OrderService's DbContext, so CLAUDE.md's "DbContext is not thread-safe" rule still holds. Batch-RPC shape captured as a deferred follow-up in Open Issues.
- **`WolverineEventPublisher` cancellation propagation** — all three services (`OrderService`, `PaymentService`, `ShippingService`) now `ct.ThrowIfCancellationRequested()` before calling `bus.PublishAsync`. Wolverine 5.36's `IMessageBus.PublishAsync` has no CancellationToken overload, so this is the cleanest way to refuse outbox staging for an already-cancelled request.
- **PaymentRecoveryJob — stuck-Pending sweeper.** New `BackgroundService` that wakes every minute, acquires the `payments-recovery` distributed lock (SQL Server `sp_getapplock` via [DistributedLock.SqlServer](https://github.com/madelson/DistributedLock)), finds Payments stuck Pending past the 5-min stale threshold, and marks each Failed + publishes `PaymentFailedEvent`. RowVersion is the per-row backstop if multiple sweepers race past the lock. `TimeProvider` injected for testability. `BuyerId` denormalized onto `Payment` via migration `20260521210000_AddBuyerIdToPayment` so the event can carry it. 7 unit tests via `FakeTimeProvider`. Canonical use case for the [distributed-lock pattern documented in project-decisions §22](project-decisions.md#22-dapr--considered-not-adopted-and-distributed-locks).
- **EF Core review + two follow-up fixes** (this session, uncommitted) — full audit against the `dotnet-performance` skill rubric: concurrency tokens on every aggregate (`xmin` for Postgres, `RowVersion` for SQL Server), `AsNoTracking`/projection on read paths, no sync-over-async anywhere in production code, `CancellationToken` propagation universal, outbox atomicity verified for Wolverine handler paths via `AutoApplyTransactions` + `UseEntityFrameworkCoreTransactions`. Two real findings closed:
- **EF Core review + two follow-up fixes** (commit `825d6bd` — PaymentRecoveryJob atomicity; ProductRepository identity-resolution change landed alongside) — full audit against the `dotnet-performance` skill rubric: concurrency tokens on every aggregate (`xmin` for Postgres, `RowVersion` for SQL Server), `AsNoTracking`/projection on read paths, no sync-over-async anywhere in production code, `CancellationToken` propagation universal, outbox atomicity verified for Wolverine handler paths via `AutoApplyTransactions` + `UseEntityFrameworkCoreTransactions`. Two real findings closed:
- **CatalogService ProductRepository: `AsNoTracking()` + `Include(p => p.Category)` → `AsNoTrackingWithIdentityResolution()`.** With multiple products sharing a category, plain `AsNoTracking` + `Include` duplicates the Category entity per row (the canonical EF trap). The identity-resolution variant keeps reference equality without re-enabling change tracking. Applied to `GetAllAsync` / `GetByCategoryAsync` / `SearchAsync` in [ProductRepository.cs](../CatalogService/CatalogService.Infrastructure/Repositories/ProductRepository.cs).
- **PaymentRecoveryJob outbox atomicity.** The sweeper runs outside Wolverine's handler pipeline, so `AutoApplyTransactions` doesn't wrap it — `repository.UpdateAsync(payment)` (commits Failed) followed by `eventPublisher.PublishAsync(...)` had a gap where a process crash between the two would leave the Payment Failed in-DB but the event never enqueued, stalling the saga. Fixed by adding `IPaymentRepository.ExecuteInTransactionAsync(work, ct)` (wraps `Database.BeginTransactionAsync`); the sweeper now invokes the mark+publish inside the transaction. Wolverine's `UseEntityFrameworkCoreTransactions` bridges the outbox row write into the same transaction, so entity write + event stage commit atomically. Unit tests get a pass-through stub for the new repo method.

### Build / test state
- `dotnet build` — clean, 0 warnings, 0 errors.
- `dotnet test` — 134/134 unit tests pass.
- **Integration tests** — two slices live now, *both verified green*. **Catalog** (`tests/CatalogService.Tests.Integration`, 4 tests, Postgres + Redis): migrations apply, HybridCache caches + invalidates, `xmin` token fires. **Order** (`tests/OrderService.Tests.Integration`, 4 tests, SQL Server + stubbed Wolverine transport): Wolverine outbox + saga handlers verified — `OrderPlacedEvent` flows through the durable pipeline, `PaymentCompletedEvent` consumer transitions the Order through real EF, idempotency guards work, `RowVersion` token fires. Local run 2026-05-23: 4/4 pass in 16.2s with clean shutdown. CI confirmation pending the fix PR merge (see Recently Landed). **Cross-service choreography over the real Azure Service Bus wire** is still uncovered — that's the deferred ASB-emulator slice.
- **Coverage** — Codecov badge currently reflects unit tests only. Integration-coverage upload is now configured in `.github/workflows/ci.yml` (both `dotnet test` steps in the integration-tests job emit Cobertura XML; both flagged uploads land in Codecov). The badge + dashboard will reflect the combined aggregate (~58% line / 57% branch per reportgenerator's prior measurement) after the next successful CI run on main with the integration-tests job green.
- **Integration tests** — two slices live, both green in CI and locally. **Catalog** (`tests/CatalogService.Tests.Integration`, 4 tests, Postgres + Redis): migrations apply, HybridCache caches + invalidates, `xmin` token fires. **Order** (`tests/OrderService.Tests.Integration`, 4 tests, SQL Server + stubbed Wolverine transport): Wolverine outbox + saga handlers verified — `OrderPlacedEvent` flows through the durable pipeline, `PaymentCompletedEvent` consumer transitions the Order through real EF, idempotency guards work, `RowVersion` token fires. CI duration: Catalog 58s, Order 40s. **Cross-service choreography over the real Azure Service Bus wire** is still uncovered — that's the deferred ASB-emulator slice.
- **Coverage** — Codecov main-branch badge at **56.14%** (combined unit + integration). Close to the 58.5% baseline cited above (small gap is Coverlet attribution differences between reportgenerator's aggregation and Codecov's per-flag combine — not worth optimizing).
- **Performance baselines not measured yet** — harness exists; no recorded baseline numbers.

---
Expand Down
Loading
Loading