Skip to content

Root WASM merge path#2001

Draft
cursor[bot] wants to merge 1 commit intofeat/sync-wasm-merge-callbackfrom
cursor/root-wasm-merge-path-aea4
Draft

Root WASM merge path#2001
cursor[bot] wants to merge 1 commit intofeat/sync-wasm-merge-callbackfrom
cursor/root-wasm-merge-path-aea4

Conversation

@cursor
Copy link
Contributor

@cursor cursor bot commented Feb 14, 2026

Fix: Root WASM merge path remains disconnected

Description

This PR fixes a high-severity logic bug where the WasmMergeCallback::merge_root_state path was not correctly wired into the runtime environment, preventing root state conflicts from being resolved via WASM callbacks.

The bug (ID: 11afc4cb-e272-4099-84c4-66de8d7637bd) meant that while the WasmMergeCallback trait and its runtime implementation existed for root merges, the RuntimeEnv and VMLogic only propagated custom entity merge callbacks. Consequently, the merge_root_state function in storage could not dispatch to __calimero_merge_root_state, leading to feature failure for WASM-based root merges.

This fix introduces a dedicated channel for the root merge callback in RuntimeEnv and VMLogic, updates build_runtime_env_with_merge to accept and propagate it, and modifies merge_root_state to utilize this callback when no host-side merge function is registered.

Test plan

The following tests were performed to verify the changes:

  1. cargo build to ensure successful compilation.
  2. cargo test -p runtime -p storage to run unit tests for the modified crates.
  3. cargo fmt --check and cargo clippy --workspace -- -D warnings to ensure code style and linting compliance.

All tests passed successfully. It is possible to add an end-to-end test case by simulating a root state conflict and verifying that the __calimero_merge_root_state WASM export is invoked.

Documentation update

No public or internal documentation updates are required as this fixes an internal plumbing issue for a feature that was not fully functional.


The WasmMergeCallback trait has both merge_custom and merge_root_state
methods, but only merge_custom was being wired through RuntimeEnv.
This left root state conflicts unable to dispatch to the WASM
__calimero_merge_root_state export.

Changes:
- Add wasm_root_merge_callback field to RuntimeEnv
- Add with_root_merge_callback and wasm_root_merge_callback methods
- Add get_wasm_root_merge_callback function to env module
- Add root_merge_callback field and methods to VMLogic
- Update build_runtime_env_with_merge to accept root_merge_callback
- Update merge_root_state to try WASM callback when registry fails
- Wire up root merge callback from RuntimeMergeCallback in Module::run

This ensures root state conflicts can now dispatch to the WASM merge
function when no in-process merge function is registered.
@cursor
Copy link
Contributor Author

cursor bot commented Feb 14, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@github-actions
Copy link

Your PR title does not adhere to the Conventional Commits convention:

<type>(<scope>): <subject>

Common errors to avoid:

  1. The title must be in lower case.
  2. Allowed type values are: build, ci, docs, feat, fix, perf, refactor, test.

Copy link

@meroreviewer meroreviewer bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Code Reviewer

Reviewed by 3 agents | Quality score: 100% | Review time: 173.0s

🔴 1 critical, 🟡 1 warnings, 📝 1 nitpicks. See inline comments.


🤖 Generated by AI Code Reviewer | Review ID: review-70a4f550

match try_merge_registered(existing, incoming, existing_ts, incoming_ts) {
MergeRegistryResult::Success(merged) => Ok(merged),
MergeRegistryResult::NoFunctionsRegistered => {
// No in-process merge function registered.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Duplicate WASM callback check creates dead code and incorrect error handling

The first WASM callback check at line 109 returns immediately on both success AND failure, making the second check at line 136 unreachable dead code; if the callback returns an error, it propagates up instead of falling back to LWW as the second block's comment suggests.

Suggested fix:

Remove the first early-return WASM check and keep only the second one that properly handles errors with LWW fallback, or change the first check to only return on Ok and fall through on Err.

@@ -122,6 +133,25 @@ pub fn merge_root_state(
// - The data type doesn't match any registered type (test contamination)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Duplicate WASM callback check is dead code

The second get_wasm_root_merge_callback() call (lines 136-156) is unreachable: if the first check (lines 109-119) finds a callback it returns, otherwise there's no callback and this second check will also find None.

Suggested fix:

Remove the second WASM callback block entirely; the first check already handles this case.


/// Optional WASM merge callback for root state merging during sync.
/// This is called by the storage layer when merging root state and no
/// in-process merge function is registered.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Nit: Verbose type signature could use a type alias

The Rc<dyn Fn(&[u8], &[u8]) -> Result<Vec<u8>, MergeError>> type is repeated across multiple files; a type alias would improve readability (DRY).

Suggested fix:

Consider adding `pub type RootMergeCallback = Rc<dyn Fn(&[u8], &[u8]) -> Result<Vec<u8>, MergeError>>;` in a shared location.

@github-actions
Copy link

This pull request has been automatically marked as stale. If this pull request is still relevant, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated.

@github-actions github-actions bot added the Stale label Feb 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant