refactor: migrate remaining flat/forwarding RetryableError impls to derive (tier 1)#3757
refactor: migrate remaining flat/forwarding RetryableError impls to derive (tier 1)#3757insipx wants to merge 2 commits into
Conversation
…able)]
Replace ~28 hand-written `impl RetryableError` blocks across 9 crates with the
new `#[derive(Retryable)]`. Behavior is preserved per-variant:
- #[from] variants auto-forward to the inner error's is_retryable()
- foreign-wrapping #[from] variants (prost/http/derive_builder/etc) carry an
explicit #[retry(true|false)] since the inner type can't impl RetryableError
- inverted 'retryable-unless' enums use #[retry(default = true)] + #[retry(false)]
- ClientError::Generic uses #[retry(when = this.contains("database is locked"))]
- VerifierError uses #[retry(true)] + #[retry(inherit)]
Golden equivalence tests added for every non-trivial migrated enum.
Kept hand-written: specialized RetryableError<Mls> openmls impls, collection
aggregators (ReceiveErrors), and enums matching into foreign inner errors
(StorageError, ConnectionError, ApiClientError, EnvelopeError,
GroupMessageProcessingError, SubscribeError, ...).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…erive (tier 1) Migrates the 17 remaining non-specialized hand-written impls: GroupError, SubscribeError, StorageError, ConnectionError, ApiClientError, PlatformStorageError (native+wasm), GroupMessageProcessingError, CommitLogError, EnvelopeError, QueryError, GrpcError, BootstrapSynthesisError, stream errors, and four flat validation enums. Multi-field forwards use #[retry(when = source.is_retryable())]; collection aggregation uses when over .any(); inverted enums use default = true. Pre-step: ProcessMessageWithAppDataError now derives Retryable itself (with a ProcessMessageError<S>: RetryableError<Mls> where-bound), letting the outer nested match collapse to #[retry(inherit)]. Still hand-written (intentionally): the 22 specialized RetryableError<Mls> impls for foreign openmls/diesel types, GroupAppDataError (instantiation- specific), ReceiveErrors/SyncSummary aggregator one-liners, and test mocks. 32 golden tests pin pre-migration behavior; adversarial audit re-derived all 204 variants against the saved ground truth: zero mismatches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @insipx's task —— View job Code Review CompleteReview Date: 2026-06-10 20:02 UTC OverviewThis PR successfully completes the migration of the remaining hand-written ✅ Strengths
FindingsNo critical issues found. The migration is high-quality with excellent attention to detail. Minor observations (not blocking):
Code Quality Assessment
ConclusionThis is a well-executed refactoring that:
Recommendation: Approve and merge. The PR is ready to land after #3754. |
ApprovabilityVerdict: Approved Mechanical refactoring that migrates manual You can customize Macroscope's approvability policy. Learn more. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## insipx/retryable-migration #3757 +/- ##
==============================================================
+ Coverage 84.63% 84.91% +0.27%
==============================================================
Files 407 407
Lines 59818 59871 +53
==============================================================
+ Hits 50629 50840 +211
+ Misses 9189 9031 -158 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
526c596 to
89a43a6
Compare
Summary
Stacked on #3754 (which stacks on #3753) — merge in order.
Completes the
#[derive(Retryable)]migration for every remaining impl that the macro can express, including the heavyweights deliberately deferred from #3754:GroupError(45 variants),SubscribeError,StorageError,ConnectionError,ApiClientError,GroupMessageProcessingError. 17 hand-written impls deleted across 6 crates + xnet.What each shape became
#[retry(inherit)]GroupError::LockUnavailable,SubscribeError::StreamStale,EnvelopeError::Decode,ApiClientError::Http/Expired, …) →#[retry(true)]GrpcError, wasmPlatformStorageError) →#[retry(default = true)]; all-false validation enums → bare derive, zero attrsApiClientError::ClientWithEndpoint,BootstrapSynthesisError::IdentityUpdateLookup,CommitLogError::FailedToSendReadd) →#[retry(when = source.is_retryable())]CommitLogError::FailedReadds) →#[retry(when = errors.iter().any(|e| e.is_retryable()))]GroupMessageProcessingError's nested match collapsed toinheritby deriving on the innerProcessMessageWithAppDataError(the one API-surface change: that generic enum gains aProcessMessageError<S>: RetryableError<Mls>where-bound, per the derive's documented generics rule; the only workspace instantiation already satisfies it)Still hand-written, by design
The 22 specialized
impl RetryableError<Mls> for openmls::*/diesel orphan-rule impls (mirror-type macro support would cost more code than it removes),GroupAppDataError<SqlKeyStoreError>(instantiation-specific),ReceiveErrors/SyncSummaryone-line aggregators, and test mocks. After this PR, hand-written impls are a single explainable category: foreign-type orphan workarounds.Verification
cargo check --workspace --all-targetsclean; wasmcargo check -p xmtp_db --target wasm32-unknown-unknownclean (wasmPlatformStorageErrormigrated); fmt + clippy-Dwarningsclean🤖 Generated with Claude Code
Note
Migrate manual
RetryableErrorimplementations to#[derive(Retryable)]across tier-1 cratesReplaces hand-written
impl RetryableErrorblocks across ~18 files with#[derive(Retryable)]and per-variant#[retry(...)]annotations fromxmtp_common.#[retry(true)],#[retry(inherit)], or a conditional expression (e.g.#[retry(when = source.is_retryable())]) to preserve existing retryability semantics exactly.#[retry(default = true)]at the enum level, and enums where all variants were non-retryable are left unannotated.xmtp_api_d14n,xmtp_api_grpc,xmtp_db,xmtp_mls,xmtp_proto, andxnet.Macroscope summarized 2455683.