@@ -3899,11 +3899,44 @@ fn load_desktop_snapshot() -> Result<DesktopSnapshot, String> {
38993899}
39003900
39013901fn load_desktop_snapshot_at_state_root(state_root: &Path) -> Result<DesktopSnapshot, String> {
3902+ ensure_dev_sidecars_match_desktop_before_state_open(state_root)?;
39023903 let store =
39033904 SqliteStateStore::open(state_root.to_path_buf()).map_err(|error| error.to_string())?;
39043905 load_desktop_snapshot_from_store(&store, &state_root)
39053906}
39063907
3908+ fn ensure_dev_sidecars_match_desktop_before_state_open(state_root: &Path) -> Result<(), String> {
3909+ #[cfg(debug_assertions)]
3910+ {
3911+ if !state_root.join("state.sqlite3").exists() {
3912+ return Ok(());
3913+ }
3914+ let desktop_build_id = current_desktop_build_id();
3915+ let daemon_build_id = current_daemon_build_id();
3916+ if let Some(message) = dev_sidecar_build_skew_message(&desktop_build_id, &daemon_build_id) {
3917+ desktop_log("warn", "daemon.dev_sidecar_build_skew", message.clone());
3918+ return Err(message);
3919+ }
3920+ }
3921+ let _ = state_root;
3922+ Ok(())
3923+ }
3924+
3925+ fn dev_sidecar_build_skew_message(desktop_build_id: &str, daemon_build_id: &str) -> Option<String> {
3926+ if desktop_build_id.is_empty()
3927+ || daemon_build_id.is_empty()
3928+ || desktop_build_id == "unknown"
3929+ || daemon_build_id == "unknown"
3930+ || desktop_build_id == daemon_build_id
3931+ {
3932+ return None;
3933+ }
3934+
3935+ Some(format!(
3936+ "Locality debug sidecars are stale: desktop build {desktop_build_id} but bundled localityd build {daemon_build_id}. Run `make prepare-desktop-dev-sidecars` and relaunch Locality; do not delete ~/.loc/state.sqlite3."
3937+ ))
3938+ }
3939+
39073940fn load_desktop_snapshot_for_surface() -> Result<DesktopSnapshot, String> {
39083941 let mut cache = DESKTOP_SNAPSHOT_CACHE
39093942 .get_or_init(|| Mutex::new(DesktopSnapshotCache::default()))
@@ -11868,6 +11901,29 @@ mod tests {
1186811901 );
1186911902 }
1187011903
11904+ #[test]
11905+ fn dev_sidecar_build_skew_message_reports_known_mismatch_only() {
11906+ let message = super::dev_sidecar_build_skew_message("desktop-build", "daemon-build")
11907+ .expect("known mismatch should report");
11908+
11909+ assert!(message.contains("desktop build desktop-build"));
11910+ assert!(message.contains("bundled localityd build daemon-build"));
11911+ assert!(message.contains("make prepare-desktop-dev-sidecars"));
11912+ assert!(message.contains("do not delete ~/.loc/state.sqlite3"));
11913+ assert_eq!(
11914+ super::dev_sidecar_build_skew_message("same-build", "same-build"),
11915+ None
11916+ );
11917+ assert_eq!(
11918+ super::dev_sidecar_build_skew_message("unknown", "daemon-build"),
11919+ None
11920+ );
11921+ assert_eq!(
11922+ super::dev_sidecar_build_skew_message("desktop-build", "unknown"),
11923+ None
11924+ );
11925+ }
11926+
1187111927 #[test]
1187211928 fn daemon_process_started_before_bundled_binary_compares_modified_times() {
1187311929 let pid_modified = UNIX_EPOCH + Duration::from_secs(10);
0 commit comments