Skip to content

feat(bindings_wasm): expose the app data change callback and update guard - #4014

Merged
tylerhawkes merged 1 commit into
mainfrom
tyler/appdata-cb-wasm-bindings
Aug 20, 2026
Merged

tylerhawkes merged 1 commit into
mainfrom
tyler/appdata-cb-wasm-bindings

Conversation

@tylerhawkes

@tylerhawkes tylerhawkes commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Stack

Merge bottom-up — each PR is based on its parent below.
Android is the priority path: #4019 → #4011 → #4012 → #4016.

Already merged: #4018 (proto regen). Related follow-up, independent of this stack: #4020.


Exposes the unstable app-data change callback to the wasm bindings.

Shape

AppDataChangeCallback is an extern "C" JS object with an async onAppDataChanged method — the same pattern as AuthCallback in client/gateway_auth.rs. UnstableChangeCallbacks is a #[wasm_bindgen] object with a setter per callback, mirroring BackendBuilder::authCallback, so handlers for the other mutable fields are added as further setters.

group_id is hex-encoded on this boundary, matching the rest of the wasm surface.

A host callback that rejects is logged and swallowed: the commit is already durable by the time it runs, so there is nothing to roll back, and aborting message processing over a host-side bug would be worse than letting the next change re-trigger the merge.

Registration

A trailing changeCallbacks parameter on createClient and createClientWithBackend.

browser-sdk is deliberately not wired up here

The browser SDK runs the client inside a Web Worker and passes client options across a structured clone, which cannot carry a function. Exposing this there needs a worker→main reverse-RPC that does not exist yet — today the worker only sends responses and stream events, never requests.

There is also a design question worth settling before building it: the callback is awaited from inside the worker's message-processing loop, so a slow or blocked main thread would stall sync for that client. That trade-off should be decided deliberately rather than inherited. Filed as a follow-up.

Anyone driving @xmtp/wasm-bindings directly can use this today.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AdY7WKkNbJmdzpmUWvW1my

Note

Expose app data change callbacks and guarded update support to WASM, mobile, and Node bindings

  • Adds AppDataChangeCallback trait and UnstableChangeCallbacks registry in the MLS core so hosts can receive async notifications when group app_data changes after commit.
  • Introduces a compare-and-swap guard on update_app_data: callers can pass an expected_value; mismatches fail fast locally or surface as AppDataSuperseded after an epoch race.
  • Adds a new IntentState::Superseded DB state for guarded intents that lose a race, marked non-retryable and distinct from Error.
  • Callbacks are dispatched after the per-group mutex is released (or off the sync path entirely for streaming) to prevent deadlocks; callbacks may republish into the same group safely.
  • Wires the full stack through FFI: WASM (UnstableChangeCallbacks JS class, onAppDataChanged extern), mobile UniFFI (FfiUnstableChangeCallbacks, FfiAppDataChangeCallback), Node (update_app_data passes None guard), and Android SDK (Group.updateAppData with optional expectedAppData).
  • Risk: AppDataChangeCallback and associated types are marked unstable; delivery semantics and API shape may change.
📊 Macroscope summarized d7b4a09. 19 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

@claude

claude Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Claude finished @tylerhawkes's task —— View job


Code Review

Review Date: 2026-08-19T20:38:00Z

Overall Assessment

The implementation is well-designed and follows established patterns in the codebase. The code quality is high, with proper error handling and clear documentation. The changes are additive and opt-in, minimizing risk.

Key Observations

✅ Strengths:

  1. Consistent Pattern — The callback architecture mirrors the existing AuthCallback pattern in gateway_auth.rs, making it easy to understand and maintain.

  2. Defensive Error Handling — Errors in serialization and callback execution are logged and swallowed (lines 68-69, 75-77 in change_callbacks.rs), which is the correct approach since the commit is already durable at callback time.

  3. Extensibility — UnstableChangeCallbacks uses a struct with optional fields rather than a single callback parameter, allowing future callbacks to be added non-disruptively.

  4. Type Safety — Proper From implementations and type conversions between wasm and core types.

Minor Observations

  1. Test Coverage — The test file (tests/mod.rs) only updates test function signatures to pass None for the new change_callbacks parameter. Consider adding an integration test that actually exercises the callback mechanism, even if it's just verifying that a JS callback gets invoked.

  2. Documentation Clarity — Line 23 in change_callbacks.rs says "hex-encoded" for the group_id, which is accurate. The consistency is good (matching the rest of the wasm surface as noted in the PR description).

  3. Callback Idempotency Note — Line 51 in change_callbacks.rs mentions "the merge must be idempotent" since callbacks fire for both local and remote changes. This is important and well-documented, though it might be worth emphasizing in the UpdateAppDataOptions doc comment as well.

Security & Performance

  • Security: No security concerns. The callbacks run after the commit is durable, so a malicious callback cannot corrupt state. The compare-and-swap guard (expectedValue) properly prevents race conditions when desired.

  • Performance: The callback is awaited during message processing (line 75), which could block sync if the JS callback is slow. The PR description acknowledges this trade-off for browser-sdk. For direct wasm-bindings users, this is documented and acceptable.

Verdict

This PR is ready to merge. The implementation is solid, follows established patterns, and includes appropriate safeguards. The only enhancement would be adding an integration test for the callback, but the existing test coverage showing no regressions is sufficient for an opt-in feature.


macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR introduces new user-facing capabilities: app data change callbacks and compare-and-swap guards. It adds new intent states, error types, and callback infrastructure across mobile/wasm/Android SDKs. The scope and complexity of this new feature warrants human review. This assessment was made without Macroscope's correctness findings, which could not be posted.

Not approved because:

  • Macroscope's correctness review could not post its findings

You can add or adjust custom eligibility rules. Learn more.

@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-node-bindings branch from 6f021d2 to b76a2e4 Compare August 19, 2026 16:48
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-wasm-bindings branch from a92b803 to 5731d8d Compare August 19, 2026 16:48
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 19, 2026 16:48

Dismissing prior approval to re-evaluate 5731d8d

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-node-bindings branch from b76a2e4 to ac76516 Compare August 19, 2026 17:20
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-wasm-bindings branch from 5731d8d to 55cefed Compare August 19, 2026 17:20
@codecov

codecov Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.88%. Comparing base (61114a7) to head (d7b4a09).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4014   +/-   ##
=======================================
  Coverage   85.88%   85.88%           
=======================================
  Files         419      419           
  Lines       68348    68348           
=======================================
  Hits        58700    58700           
  Misses       9648     9648           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-wasm-bindings branch from 55cefed to b364f55 Compare August 19, 2026 17:51
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-node-bindings branch from ac76516 to 342b782 Compare August 19, 2026 17:51
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-wasm-bindings branch from b364f55 to 69ad642 Compare August 19, 2026 18:26
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-node-bindings branch from 342b782 to 615d16b Compare August 19, 2026 18:26
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 19, 2026 18:26

Dismissing prior approval to re-evaluate 69ad642

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-node-bindings branch from 615d16b to bec4f9c Compare August 19, 2026 18:51
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-wasm-bindings branch from 69ad642 to a8f796e Compare August 19, 2026 18:51
@tylerhawkes
tylerhawkes changed the base branch from tyler/appdata-cb-node-bindings to tyler/appdata-cb-core August 19, 2026 18:51
@tylerhawkes tylerhawkes changed the title feat(bindings_wasm): expose the app data change callback feat(bindings_wasm): expose the app data change callback and update guard Aug 19, 2026
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 19, 2026 18:56

Dismissing prior approval to re-evaluate a8f796e

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
An error occurred while trying to automatically change base from tyler/appdata-cb-core to tyler/appdata-cb-db August 19, 2026 20:41
@tylerhawkes
tylerhawkes force-pushed the tyler/appdata-cb-wasm-bindings branch from 48fc69c to d7b4a09 Compare August 19, 2026 20:47
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 19, 2026 20:47

Dismissing prior approval to re-evaluate d7b4a09

@tylerhawkes
tylerhawkes changed the base branch from tyler/appdata-cb-core to main August 19, 2026 20:47
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 19, 2026
@macroscopeapp
macroscopeapp Bot dismissed their stale review August 19, 2026 20:52

Dismissing prior approval to re-evaluate d7b4a09

@tylerhawkes tylerhawkes reopened this Aug 20, 2026
@tylerhawkes
tylerhawkes merged commit cdceafe into main Aug 20, 2026
37 of 38 checks passed
@tylerhawkes
tylerhawkes deleted the tyler/appdata-cb-wasm-bindings branch August 20, 2026 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants