Conversation
Add specification 0026 and ADR 0056 for fixing TimedOutboxArchiver to fall back to sync Archive when only a sync outbox is registered. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add HasAsyncOutbox() and HasOutbox() to OutboxArchiver (matches OutboxProducerMediator convention) - Replace unconditional ArchiveAsync with three-way branch: async → sync via Task.Run → log warning - Remove duplicate OutboxSweeperSleeping log call - Test: When_archiving_with_sync_only_outbox_should_call_sync_archive Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Test: When_archiving_with_async_only_outbox_should_call_async_archive - Add AsyncOnlyOutboxWrapper test double Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e (AC3) - Test: When_archiving_with_both_sync_and_async_outbox_should_prefer_async - Uses InMemoryOutbox which implements both interfaces Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Test: When_archiving_with_no_outbox_configured_should_not_throw - Add NullOutbox test double (base IAmAnOutbox only) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Gates Passed
4 Quality Gates Passed
See analysis details in CodeScene
Quality Gate Profile: Clean Code Collective
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.
Code Review — PR #4066: TimedOutboxArchiver sync fallbackOverviewThis PR fixes a real bug (#3670): Positives
IssuesMedium — Test name/assertion mismatch in the "no outbox" testFile: The file is named Suggestion: Either rename the file to Minor —
|
| Area | Assessment |
|---|---|
| Core fix correctness | ✅ Correct three-way branch, no regressions |
| API design | ✅ Follows existing OutboxProducerMediator convention |
| Test coverage (branches) | ✅ All three paths covered |
| Logging assertion (no-outbox path) | should_log_warning but doesn't assert it |
| Test reliability | |
| Documentation | ✅ ADR is thorough; specs housekeeping is minor nit |
The fix is sound and the approach is the right one. The logging assertion gap is the only thing worth addressing before merge — everything else is minor.
Summary
TimedOutboxArchiver.Archive()now branches on outbox capability instead of unconditionally callingArchiveAsync, which threw when only a sync outbox was registeredHasAsyncOutbox()/HasOutbox()toOutboxArchiver, matching the existingOutboxProducerMediatorconventionOutboxSweeperSleepinglog call (was logged twice on the success path)Closes #3670
Design
Three-way branch in
TimedOutboxArchiver.Archive():HasAsyncOutbox()→await ArchiveAsync(...)(existing behavior, preferred)HasOutbox()→await Task.Run(() => Archive(...))(sync fallback)Log.NoOutboxConfigured(...)(guard, no throw)See ADR 0056 for full rationale.
Test plan
When_archiving_with_sync_only_outbox_should_call_sync_archive— core fix, sync-only outbox archives messagesWhen_archiving_with_async_only_outbox_should_call_async_archive— async-only still works (FR1/AC2)When_archiving_with_both_sync_and_async_outbox_should_prefer_async— async preferred (AC3)When_archiving_with_no_outbox_configured_should_not_throw— guard path (FR3)🤖 Generated with Claude Code