feat(xmtp_mls): same-commit registry lookahead — register-then-write in one commit#3866
feat(xmtp_mls): same-commit registry lookahead — register-then-write in one commit#3866tylerhawkes wants to merge 2 commits into
Conversation
|
Claude finished @tylerhawkes's task —— View job Code Review CompleteReview Date: 2026-07-20 18:50 UTC SummaryThe author's self-review (2026-07-17T08:36:00Z) already identifies two critical blocking issues with this PR. I've reviewed the code and can confirm these issues are valid and represent serious problems that should prevent this from merging into v1.11. Critical Issues (Already Identified by Author)1. Order Divergence Between Apply and Validate Paths The PR's central premise is that both receive stages "fold registry updates identically in queue order." However:
Since 2. Honest-Sender Fork Risk The loosened Additional Code Quality ObservationsBeyond the blocking issues: Line mod.rs:276-280: The comment states "The in-batch chaining above already computed the post-proposal snapshot; adopt it instead of re-applying the delta." This optimization is correct but tightly couples the overlay's correctness to the apply path's computation, making the code fragile to refactoring. Coverage Gap: The PR notes (in description) that end-to-end staged-commit testing through the full commit-validation loop is missing. Given the order-divergence issue, this gap is particularly concerning. Function visibility change (validated_commit.rs:1310): Making RecommendationStrongly agree with the author's recommendation to defer this PR. The two fork risks and the falsified invariant make this unsuitable for v1.11. Same-commit register-then-write can be added correctly later, gated behind a If this must ship in v1.11, the reconciliation would require:
|
ApprovabilityVerdict: Needs human review This PR introduces new runtime behavior in MLS commit validation - enabling same-commit register-then-write patterns that were previously rejected. While well-tested and authored by a code owner, this changes core validation logic affecting what commits are accepted across all receivers, warranting human review. No code changes detected at You can customize Macroscope's approvability policy. Learn more. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3866 +/- ##
==========================================
+ Coverage 85.61% 85.68% +0.06%
==========================================
Files 415 415
Lines 67401 67706 +305
==========================================
+ Hits 57704 58012 +308
+ Misses 9697 9694 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Re-review (D9): BLOCK — recommend deferring this loosening out of v1.11Adversarial re-review + an independent verifier found two critical issues, both rooted in the same-commit register-then-write loosening this PR introduces:
Recommendation: defer. libxmtp doesn't currently produce same-commit register-then-write (registration and component-writes each go through their own propose-then-commit cycle), so this is speculative future-proofing that arms two fork risks while falsifying its own invariant. Keeping the strict pre-PR behavior (write-to-unregistered-component rejected) is the safe choice for the permanent-floor v1.11 contract. Same-commit register-then-write can be added later, correctly, gated behind a If we do want it in v1.11, the reconciliation is non-trivial: validation must iterate the same source/order as apply (not the fork's sorted iterator), the standalone-proposal arrival path needs consistent overlay semantics, and the contradictory change-control docs at both validator entry points need fixing — see |
|
This currently goes against XIP-81 spec, so will be closing for now. |
Problem
Receivers dispatched and policy-checked every
AppDataUpdateproposal against the pre-commitCOMPONENT_REGISTRYsnapshot. A commit that registers a new component and writes to it in the same commit was therefore rejected by every receiver — the write's type dispatch found no registry entry (UnknownComponent) and its policy lookup hit deny-by-default (NoRegistryEntry). "Register in an earlier commit" was documented as a convention (registry_table.rs) but enforced by nothing on the send side: any future client that batched the two would produce commits every fielded receiver rejects. Since v1.11 is the first stable release carrying the app-data validators, this acceptance-widening is only shippable now.Change
New
RegistryOverlay(groups/app_data/mod.rs): tracks the registry snapshot as a commit's proposals are walked in queue order, folding eachCOMPONENT_REGISTRYmutation so subsequent proposals see it. Both receive stages fold through this one type, so their semantics cannot drift:accumulate_app_data_updates): the in-batch chaining already computes the post-proposal registry snapshot; the overlay adopts it (set_snapshot) instead of re-applying the delta. Folding here is optimistic — results are discarded unless commit validation later passes.validate_app_data_update_proposals_in_commit): the overlay folds a registry mutation only after that proposal passes its own hardcoded super-admin-only policy check, so overlaid entries carry exactly the trust of entries committed earlier. Trusting them same-commit is equivalent to trusting them one commit later — no new abuse surface.Queue order is fixed by the commit, so every honest receiver folds identically. Write-before-register in queue order still deterministically fails.
Removeof the whole registry component clears the overlay to deny-all (that operation is itself rejected at validation — see #3863). The change-control docs inregistry_table.rsand the stale corner-case comment inaccumulate_app_data_updatesnow state the queue-order rule instead of the old trap.validate_one_app_data_updatebecomespub(super)(mirroring its_with_old_valuesibling) for direct test coverage.Testing
test_accumulate_registry_lookahead_register_then_write: registry-insert + write batched in queue order applies; the write dispatches through the just-registered String type. Mutation-verified: with the overlay's snapshot adoption neutralized, the test fails; restored, it passes.test_accumulate_registry_lookahead_write_before_register_fails: reversed queue order still failsUnknownComponent— the overlay looks back, never ahead.test_validate_registry_lookahead_policy_sees_folded_entry: drivesvalidate_one_app_data_update+RegistryOverlay::folddirectly — the same pair the commit-validation loop walks: deny-by-default before the fold, allow-all entry admits the write after it.xmtp_mls+xmtp_mls_commonsuites against the local node: 1502/1502 (known flakeshould_reconnectretried). Changed files clean under clippy 1.97 (remaining flags on this machine are pre-existing trunk debt in identity.rs / mls_sync.rs / bidi_fuzz_tests.rs).Composes with #3863: entry-level validation there means only structurally valid, super-admin-authored registry mutations ever reach the overlay.
Part of the pre-v1.11 app-data hardening plan (P0.6) — the last of the Phase-0 protocol-acceptance changes. Siblings: #3863, #3864, #3865.
🤖 Generated with Claude Code
https://claude.ai/code/session_01PkZpajnDV829SVuW9danrW
Note
Add same-commit registry lookahead so register-then-write succeeds in one commit
RegistryOverlayin mod.rs to maintain an in-batch view of the component registry while processingAppDataUpdateproposals, updating the overlay as each registry mutation is applied in queue order.accumulate_app_data_updatesandvalidate_app_data_update_proposals_in_commitin validated_commit.rs to use the overlay so later proposals in the same commit see components registered earlier in the batch.UnknownComponent); removing the registry mid-batch yields a deny-all view for subsequent proposals.Macroscope summarized ec6ef73.