Skip to content

fix(sync): split global and automatic controls - #28

Merged
mkdir700 merged 3 commits into
mainfrom
switch-sync
Aug 12, 2026
Merged

fix(sync): split global and automatic controls#28
mkdir700 merged 3 commits into
mainfrom
switch-sync

Conversation

@mkdir700

@mkdir700 mkdir700 commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

  • Split the global sync permission from automatic sync behavior.
  • Block all sync paths when global sync is disabled while preserving manual resend when only automatic sync is disabled.
  • Update the Engine contract, mobile probes, version metadata, and architecture documentation.

Verification

  • cargo test -p uc-core --lib --locked
  • cargo test -p uc-application --lib --locked
  • cargo test -p uc-mobile-probe-core --lib --locked
  • mobile binding public-contract checks
  • two-device file resend check
  • cargo check --workspace --all-targets --locked
  • cargo fmt --all -- --check
  • node scripts/architecture/check-engine-repository.mjs
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Added separate controls for global synchronization and automatic synchronization.
    • Manual resend remains available when automatic synchronization is disabled.
    • Added clear feedback when synchronization is disabled during resend.
  • Bug Fixes

    • Clipboard delivery, recovery, outbound transfers, and restored-state broadcasts are now blocked when global synchronization is disabled.
    • Offline recovery no longer retries automatically when Global Sync is turned off.
  • Documentation

    • Updated synchronization rules and release documentation to reflect the new controls.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@mkdir700, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6fe8123d-0d6f-48f1-af04-06fc1caacf6d

📥 Commits

Reviewing files that changed from the base of the PR and between 5fc08ac and 7372935.

📒 Files selected for processing (2)
  • crates/uc-application/src/clipboard/sync/outbound_plan.rs
  • docs/architecture/architecture-bible.md
📝 Walkthrough

Walkthrough

The PR separates global synchronization from automatic synchronization. It gates inbound delivery, outbound planning, recovery, and restore broadcasts. Manual resend remains independent of automatic synchronization and reports a new disabled outcome when global synchronization is off.

Changes

Synchronization gating

Layer / File(s) Summary
Separate synchronization settings
crates/uc-core/src/settings/*, crates/uc-application/src/settings/models.rs, crates/uc-engine/src/contract/settings.rs, crates/uc-engine/src/operations/settings/settings.rs, tests/hosts/uc-mobile-probe-core/src/lib.rs
auto_sync is replaced by independent sync_enabled and auto_sync_enabled fields across settings models, defaults, patches, mappings, and responses.
Runtime synchronization gates
crates/uc-application/src/clipboard/inbound/runtime.rs, crates/uc-application/src/clipboard/sync/*, crates/uc-engine/src/assembly/clipboard_runtime.rs
Global synchronization now gates inbound processing, outbound intents, automatic delivery, recovery, and restore broadcasts. Tests cover disabled global synchronization and independent automatic synchronization.
Disabled resend outcome propagation
crates/uc-application/src/clipboard/sync/resend_entry.rs, crates/uc-engine/src/{contract,operations/history}/*, bindings/uc-engine-uniffi/src/runtime.rs, tests/hosts/uc-mobile-probe-core/src/lib.rs, crates/uc-engine/tests/public_contract.rs
Manual resend returns and serializes SynchronizationDisabled when global synchronization is disabled.
Integration coverage and release updates
crates/uc-engine/src/testing/host_adapter_contract.rs, CONTEXT.md, docs/architecture/architecture-bible.md, Cargo.toml, tests/hosts/ohos/engine/oh-package.json5
Host adapter coverage verifies file resend behavior and disabled synchronization. Documentation and package versions are updated for rc.8.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HostAdapter
  participant SyncRuntime
  participant SettingsPort
  participant OutboundPlan
  participant RemoteDevice
  HostAdapter->>SyncRuntime: request manual resend
  SyncRuntime->>SettingsPort: load synchronization settings
  SettingsPort-->>SyncRuntime: sync_enabled and auto_sync_enabled
  SyncRuntime->>OutboundPlan: create resend plan
  OutboundPlan->>RemoteDevice: deliver file when sync_enabled is true
  RemoteDevice-->>HostAdapter: receive resent file
  SyncRuntime-->>HostAdapter: SynchronizationDisabled when sync_enabled is false
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: separating global synchronization control from automatic synchronization control.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch switch-sync
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch switch-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/uc-application/src/clipboard/sync/outbound_plan.rs`:
- Around line 92-97: Update the settings-load error path in the outbound sync
plan construction to return an empty OutboundSyncPlan with no clipboard intent
or files. Ensure this failure handling occurs before the sync_enabled gate so
unavailable settings cannot permit clipboard transmission.

In `@crates/uc-core/src/settings/model.rs`:
- Around line 174-175: Update the auto_sync_enabled field in
crates/uc-core/src/settings/model.rs:174-175 to accept the legacy auto_sync key
during deserialization via an alias, while retaining the current field name and
default behavior. Add a regression test in
crates/uc-core/src/settings/defaults.rs:625-644 covering deserialization of
{"auto_sync": false} and verifying auto_sync_enabled remains false.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e4e56ff-f086-45e6-a8bc-d92bd2bff408

📥 Commits

Reviewing files that changed from the base of the PR and between 0488071 and 5fc08ac.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • CONTEXT.md
  • Cargo.toml
  • bindings/uc-engine-uniffi/src/runtime.rs
  • crates/uc-application/src/clipboard/inbound/runtime.rs
  • crates/uc-application/src/clipboard/sync/active_state/restore_broadcast_worker.rs
  • crates/uc-application/src/clipboard/sync/outbound_plan.rs
  • crates/uc-application/src/clipboard/sync/resend_entry.rs
  • crates/uc-application/src/clipboard/sync/sync_runtime.rs
  • crates/uc-application/src/settings/models.rs
  • crates/uc-core/src/settings/defaults.rs
  • crates/uc-core/src/settings/model.rs
  • crates/uc-engine/src/assembly/clipboard_runtime.rs
  • crates/uc-engine/src/contract/operation.rs
  • crates/uc-engine/src/contract/settings.rs
  • crates/uc-engine/src/operations/history/resend.rs
  • crates/uc-engine/src/operations/settings/settings.rs
  • crates/uc-engine/src/testing/host_adapter_contract.rs
  • crates/uc-engine/tests/public_contract.rs
  • docs/architecture/architecture-bible.md
  • tests/hosts/ohos/engine/oh-package.json5
  • tests/hosts/uc-mobile-probe-core/src/lib.rs

Comment on lines +92 to +97
if !settings.sync.sync_enabled {
return OutboundSyncPlan {
clipboard: None,
files: vec![],
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Fail closed when settings loading fails.

The early error path at Lines 72-89 returns an enabled clipboard intent before this gate runs. A user with global synchronization disabled can transmit clipboard content when settings storage is unavailable. Return an empty plan on settings-load failure.

Proposed fix
-                // Safe default: allow clipboard sync, skip file sync.
                 return OutboundSyncPlan {
-                    clipboard: Some(ClipboardSyncIntent {
-                        snapshot,
-                        file_transfers: vec![],
-                    }),
+                    clipboard: None,
                     files: vec![],
                 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/uc-application/src/clipboard/sync/outbound_plan.rs` around lines 92 -
97, Update the settings-load error path in the outbound sync plan construction
to return an empty OutboundSyncPlan with no clipboard intent or files. Ensure
this failure handling occurs before the sync_enabled gate so unavailable
settings cannot permit clipboard transmission.

Comment on lines +174 to +175
pub sync_enabled: bool,
pub auto_sync_enabled: bool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 3 'auto_sync|auto_sync_enabled|sync_enabled' \
  crates/uc-core/src/settings \
  crates/uc-application/src/settings \
  crates/uc-engine/src \
  bindings \
  tests

Repository: UniClipboard/Engine

Length of output: 26512


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- SyncSettings definition and defaults ---'
sed -n '150,195p' crates/uc-core/src/settings/model.rs
sed -n '100,145p' crates/uc-core/src/settings/defaults.rs
printf '%s\n' '--- Settings loading and migration references ---'
rg -n -C 4 'Settings|serde_json::from|serde_json::to|schema_version|migration|auto_sync' \
  crates/uc-core/src/settings crates/uc-application/src/settings crates/uc-infra crates/uc-engine \
  -g '*.rs' | head -n 500
printf '%s\n' '--- Relevant repository diff ---'
git diff --stat
git diff -- crates/uc-core/src/settings/model.rs crates/uc-core/src/settings/defaults.rs

Repository: UniClipboard/Engine

Length of output: 41122


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- All persisted settings read/write call sites ---'
rg -n -C 5 'settings.*(json|read|write|load|save)|from_str|from_slice|to_string|to_vec|to_writer|write_all|read_to_string' \
  crates -g '*.rs' | head -n 700
printf '%s\n' '--- SyncSettings serde attributes and explicit migration code ---'
rg -n -C 8 '#\[serde|impl.*Deserialize|deserialize_with|rename|alias|auto_sync' \
  crates/uc-core/src/settings crates/uc-infra crates/uc-application crates/uc-engine -g '*.rs' | head -n 700

Repository: UniClipboard/Engine

Length of output: 50376


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- Settings infrastructure files ---'
fd -t f -i 'settings|migration' crates/uc-infra crates/uc-core | sort
printf '%s\n' '--- SettingsPort implementations and JSON deserialization ---'
rg -n -C 6 'impl .*SettingsPort|SettingsPort for|serde_json::from_(str|slice|reader)|serde_json::to_(string|vec|writer)|settings\.json|CURRENT_SCHEMA_VERSION|MigrationV[0-9]' \
  crates/uc-infra crates/uc-core -g '*.rs'
printf '%s\n' '--- Migration tests and fixtures containing sync fields ---'
rg -n -C 8 'auto_sync|sync_enabled|sync_frequency|SyncSettings' \
  crates/uc-infra crates/uc-core -g '*.rs' -g '*.json' | head -n 500

Repository: UniClipboard/Engine

Length of output: 50378


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

model = Path("crates/uc-core/src/settings/model.rs").read_text()
defaults = Path("crates/uc-core/src/settings/defaults.rs").read_text()

struct = re.search(r'pub struct SyncSettings\s*\{(?P<body>.*?)\n\}', model, re.S)
assert struct, "SyncSettings definition not found"
body = struct.group("body")

assert "pub auto_sync_enabled: bool" in body
assert "pub auto_sync: bool" not in body
field_start = body.index("pub auto_sync_enabled: bool")
preceding = body[max(0, field_start - 160):field_start]
assert "alias = \"auto_sync\"" not in preceding

default_impl = re.search(
    r'impl Default for SyncSettings\s*\{.*?Self\s*\{(?P<body>.*?)\n\s*\}\s*\}',
    defaults,
    re.S,
)
assert default_impl, "SyncSettings default implementation not found"
default_body = default_impl.group("body")
assert re.search(r'\bauto_sync_enabled:\s*true\b', default_body)

print("SyncSettings has no auto_sync field or serde alias.")
print("SyncSettings::default() sets auto_sync_enabled to true.")
print("Therefore derived serde deserialization treats legacy auto_sync as unknown and uses true.")
PY

Repository: UniClipboard/Engine

Length of output: 355


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- uc-infra settings migration ---'
wc -l crates/uc-infra/src/settings/migration.rs
cat -n crates/uc-infra/src/settings/migration.rs
printf '%s\n' '--- SettingsPort implementations ---'
rg -n -C 12 'SettingsPort' crates -g '*.rs' | grep -E 'SettingsPort|impl|async fn load|async fn save|from_str|from_slice|to_string|to_vec|settings_path' | head -n 300
printf '%s\n' '--- Actual settings file reads and writes ---'
rg -n -C 10 'settings_path|settings\.json|read_to_string|write_all|to_string_pretty|to_vec_pretty' \
  crates/uc-infra crates/uc-engine crates/uc-application -g '*.rs' | head -n 500

Repository: UniClipboard/Engine

Length of output: 50377


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

cat -n crates/uc-infra/src/settings/repository.rs | sed -n '1,285p'
printf '%s\n' '--- construction and migration wiring ---'
rg -n -C 12 'FileSettingsRepository::new|SettingsMigrator|migrate_to_latest|settings_repo' \
  crates/uc-engine/src crates/uc-infra/src -g '*.rs' | head -n 500

Repository: UniClipboard/Engine

Length of output: 50377


Preserve the legacy auto_sync value during deserialization.

FileSettingsRepository deserializes settings directly, and no migration handles auto_sync. An existing { "auto_sync": false } configuration therefore uses the default auto_sync_enabled: true and re-enables automatic synchronization.

Add a deserialization alias for auto_sync and a regression test for this case.

📍 Affects 2 files
  • crates/uc-core/src/settings/model.rs#L174-L175 (this comment)
  • crates/uc-core/src/settings/defaults.rs#L625-L644
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/uc-core/src/settings/model.rs` around lines 174 - 175, Update the
auto_sync_enabled field in crates/uc-core/src/settings/model.rs:174-175 to
accept the legacy auto_sync key during deserialization via an alias, while
retaining the current field name and default behavior. Add a regression test in
crates/uc-core/src/settings/defaults.rs:625-644 covering deserialization of
{"auto_sync": false} and verifying auto_sync_enabled remains false.

@mkdir700
mkdir700 merged commit 236ca1f into main Aug 12, 2026
2 checks passed
@mkdir700
mkdir700 deleted the switch-sync branch August 12, 2026 12:40
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.

1 participant