fix(xmtp_db,xmtp_mls): tolerate unknown intent kinds + app-data hardening docs#3871
Conversation
|
Claude finished @tylerhawkes's task —— View job Code Review CompleteReview Date: 2026-07-21 19:56 UTC Tasks
Review SummaryThis PR effectively addresses the downgrade tolerance problem for Positive Observations
Code Quality Observationscrates/xmtp_db/src/encrypted_store/group_intent.rs:82-101 The
The alternative (making strum a direct dependency) would be more robust but adds maintenance burden. The current trade-off is reasonable. crates/xmtp_mls/src/groups/mls_sync.rs:584, 2830, 3954 Good: All three production query sites now use Minor performance note: Each call site materializes a fresh vector. For hot paths, consider caching this as a Test Coverage Assessmentcrates/xmtp_db/src/encrypted_store/group_intent.rs:790-860 Excellent test coverage:
The tests correctly use raw SQL insertion to simulate future variants, which is the only way to exercise this path. Documentation Qualitydocs/app-data-validator-remediation.md The runbook is production-ready:
One minor suggestion: Consider adding a worked example with version numbers and commit sequence to make the remediation steps more concrete for operators unfamiliar with the codebase. Uncovered Edge CasesResidual known issue (acknowledged in PR description):
No Blocking IssuesAll previously raised concerns have been addressed. The implementation correctly balances robustness with pragmatism, and the remaining trade-offs are well-documented and intentional. |
ApprovabilityVerdict: Approved Defensive hardening change that filters intent queries to avoid deserialization failures during app downgrades. Uses strum::EnumIter for exhaustive variant iteration, includes comprehensive tests, and the author owns all modified code files. The unresolved review comment references stale code (the implementation now uses automatic enum iteration, not manual arrays). 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 @@
## main #3871 +/- ##
==========================================
+ Coverage 85.54% 85.60% +0.06%
==========================================
Files 426 426
Lines 67724 67773 +49
==========================================
+ Hits 57932 58020 +88
+ Misses 9792 9753 -39 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
8dfbce6 to
2902016
Compare
Dismissing prior approval to re-evaluate 2902016
2902016 to
878ee82
Compare
| .into_iter() | ||
| .map(|k| match k { | ||
| // No wildcard arm: a new variant fails to compile here until | ||
| // it is added to the array above. |
There was a problem hiding this comment.
interesting, good compile time insurance
|
good catch |
…ades don't wedge outbound
878ee82 to
e4f44ea
Compare
Problem
IntentKind'sFromSqlerrors on unknown discriminants, andfind_group_intentsuses all-or-nothingload()— so a single intent row written by a newer build (e.g. a future kind 13) poisons every intent query for that group after an app downgrade:publish_intents, committed-intent processing, andsync_until_last_intent_resolvedall fail, permanently wedging outbound for the group until re-upgrade. Intents are retained (not deleted) after processing, so this fires even for long-completed intents. Because v1.11's own unknown-kind behavior gets locked into the field, this tolerance can only usefully ship before the kinds it protects against exist.Changes
IntentKind::all()— the deserializable-kind allowlist, passed as the SQLallowed_kindsfilter at the three production query sites inmls_sync. Unknown-kind rows are excluded in SQL (never deserialized), stay untouched in the table, and resume processing on re-upgrade. NoUnknownsentinel variant — that would force handling in every match and risk being written back.find_group_intent_by_payload_hashstill errors if a downgraded client meets its own newer-build commit; it holds the cursor and self-resolves on re-upgrade.WELL_KNOWNchange-control block inregistry_table.rs; a newdocs/app-data-validator-remediation.mdrunbook (per-group floor bumps as the no-migration remediation lever for fielded validator bugs, with the sharp edges); two stale comments removed that claimed the bootstrap receive-side validator "isn't wired" (it is —validated_commit.rsroutes bootstraps throughbootstrap_validator).Testing
intent_kind_all_is_exhaustive— pins the allowlist to every discriminant1..=max, so adding an enum variant without updatingall()fails the build's tests.unknown_kind_row_is_excluded_by_kind_filter— inserts a raw future-discriminant row: the kind-filtered query returns the known intent and excludes the row; the unfiltered query is pinned as still erroring (so a future behavior change is a conscious one).xmtp_db+xmtp_mlssuites against the local node: 911/911 (known flakeshould_reconnectretried).Part of the pre-v1.11 app-data hardening plan (P2.1 + P2.2) — the final planned code change of the set. Siblings: #3863–#3870.
🤖 Generated with Claude Code
https://claude.ai/code/session_01PkZpajnDV829SVuW9danrW
Note
Filter unknown
IntentKinddiscriminants to tolerate database downgrade scenariosIntentKind::all()(viastrum::EnumIter) to enumerate all known intent kinds at runtime.sync_until_last_intent_resolved,publish_intents, andpost_commitin mls_sync.rs to passSome(IntentKind::all().collect())as a kind filter, silently skipping rows with unrecognized discriminants inserted by a newer build.Macroscope summarized e4f44ea.