@@ -176,7 +176,7 @@ const VIRTUAL_PROJECTION_SOURCE_READY_TIMEOUT: Duration = Duration::from_secs(30
176176const VIRTUAL_PROJECTION_SOURCE_READY_POLL: Duration = Duration::from_millis(250);
177177const VIRTUAL_PROJECTION_SOURCE_READY_LOG_EVERY: Duration = Duration::from_secs(2);
178178#[cfg(target_os = "macos")]
179- const MACOS_FILE_PROVIDER_MOUNT_ROOT_APPEAR_TIMEOUT: Duration = Duration::from_secs(5 );
179+ const MACOS_FILE_PROVIDER_MOUNT_ROOT_APPEAR_TIMEOUT: Duration = Duration::from_secs(30 );
180180#[cfg(target_os = "macos")]
181181const MACOS_FILE_PROVIDER_MOUNT_ROOT_RECOVERY_TIMEOUT: Duration = Duration::from_secs(30);
182182const LIVE_MODE_RUNNER_ACTIVE_INTERVAL: Duration = Duration::from_millis(500);
@@ -8808,7 +8808,7 @@ fn activate_virtual_projection_mount(
88088808 wait_for_mount_entities(state_root, &mount.mount_id)?;
88098809 }
88108810 ensure_virtual_projection_runtime(state_root, mount)?;
8811- signal_virtual_projection_refresh (mount);
8811+ refresh_virtual_projection_mount_activation (mount);
88128812 recover_virtual_projection_mount_root_if_needed(state_root, mount)?;
88138813 desktop_log(
88148814 "info",
@@ -8877,25 +8877,15 @@ fn recover_macos_file_provider_mount_root_if_needed(
88778877
88788878 desktop_log(
88798879 "warn",
8880- "file_provider.recover_started ",
8880+ "file_provider.mount_repair_started ",
88818881 format!(
8882- "recovering macOS File Provider domain for mount `{}` after activation: {reason}",
8882+ "refreshing macOS File Provider mount `{}` after activation without resetting the shared domain : {reason}",
88838883 mount.mount_id.0
88848884 ),
88858885 );
8886- reset_macos_file_provider_domain(&reason)?;
8887- register_macos_virtual_projection(&mount.mount_id.0, &mount.root.display().to_string())?;
8888- macos_file_provider_domain_url(localityd::file_provider::MACOS_FILE_PROVIDER_DOMAIN_ID)
8889- .map_err(|error| {
8890- format!(
8891- "Could not open macOS File Provider domain after recovery for `{}`: {}",
8892- mount.mount_id.0,
8893- error.message()
8894- )
8895- })?;
88968886 prefetch_virtual_projection_root(state_root, mount)?;
88978887 ensure_virtual_projection_runtime(state_root, mount)?;
8898- signal_virtual_projection_refresh (mount);
8888+ refresh_virtual_projection_mount_activation (mount);
88998889
89008890 wait_for_macos_file_provider_mount_root_recovery(
89018891 &root,
@@ -8905,9 +8895,9 @@ fn recover_macos_file_provider_mount_root_if_needed(
89058895
89068896 desktop_log(
89078897 "info",
8908- "file_provider.recover_finished ",
8898+ "file_provider.mount_repair_finished ",
89098899 format!(
8910- "recovered macOS File Provider domain for mount `{}`",
8900+ "refreshed macOS File Provider mount `{}` without resetting the shared domain ",
89118901 mount.mount_id.0
89128902 ),
89138903 );
@@ -8937,7 +8927,7 @@ fn wait_for_macos_file_provider_mount_root_recovery(
89378927
89388928 if Instant::now() >= deadline {
89398929 return Err(format!(
8940- "macOS File Provider recovery did not produce a healthy mount root `{}`: {reason}",
8930+ "macOS File Provider did not finish preparing mount root `{}`: {reason}",
89418931 root.display()
89428932 ));
89438933 }
@@ -9000,22 +8990,10 @@ fn evaluate_macos_file_provider_mount_root(root: &Path) -> Result<String, String
90008990 Ok(details)
90018991}
90028992
9003- #[cfg(target_os = "macos")]
9004- fn reset_macos_file_provider_domain(reason: &str) -> Result<(), String> {
9005- run_macos_file_provider_helper("reset", Vec::new())
9006- .map(|_| ())
9007- .map_err(|error| {
9008- format!(
9009- "Could not reset macOS File Provider domain while recovering from `{reason}`: {}",
9010- error.message()
9011- )
9012- })
9013- }
9014-
90158993fn macos_file_provider_mount_root_health_error(root: &Path, details: &str) -> Option<String> {
90168994 if details.contains("uploadingError") || details.contains("NSCocoaErrorDomain Code=3328") {
90178995 return Some(format!(
9018- "The macOS File Provider mount root `{}` is in a local-upload error state. Run clean-start or reset the Locality File Provider domain, then reconnect Notion .",
8996+ "The macOS File Provider mount root `{}` is in a local-upload error state. Locality left the shared File Provider domain and other source folders unchanged; review File Provider diagnostics before repairing this mount .",
90198997 root.display()
90208998 ));
90218999 }
@@ -9802,42 +9780,82 @@ fn running_daemon_build(state_root: &Path) -> Option<DaemonBuildInfo> {
98029780 .map(|report| report.build)
98039781}
98049782
9805- fn signal_virtual_projection_refresh(mount: &MountConfig) {
9806- for identifier in virtual_projection_refresh_signal_identifiers(mount) {
9807- if let Err(error) = signal_virtual_projection_container(mount, &identifier) {
9808- desktop_log(
9809- "warn",
9810- "file_provider.signal_failed",
9811- format!(
9812- "could not signal {}:{} refresh: {error}",
9813- mount.mount_id.0, identifier
9814- ),
9815- );
9783+ #[derive(Clone, Debug, PartialEq, Eq)]
9784+ enum VirtualProjectionRefreshAction {
9785+ Signal(String),
9786+ ReimportThenSignal(String),
9787+ }
9788+
9789+ impl VirtualProjectionRefreshAction {
9790+ fn identifier(&self) -> &str {
9791+ match self {
9792+ Self::Signal(identifier) | Self::ReimportThenSignal(identifier) => identifier,
98169793 }
98179794 }
98189795}
98199796
9820- fn virtual_projection_refresh_signal_identifiers(mount: &MountConfig) -> Vec<String> {
9797+ fn signal_virtual_projection_refresh(mount: &MountConfig) {
9798+ for action in virtual_projection_refresh_actions(mount) {
9799+ run_virtual_projection_refresh_action(mount, &action);
9800+ }
9801+ }
9802+
9803+ fn refresh_virtual_projection_mount_activation(mount: &MountConfig) {
9804+ for action in virtual_projection_mount_activation_refresh_actions(mount) {
9805+ run_virtual_projection_refresh_action(mount, &action);
9806+ }
9807+ }
9808+
9809+ fn run_virtual_projection_refresh_action(
9810+ mount: &MountConfig,
9811+ action: &VirtualProjectionRefreshAction,
9812+ ) {
9813+ if let Err(error) = refresh_virtual_projection_container(mount, &action) {
9814+ desktop_log(
9815+ "warn",
9816+ "file_provider.signal_failed",
9817+ format!(
9818+ "could not signal {}:{} refresh: {error}",
9819+ mount.mount_id.0,
9820+ action.identifier()
9821+ ),
9822+ );
9823+ }
9824+ }
9825+
9826+ fn virtual_projection_refresh_actions(mount: &MountConfig) -> Vec<VirtualProjectionRefreshAction> {
98219827 if mount.projection == ProjectionMode::MacosFileProvider {
98229828 return vec![
9823- ROOT_CONTAINER_IDENTIFIER.to_string(),
9824- mount_point_identifier(mount),
9825- "working-set".to_string(),
9829+ VirtualProjectionRefreshAction::Signal( ROOT_CONTAINER_IDENTIFIER.to_string() ),
9830+ VirtualProjectionRefreshAction::ReimportThenSignal( mount_point_identifier(mount) ),
9831+ VirtualProjectionRefreshAction::Signal( "working-set".to_string() ),
98269832 ];
98279833 }
98289834 vec![
9829- ROOT_CONTAINER_IDENTIFIER.to_string(),
9830- mount_point_identifier(mount),
9835+ VirtualProjectionRefreshAction::Signal( ROOT_CONTAINER_IDENTIFIER.to_string() ),
9836+ VirtualProjectionRefreshAction::Signal( mount_point_identifier(mount) ),
98319837 ]
98329838}
98339839
9834- fn signal_virtual_projection_container (
9840+ fn virtual_projection_mount_activation_refresh_actions (
98359841 mount: &MountConfig,
9836- container_identifier: &str,
9842+ ) -> Vec<VirtualProjectionRefreshAction> {
9843+ if mount.projection == ProjectionMode::MacosFileProvider {
9844+ return vec![
9845+ VirtualProjectionRefreshAction::Signal(ROOT_CONTAINER_IDENTIFIER.to_string()),
9846+ VirtualProjectionRefreshAction::ReimportThenSignal(mount_point_identifier(mount)),
9847+ ];
9848+ }
9849+ virtual_projection_refresh_actions(mount)
9850+ }
9851+
9852+ fn refresh_virtual_projection_container(
9853+ mount: &MountConfig,
9854+ action: &VirtualProjectionRefreshAction,
98379855) -> Result<(), String> {
98389856 match mount.projection {
98399857 ProjectionMode::MacosFileProvider => {
9840- refresh_macos_virtual_projection(&mount.mount_id.0, container_identifier )
9858+ refresh_macos_virtual_projection(&mount.mount_id.0, action )
98419859 }
98429860 ProjectionMode::LinuxFuse
98439861 | ProjectionMode::PlainFiles
@@ -9846,13 +9864,19 @@ fn signal_virtual_projection_container(
98469864}
98479865
98489866#[cfg(target_os = "macos")]
9849- fn refresh_macos_virtual_projection(mount_id: &str, identifier: &str) -> Result<(), String> {
9850- if identifier == "working-set" {
9851- return signal_macos_virtual_projection(mount_id, identifier);
9867+ fn refresh_macos_virtual_projection(
9868+ mount_id: &str,
9869+ action: &VirtualProjectionRefreshAction,
9870+ ) -> Result<(), String> {
9871+ match action {
9872+ VirtualProjectionRefreshAction::Signal(identifier) => {
9873+ signal_macos_virtual_projection(mount_id, identifier)
9874+ }
9875+ VirtualProjectionRefreshAction::ReimportThenSignal(identifier) => {
9876+ reimport_macos_virtual_projection(mount_id, identifier)
9877+ .or_else(|_| signal_macos_virtual_projection(mount_id, identifier))
9878+ }
98529879 }
9853-
9854- reimport_macos_virtual_projection(mount_id, identifier)
9855- .or_else(|_| signal_macos_virtual_projection(mount_id, identifier))
98569880}
98579881
98589882#[cfg(target_os = "macos")]
@@ -9903,7 +9927,10 @@ fn run_macos_file_provider_refresh_action(
99039927}
99049928
99059929#[cfg(not(target_os = "macos"))]
9906- fn refresh_macos_virtual_projection(_mount_id: &str, _identifier: &str) -> Result<(), String> {
9930+ fn refresh_macos_virtual_projection(
9931+ _mount_id: &str,
9932+ _action: &VirtualProjectionRefreshAction,
9933+ ) -> Result<(), String> {
99079934 Ok(())
99089935}
99099936
@@ -10512,7 +10539,7 @@ fn recoverable_macos_file_provider_activation_error(message: &str) -> bool {
1051210539 || message.contains("registered but not enabled")
1051310540 || message.contains("did not return a CloudStorage URL")
1051410541 || message.contains("macOS has not created")
10515- || (message.contains("did not produce a healthy mount root")
10542+ || (message.contains("did not finish preparing mount root")
1051610543 && macos_file_provider_mount_root_is_missing(message))
1051710544}
1051810545
@@ -12001,12 +12028,13 @@ mod tests {
1200112028 ActionReport, DESKTOP_ACTIVITY_LIMIT, DESKTOP_INSTALL_MARKER_VERSION,
1200212029 LIVE_MODE_REMOTE_FAST_FORWARD_LEASE, LIVE_MODE_RUNNER_ACTIVE_INTERVAL,
1200312030 LIVE_MODE_RUNNER_PERIODIC_RECHECK, MonitorScreenBounds, PendingChange, ScreenBounds,
12004- TerminalCliLinkState, TrayVisualState, acknowledge_install_state_at, activity_timestamp,
12005- clear_mount_cached_projection, clear_state_root_contents, clear_visible_projection_paths,
12006- conflict_preview, connection_metadata_changed, current_daemon_build_id,
12007- current_desktop_build_id, diff_report_message, exact_located_entity_record,
12008- exact_notion_entry_matches, failed_push_summary, has_unresolved_conflict_markers,
12009- hydration_after_editor_write, inspect_install_state, install_terminal_cli_link_at,
12031+ TerminalCliLinkState, TrayVisualState, VirtualProjectionRefreshAction,
12032+ acknowledge_install_state_at, activity_timestamp, clear_mount_cached_projection,
12033+ clear_state_root_contents, clear_visible_projection_paths, conflict_preview,
12034+ connection_metadata_changed, current_daemon_build_id, current_desktop_build_id,
12035+ diff_report_message, exact_located_entity_record, exact_notion_entry_matches,
12036+ failed_push_summary, has_unresolved_conflict_markers, hydration_after_editor_write,
12037+ inspect_install_state, install_terminal_cli_link_at,
1201012038 install_terminal_cli_link_in_path_dirs, is_notion_access_lost_message,
1201112039 is_unsupported_schema_version_message, live_mode_claim_remote_fast_forward_key,
1201212040 live_mode_enabled_mount, live_mode_local_reconcile_targets_for_mount_at,
@@ -12032,8 +12060,8 @@ mod tests {
1203212060 summarize_virtual_projection_children, terminal_cli_link_state, tray_icon_image,
1203312061 tray_icon_should_use_template, tray_popover_anchor, tray_popover_position,
1203412062 unsupported_notion_locator_url_message, validate_mount_root,
12035- validate_source_action_confirmation, virtual_projection_prefetch_container_identifiers ,
12036- virtual_projection_refresh_signal_identifiers ,
12063+ validate_source_action_confirmation, virtual_projection_mount_activation_refresh_actions ,
12064+ virtual_projection_prefetch_container_identifiers, virtual_projection_refresh_actions ,
1203712065 virtual_projection_source_ready_timeout_message,
1203812066 virtual_projection_waits_for_mount_point_children_before_registration,
1203912067 wait_for_live_mode_state_change, wake_live_mode_runner, write_terminal_cli_path_section,
@@ -14445,9 +14473,9 @@ mod tests {
1444514473 }
1444614474
1444714475 #[test]
14448- fn file_provider_missing_child_mount_after_recovery_is_recoverable () {
14476+ fn file_provider_missing_child_mount_while_preparing_is_recoverable () {
1444914477 assert!(super::recoverable_macos_file_provider_activation_error(
14450- "macOS File Provider recovery did not produce a healthy mount root `/Users/codeflash/Library/CloudStorage/Locality/linear`: Could not inspect macOS File Provider mount root `/Users/codeflash/Library/CloudStorage/Locality/linear`: Error: Error Domain=NSPOSIXErrorDomain Code=2 \"Couldn't find a file for /Users/codeflash/Library/CloudStorage/Locality/linear\" UserInfo={NSDescription=Couldn't find a file for /Users/codeflash/Library/CloudStorage/Locality/linear}"
14478+ "macOS File Provider did not finish preparing mount root `/Users/codeflash/Library/CloudStorage/Locality/linear`: Could not inspect macOS File Provider mount root `/Users/codeflash/Library/CloudStorage/Locality/linear`: Error: Error Domain=NSPOSIXErrorDomain Code=2 \"Couldn't find a file for /Users/codeflash/Library/CloudStorage/Locality/linear\" UserInfo={NSDescription=Couldn't find a file for /Users/codeflash/Library/CloudStorage/Locality/linear}"
1445114479 ));
1445214480 }
1445314481
@@ -14970,7 +14998,27 @@ mod tests {
1497014998 }
1497114999
1497215000 #[test]
14973- fn virtual_projection_refresh_signals_macos_file_provider_visible_containers() {
15001+ fn macos_file_provider_refresh_is_scoped_to_the_new_mount() {
15002+ let mount = MountConfig::new(
15003+ MountId::new("google-calendar-main"),
15004+ "google-calendar",
15005+ "/tmp/Locality/google-calendar-main",
15006+ )
15007+ .projection(ProjectionMode::MacosFileProvider);
15008+
15009+ assert_eq!(
15010+ virtual_projection_mount_activation_refresh_actions(&mount),
15011+ vec![
15012+ VirtualProjectionRefreshAction::Signal("root".to_string()),
15013+ VirtualProjectionRefreshAction::ReimportThenSignal(
15014+ "mount:google-calendar-main".to_string(),
15015+ ),
15016+ ]
15017+ );
15018+ }
15019+
15020+ #[test]
15021+ fn macos_file_provider_visible_refresh_never_reimports_the_shared_root() {
1497415022 let mount = MountConfig::new(
1497515023 MountId::new("notion-main"),
1497615024 "notion",
@@ -14979,15 +15027,28 @@ mod tests {
1497915027 .projection(ProjectionMode::MacosFileProvider);
1498015028
1498115029 assert_eq!(
14982- virtual_projection_refresh_signal_identifiers (&mount),
15030+ virtual_projection_refresh_actions (&mount),
1498315031 vec![
14984- "root".to_string(),
14985- "mount:notion-main".to_string(),
14986- "working-set".to_string(),
15032+ VirtualProjectionRefreshAction::Signal( "root".to_string() ),
15033+ VirtualProjectionRefreshAction::ReimportThenSignal( "mount:notion-main".to_string(), ),
15034+ VirtualProjectionRefreshAction::Signal( "working-set".to_string() ),
1498715035 ]
1498815036 );
1498915037 }
1499015038
15039+ #[cfg(target_os = "macos")]
15040+ #[test]
15041+ fn macos_file_provider_mount_preparation_uses_non_trivial_waits() {
15042+ assert_eq!(
15043+ super::MACOS_FILE_PROVIDER_MOUNT_ROOT_APPEAR_TIMEOUT,
15044+ Duration::from_secs(30)
15045+ );
15046+ assert_eq!(
15047+ super::MACOS_FILE_PROVIDER_MOUNT_ROOT_RECOVERY_TIMEOUT,
15048+ Duration::from_secs(30)
15049+ );
15050+ }
15051+
1499115052 #[test]
1499215053 fn virtual_projection_prefetch_container_identifiers_use_mount_point_root() {
1499315054 let mount = MountConfig::new(
0 commit comments